解决CustomTkinter中destroy()引发的无效命令名及程序残留问题
问题解决:CustomTkinter登录窗口销毁后报错并后台运行
错误信息
invalid command name "2454343926912update" while executing "2454343926912update" ("after" script) invalid command name "2454379091328check_dpi_scaling" while executing "2454379091328check_dpi_scaling" ("after" script)
问题现象
登录窗口调用app.destroy()后,控制台抛出上述错误,程序仍在后台运行,尝试.withdraw()、.exit()等方法均无效。
登录窗口代码
# Importing required modules import tkinter import customtkinter from main_window import create_main_window import tkinter.messagebox as messagebox import json def create_login_window(): customtkinter.set_appearance_mode("dark") customtkinter.set_default_color_theme("dark-blue") app = customtkinter.CTk() app.geometry("800x600") app.title('Login') # Get screen dimensions screen_width = app.winfo_screenwidth() screen_height = app.winfo_screenheight() # Calculate window width and height window_width = int(screen_width * 0.8) window_height = int(screen_height * 0.8) # Calculate window position to center it on the screen x_position = (screen_width - window_width) // 2 y_position = (screen_height - window_height) // 2 # Set window size and position app.geometry(f"{window_width}x{window_height}+{x_position}+{y_position}") def login(event=None): _ = event # Use '_' to indicate that the 'event' parameter is not used explicitly entered_username = entry1.get() entered_password = entry2.get() # Admin login if entered_username == "ADMIN" and entered_password == "ADMIN": # Destroy current window and create the main window app.destroy() create_main_window() return else: # Load user data from JSON file try: with open("user_data.json", "r") as json_file: user_data = json.load(json_file) except FileNotFoundError: messagebox.showerror("Error", "User data file not found.") return # Check if entered username and password match any user in the JSON data for user in user_data: if user["username"] == entered_username and user["password"] == entered_password: # If match found, destroy current window and create the main window app.destroy() create_main_window() return # If no match found, show error message messagebox.showerror("Error", "Invalid username or password. Please try again.") frame = customtkinter.CTkFrame(master=app, width=500, height=400, corner_radius=30) frame.place(relx=0.5, rely=0.5, anchor=tkinter.CENTER) l1 = customtkinter.CTkLabel(master=frame, text="Log into your account", font=('Century Gothic', 40)) l1.place(x=45, y=45) entry1 = customtkinter.CTkEntry(master=frame, width=400, height=40, placeholder_text='Username') entry1.place(x=45, y=130) entry2 = customtkinter.CTkEntry(master=frame, width=400, height=40, placeholder_text='Password', show="*") entry2.place(x=45, y=215) button1 = customtkinter.CTkButton(master=frame, width=200, height=50, text="Login", command=login, corner_radius=6) button1.place(x=150, y=300) app.bind('<Return>', login) # Set focus to the username entry widget after a short delay app.after(100, entry1.focus) app.mainloop()
解决办法
方法1:销毁前取消所有after任务
CustomTkinter内部会通过after注册定时任务(如DPI缩放检查、组件更新),窗口销毁后这些任务仍会触发,导致找不到对应命令报错。在调用destroy()前取消所有未执行的after任务:
# 替换原app.destroy()代码 app.after_cancel("all") app.destroy() create_main_window()
方法2:用quit()退出主循环再销毁窗口
destroy()仅销毁窗口,但主事件循环可能未完全终止。先用quit()退出循环,再销毁窗口:
# 替换原app.destroy()代码 app.quit() app.destroy() create_main_window()
方法3:重构窗口切换逻辑(适合长期维护)
避免重复创建CTk实例和启动mainloop,将登录窗口作为主窗口的子组件,切换时仅隐藏/显示组件,而非销毁整个窗口。这种方式改动较大,但能从根源避免此类问题。
额外注意事项
- 确保
create_main_window()内部仅启动一次mainloop,避免多重循环导致程序残留 - 检查是否有未终止的线程或未关闭的文件句柄,这些也可能导致程序后台运行
内容的提问来源于stack exchange,提问作者Tino Radovanović
相关产品推荐
相关产品推荐

