Python Tkinter GUI反复点击返回按钮后自动关闭问题排查
Tkinter GUI返回按钮二次点击导致程序无提示关闭问题
我的Python Tkinter GUI存在如下问题:点击左上角返回按钮可回到主界面,点击注册按钮进入注册界面操作正常,但再次点击返回按钮时,GUI会无提示自动关闭,终端未输出任何报错信息。
注册页面代码(含返回按钮)
import sqlite3 from tkinter import * from tkinter import messagebox # Import messagebox for error messages import re # Import re module for regular expressions import customtkinter from PIL import ImageTk, Image # Destroy this window and open the login screen def login(): register_screen.destroy() import login # Assuming there is a login.py file to open the login screen def main(): register_screen.destroy() import main def register_user(): # Get username and password username_value = username.get() password_value = password.get() # Check password requirements # Check if there is at least one capital letter in the password section if not re.search(r"[A-Z]", password_value): error_label.config(text="Password must contain at least one capital letter", fg="red") return # Check if there is at least one number in the password section if not re.search(r"\d", password_value): error_label.config(text="Password must contain at least one number", fg="red") return # Check if there is at least one symbol in the password section if not re.search(r"\W", password_value): error_label.config(text="Password must contain at least one symbol", fg="red") return # Insert the user into the database cursor.execute("INSERT INTO users (username, password) VALUES (?, ?)", (username_value, password_value)) connection.commit() login() # Designing for registration register_screen = tk.Tk() register_screen.title("Register") register_screen.geometry("500x800") register_screen.resizable(width=False, height=False) # Create a database connection connection = sqlite3.connect("db/registration.db") cursor = connection.cursor() # Create a table for registration cursor.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT)") # Background image background_image_login = PhotoImage(file="assets/background2.png") background_label_login = Label(register_screen, image=background_image_login) background_label_login.place(x=0, y=0, relwidth=1, relheight=1) # Creating an empty tuple for username and password username = StringVar() password = StringVar() # Create an empty label for the image logo = ImageTk.PhotoImage(Image.open("assets/Logo.png").resize((180, 130), Image.ANTIALIAS)) logo1 = customtkinter.CTkButton(master=register_screen, image=logo, text="", compound="left", hover=False, bg_color="#020202") logo1.place(relx=0.48, rely=0.2, anchor=CENTER) # Create an empty label for the text username_label = Label(register_screen, text="Username") username_label.place(relx=0.5, rely=0.3, anchor=CENTER) # Create a customentry widget for username username_entry = customtkinter.CTkEntry(master=register_screen, textvariable=username, placeholder_text="Username", height=35, width=250) username_entry.place(relx=0.5, rely=0.35, anchor=CENTER) # Create an empty label for the text password_label = Label(register_screen, text="Password") password_label.place(relx=0.5, rely=0.4, anchor=CENTER) # Create a customentry widget for password password_entry = customtkinter.CTkEntry(master=register_screen, textvariable=password, placeholder_text="Password", height=35, width=250, show="*") password_entry.place(relx=0.5, rely=0.45, anchor=CENTER) # Create a button register_button = customtkinter.CTkButton(master=register_screen, text="Register", command=register_user, height=35, width=250) register_button.place(relx=0.5, rely=0.55, anchor=CENTER) # Create an empty label for the error message error_label = Label(register_screen, text="", fg="red") error_label.place(relx=0.5, rely=0.62, anchor=CENTER) arrow_image = ImageTk.PhotoImage(Image.open("assets/hi.png").resize((50, 50), Image.ANTIALIAS)) arrow_button = customtkinter.CTkButton(master= register_screen,image = arrow_image,text="", compound="left" ,fg_color='black', hover_color='black', command= main) arrow_button.place(relx=0.1 , rely = 0.1, anchor = CENTER) register_screen.mainloop() # Close the database connection connection.close()
主页面代码
import tkinter from tkinter import * import os import customtkinter from PIL import ImageTk, Image root = Tk() # Designing the main screen def main_screen(): root.geometry("500x800") main_frame = Frame(root,bg ='#020202') main_frame.pack(padx=20) main_frame.pack() background_image = PhotoImage(file="assets/Aim high.png") background_label = Label(root,image = background_image) background_label.place(x=0, y=0, relwidth = 1 , relheight = 1) root.title("Account Login") top_text=Label(root,text="Welcome to FitQuest",bg="#020202",font=("Calibri", 45)) top_text.pack(pady=0, anchor =tkinter.CENTER) def register(): root.destroy() import register def login(): root.destroy() import login #Login button Login_button = customtkinter.CTkButton(master=root, text="Login", command=login, height = 35, width = 250,bg_color="#020202") Login_button.place(relx=0.48, rely=0.3, anchor=tkinter.CENTER) #register button register_button = customtkinter.CTkButton(master=root, text="Register", command=register,height = 35, width = 250, bg_color="#020202" ) register_button.place(relx=0.48, rely=0.4, anchor=tkinter.CENTER) #Logo act as a button logo = ImageTk.PhotoImage(Image.open("assets/Logo.png"),Image.ANTIALIAS) button1 =customtkinter.CTkButton(master=root,image=logo,text="",compound="left" , hover=False, bg_color="#020202") button1.place(relx=0.48, rely=0.158, anchor=tkinter.CENTER) root.mainloop() main_screen()
问题原因与修复方案
核心问题
- 模块重复导入冲突:每次切换页面时直接
import目标模块,会重复执行模块内的全局代码。主页面中root = Tk()是全局变量,第二次导入时会创建新的Tk实例,引发窗口逻辑混乱,导致程序退出。 - 窗口管理逻辑错误:直接销毁当前窗口后依赖模块导入自动创建新窗口,这种方式不可控,容易因模块重复加载产生异常。
修复步骤
1. 重构主页面代码(main.py)
import tkinter from tkinter import * import customtkinter from PIL import ImageTk, Image def main_screen(): # 每次调用创建新窗口,避免全局变量冲突 root = Tk() root.geometry("500x800") root.title("Account Login") main_frame = Frame(root, bg='#020202') main_frame.pack(padx=20) background_image = PhotoImage(file="assets/Aim high.png") background_label = Label(root, image=background_image) background_label.place(x=0, y=0, relwidth=1, relheight=1) top_text = Label(root, text="Welcome to FitQuest", bg="#020202", font=("Calibri", 45)) top_text.pack(pady=0, anchor=tkinter.CENTER) def register(): root.destroy() # 导入后调用注册页面入口函数 import register register.show_register_screen() def login(): root.destroy() import login login.show_login_screen() # 登录按钮 Login_button = customtkinter.CTkButton(master=root, text="Login", command=login, height=35, width=250, bg_color="#020202") Login_button.place(relx=0.48, rely=0.3, anchor=tkinter.CENTER) # 注册按钮 register_button = customtkinter.CTkButton(master=root, text="Register", command=register, height=35, width=250, bg_color="#020202") register_button.place(relx=0.48, rely=0.4, anchor=tkinter.CENTER) # Logo按钮 logo = ImageTk.PhotoImage(Image.open("assets/Logo.png"), Image.ANTIALIAS) button1 = customtkinter.CTkButton(master=root, image=logo, text="", compound="left", hover=False, bg_color="#020202") button1.place(relx=0.48, rely=0.158, anchor=tkinter.CENTER) # 绑定图片到窗口,防止被垃圾回收 root.background_image = background_image root.logo = logo root.mainloop() # 仅直接运行模块时启动主界面 if __name__ == "__main__": main_screen()
2. 修改注册页面代码(register.py)
import sqlite3 from tkinter import * from tkinter import messagebox import re import customtkinter from PIL import ImageTk, Image def show_register_screen(): # 封装注册窗口为函数 register_screen = Tk() register_screen.title("Register") register_screen.geometry("500x800") register_screen.resizable(width=False, height=False) # 数据库连接 connection = sqlite3.connect("db/registration.db") cursor = connection.cursor() cursor.execute("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT)") def login(): register_screen.destroy() import login login.show_login_screen() def back_to_main(): register_screen.destroy() import main main.main_screen() def register_user(): username_value = username.get() password_value = password.get() # 密码验证逻辑 if not re.search(r"[A-Z]", password_value): error_label.config(text="密码必须包含至少一个大写字母", fg="red") return if not re.search(r"\d", password_value): error_label.config(text="密码必须包含至少一个数字", fg="red") return if not re.search(r"\W", password_value): error_label.config(text="密码必须包含至少一个特殊符号", fg="red") return cursor.execute("INSERT INTO users (username, password) VALUES (?, ?)", (username_value, password_value)) connection.commit() connection.close() login() # 背景图片 background_image_login = PhotoImage(file="assets/background2.png") background_label_login = Label(register_screen, image=background_image_login) background_label_login.place(x=0, y=0, relwidth=1, relheight=1) username = StringVar() password = StringVar() # Logo按钮 logo = ImageTk.PhotoImage(Image.open("assets/Logo.png").resize((180, 130), Image.ANTIALIAS)) logo1 = customtkinter.CTkButton(master=register_screen, image=logo, text="", compound="left", hover=False, bg_color="#020202") logo1.place(relx=0.48, rely=0.2, anchor=CENTER) # 用户名输入 username_label = Label(register_screen, text="Username") username_label.place(relx=0.5, rely=0.3, anchor=CENTER) username_entry = customtkinter.CTkEntry(master=register_screen, textvariable=username, placeholder_text="Username", height=35, width=250) username_entry.place(relx=0.5, rely=0.35, anchor=CENTER) # 密码输入 password_label = Label(register_screen, text="Password") password_label.place(relx=0.5, rely=0.4, anchor=CENTER) password_entry = customtkinter.CTkEntry(master=register_screen, textvariable=password, placeholder_text="Password", height=35, width=250, show="*") password_entry.place(relx=0.5, rely=0.45, anchor=CENTER) # 注册按钮 register_button = customtkinter.CTkButton(master=register_screen, text="Register", command=register_user, height=35, width=250) register_button.place(relx=0.5, rely=0.55, anchor=CENTER) # 错误提示 error_label = Label(register_screen, text="", fg="red") error_label.place(relx=0.5, rely=0.62, anchor=CENTER) # 返回按钮 arrow_image = ImageTk.PhotoImage(Image.open("assets/hi.png").resize((50, 50), Image.ANTIALIAS)) arrow_button = customtkinter.CTkButton(master=register_screen, image=arrow_image, text="", compound="left", fg_color='black', hover_color='black', command=back_to_main) arrow_button.place(relx=0.1, rely=0.1, anchor=CENTER) # 绑定图片到窗口 register_screen.background_image_login = background_image_login register_screen.logo = logo register_screen.arrow_image = arrow_image register_screen.mainloop() connection.close() # 仅直接运行模块时启动注册界面 if __name__ == "__main__": show_register_screen()
关键改进点
- 窗口逻辑封装:每个页面的窗口创建都放在函数内,避免全局变量冲突,通过调用函数启动页面。
- 控制模块执行时机:用
if __name__ == "__main__"确保模块仅在直接运行时才启动窗口,导入时不会自动执行。 - 图片引用保留:将图片对象绑定到窗口实例,防止被垃圾回收导致界面显示异常。
- 明确切换逻辑:每个按钮命令都先销毁当前窗口,再调用目标页面的入口函数,流程清晰可控。
内容的提问来源于stack exchange,提问作者Joseph Wu
相关产品推荐
相关产品推荐

