Tkinter登录验证通过后切换窗口的正确实现方式及相关报错解决问询
Let's break down what's causing your error and how to fix it while keeping your desired structure.
Why the Error Happens
When you call menuPrincipal.destroy() on your root login window, it immediately removes the window from memory—but any pending after() callbacks (like periodic updates or timers) tied to that window will still try to run. Since the window no longer exists, Tkinter throws the "invalid command name" error you're seeing. Additionally, your open() function wasn't starting the mainloop for the new window, which would have caused it to close immediately otherwise.
Solution 1: Recommended Approach (Use Toplevel() for Secondary Windows)
This follows Tkinter best practices (avoiding multiple Tk() instances) while letting you keep most of your code structure:
Step 1: Update main.py Validation Logic
Instead of destroying the login window right away, hide it with withdraw() and pass it to your open() function:
for i in c: if user == i['USUARIO'] and contra == i['CONTRASEÑA']: messagebox.showinfo(title='Correcto', message='Usuario y Contraseña Correctos') # Hide login window instead of destroying it menuPrincipal.withdraw() # Pass the login window to the open function mainformuser.open(menuPrincipal) break else: messagebox.showerror(title=None, message='Usuario o Contraseña Incorrectos')
Step 2: Modify mainformuser.py to Use Toplevel()
Change your open() function to create a Toplevel window (attached to the existing root) instead of a new Tk() instance, and add cleanup logic:
from tkinter import * from time import * from PIL import ImageTk, Image import sqlite3 from tkinter import messagebox def open(parent): # Use Toplevel instead of Tk to attach to the login window's root top = Toplevel(parent) top.title('Menú Principal') top.configure(background='black') top.resizable(True, True) app_width = 1300 app_height = 777 screen_width = top.winfo_screenwidth() scree_height = top.winfo_screenheight() x = (screen_width/2) - (app_width/2) y = (scree_height/2.2) - (app_height/2) top.geometry(f'{app_width}x{app_height}+{int(x)}+{int(y)}') # Clean up both windows when the main menu is closed def on_menu_close(): top.destroy() parent.destroy() top.protocol("WM_DELETE_WINDOW", on_menu_close) # Add your additional functionality here
Solution 2: Keep Using Tk() for the New Window
If you must retain the Tk() instance in your open() function, adjust the flow to avoid immediate destruction of the login window:
Step 1: Update main.py
for i in c: if user == i['USUARIO'] and contra == i['CONTRASEÑA']: messagebox.showinfo(title='Correcto', message='Usuario y Contraseña Correctos') # Hide login window to prevent after callback errors menuPrincipal.withdraw() # Run the new window's mainloop mainformuser.open() # Destroy login window only after new window closes menuPrincipal.destroy() break else: messagebox.showerror(title=None, message='Usuario o Contraseña Incorrectos')
Step 2: Add mainloop() to mainformuser.py
Ensure your new window stays open by starting its mainloop:
def open(): top = Tk() top.title('Menú Principal') top.configure(background='black') top.resizable(True, True) app_width = 1300 app_height = 777 screen_width = top.winfo_screenwidth() scree_height = top.winfo_screenheight() x = (screen_width/2) - (app_width/2) y = (scree_height/2.2) - (app_height/2) top.geometry(f'{app_width}x{app_height}+{int(x)}+{int(y)}') # Start the mainloop for the new window top.mainloop()
Key Takeaways
- Avoid multiple
Tk()instances: UsingToplevel()is the standard way to create secondary windows in Tkinter, as it shares the existing event loop and avoids unexpected behavior. - Delay destroying the root window: Using
withdraw()instead ofdestroy()immediately prevents pendingafter()callbacks from failing, since the window still exists in memory until you explicitly destroy it later.
内容的提问来源于stack exchange,提问作者Paul

