销毁TtkBootstrap登录窗口时出现bgerror错误及程序冻结求助
解决tkinter/ttkbootstrap登录窗口销毁后主窗口报错冻结问题
我开发了一款简单的Python应用,流程为:先打开名为root的登录窗口,用户验证凭据通过后,关闭登录窗口并打开名为MainWindow的主窗口。但执行该操作时,程序总会出现以下错误并冻结:
bgerror failed to handle background error. Original error: can't invoke "event" command: application has been destroyed Error in bgerror: can't invoke "tk" command: application has been destroyed
原相关代码
import tkinter as tk from tkinter import filedialog import ttkbootstrap as tb from ttkbootstrap.dialogs import Messagebox # login check def login_check(username, password): if username == "a" and password == "p": return True else: return False # login function def on_login_click(): username = ent_codice_utente.get() password = ent_password.get() if login_check(username, password): root.destroy() MainWindow() else: err_credential = Messagebox.show_error('Invalid credential', parent=root) def MainWindow(): MainWindow = tb.Window(title='GESCON', themename='superhero') MainWindow.geometry('1366x768') MainWindow.columnconfigure(0, weight=1) MainWindow.rowconfigure(0, weight=1) MainWindow.rowconfigure(1, weight=4) frm_filtri = tb.LabelFrame(MainWindow, text="Filter", borderwidth=2) frm_filtri.columnconfigure(0, weight=10) frm_filtri.columnconfigure(1, weight=10) frm_filtri.columnconfigure(2, weight=10) frm_filtri.rowconfigure(0, weight=10) frm_filtri.rowconfigure(1, weight=10) frm_filtri.rowconfigure(2, weight=10) frm_griglia = tb.LabelFrame(MainWindow, text='Result', borderwidth=2) frm_filtri.grid(column=0, row=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=5, pady=5) frm_griglia.grid(column=0, row=1, sticky=(tk.W, tk.E, tk.N, tk.S), padx=5, pady=5) btn_elabora = tb.Button(frm_filtri, text='Search') btn_elabora.grid(column=3, row=2, padx=50, pady=5) menubar = tb.Menu(MainWindow) MainWindow.config(menu=menubar) menu_file = tb.Menu(menubar, tearoff=0) menu_help = tb.Menu(menubar, tearoff=0) menubar.add_cascade(label='File', menu=menu_file) menubar.add_cascade(label='Help', menu=menu_help) menu_anagrafiche = tb.Menu(menu_file, tearoff=0) menu_file.add_cascade(label='Anagrafiche', menu=menu_anagrafiche) menu_file.add_separator() menu_file.add_command(label='Import file') menu_file.add_separator() menu_file.add_command(label='Exit') menu_help.add_command(label='Info') #Login window root = tb.Window(title='Login', themename='superhero') root.geometry('500x300') lbl_codice_utente = tb.Label(root, text="User:") ent_codice_utente = tb.Entry(root) lbl_password = tb.Label(root, text="Password:") ent_password = tb.Entry(root, show="*") btn_login = tb.Button(root, text="Login", bootstyle='success, outline', command=on_login_click) btn_exit = tb.Button(root, text="Exit", bootstyle='danger, outline', command = lambda:root.destroy()) lbl_codice_utente.grid(padx=10, pady=10, column=0, row=0, sticky='e') ent_codice_utente.grid(pady=5, column=1, row=0, sticky='w') lbl_password.grid(padx=10, pady=10, column=0, row=1, sticky='e') ent_password.grid(pady=5, column=1, row=1, sticky='w') btn_login.grid(pady=20, column=0, row=3) btn_exit.grid(pady=20, column=1, row=3) root.mainloop()
问题原因
销毁root窗口时,ttkbootstrap底层关联的tk主对象被销毁,但原mainloop仍在运行,后续创建新的tb.Window会导致事件处理逻辑混乱,触发报错。
解决方案
方案1:隐藏登录窗口而非直接销毁
修改登录成功后的逻辑,先隐藏登录窗口,启动主窗口循环,待主窗口关闭后再销毁登录窗口:
def on_login_click(): username = ent_codice_utente.get() password = ent_password.get() if login_check(username, password): root.withdraw() # 隐藏登录窗口 main_win = MainWindow() main_win.mainloop() root.destroy() # 主窗口关闭后销毁登录窗口 else: err_credential = Messagebox.show_error('Invalid credential', parent=root)
同时修改MainWindow函数,避免变量名覆盖函数本身:
def MainWindow(): main_win = tb.Window(title='GESCON', themename='superhero') main_win.geometry('1366x768') # 其余代码保持不变,将所有MainWindow替换为main_win ...
方案2:使用单主窗口切换框架(更推荐)
不销毁主窗口,将登录界面和主界面封装为不同的Frame,登录成功后切换显示框架,这是tkinter界面切换的标准稳定做法:
import tkinter as tk from tkinter import filedialog import ttkbootstrap as tb from ttkbootstrap.dialogs import Messagebox # login check def login_check(username, password): return username == "a" and password == "p" def show_main_frame(): login_frame.pack_forget() main_frame.pack(fill=tk.BOTH, expand=True) def on_login_click(): username = ent_codice_utente.get() password = ent_password.get() if login_check(username, password): show_main_frame() else: Messagebox.show_error('Invalid credential', parent=root) # 主窗口初始化 root = tb.Window(title='GESCON', themename='superhero') root.geometry('1366x768') # 登录框架 login_frame = tb.Frame(root) login_frame.pack(expand=True) lbl_codice_utente = tb.Label(login_frame, text="User:") ent_codice_utente = tb.Entry(login_frame) lbl_password = tb.Label(login_frame, text="Password:") ent_password = tb.Entry(login_frame, show="*") btn_login = tb.Button(login_frame, text="Login", bootstyle='success, outline', command=on_login_click) btn_exit = tb.Button(login_frame, text="Exit", bootstyle='danger, outline', command=root.destroy) # 登录界面布局 lbl_codice_utente.grid(padx=10, pady=10, column=0, row=0, sticky='e') ent_codice_utente.grid(pady=5, column=1, row=0, sticky='w') lbl_password.grid(padx=10, pady=10, column=0, row=1, sticky='e') ent_password.grid(pady=5, column=1, row=1, sticky='w') btn_login.grid(pady=20, column=0, row=3) btn_exit.grid(pady=20, column=1, row=3) # 主界面框架 main_frame = tb.Frame(root) frm_filtri = tb.LabelFrame(main_frame, text="Filter", borderwidth=2) frm_filtri.columnconfigure(0, weight=10) frm_filtri.columnconfigure(1, weight=10) frm_filtri.columnconfigure(2, weight=10) frm_filtri.rowconfigure(0, weight=10) frm_filtri.rowconfigure(1, weight=10) frm_filtri.rowconfigure(2, weight=10) frm_griglia = tb.LabelFrame(main_frame, text='Result', borderwidth=2) frm_filtri.grid(column=0, row=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=5, pady=5) frm_griglia.grid(column=0, row=1, sticky=(tk.W, tk.E, tk.N, tk.S), padx=5, pady=5) btn_elabora = tb.Button(frm_filtri, text='Search') btn_elabora.grid(column=3, row=2, padx=50, pady=5) menubar = tb.Menu(root) root.config(menu=menubar) menu_file = tb.Menu(menubar, tearoff=0) menu_help = tb.Menu(menubar, tearoff=0) menubar.add_cascade(label='File', menu=menu_file) menubar.add_cascade(label='Help', menu=menu_help) menu_anagrafiche = tb.Menu(menu_file, tearoff=0) menu_file.add_cascade(label='Anagrafiche', menu=menu_anagrafiche) menu_file.add_separator() menu_file.add_command(label='Import file') menu_file.add_separator() menu_file.add_command(label='Exit', command=root.destroy) menu_help.add_command(label='Info') root.mainloop()
内容的提问来源于stack exchange,提问作者Korra DH
相关产品推荐
相关产品推荐

