Tkinter - Withdrawing previous window as opposed to destroying it causing issues in current window

Viewed 73

I have a application which I initialize with a login window - once the user and password are verified, it opens the main menu window (Code for that window is below). Previously if the user had no option to logout from the main menu to go back to the login in. But now I have inserted a logout button which SHOULD kill the current window open the former login window.

Problem is that previously I didn't have to retain the state of the login window as I never planned to go back there, but now I do want the option to go back there - so I need to retain the state - therefore I need to uses root.withdraw() as opposed to root.destroy() which seems to the root of my problems (no pun intended)

If I execute the main_menu_window() function directly on my IDE - it works 100%, but if I were to call it from the LoginWindow (the "parent") in my below code I get the error as detailed below:

def main_menu_window(_parent, _user):

    def open_user_man(event=None):
        user_man_window(main_win)
        main_menu_window(_user)

    def open_prof_man(event=None):
        prof_man_window(main_win, _user)
        main_menu_window(_user)

    def open_mon_leagues(event=None):
        mon_leagues_window(main_win)
        main_menu_window(_user)

    def back_to_login():
        main_win.destroy()
        # if _parent is not None:
        #     _parent.deiconify()

    # Hide Login Window
    if _parent is not None:
        _parent.withdraw()

    # Window Setup
    main_win = tk.Tk()
    main_win.title("11Sixteen Database Management Controller - Main menu")
    main_win.geometry("%dx%d+0+0" % (main_win.winfo_screenwidth(), main_win.winfo_screenheight()))

    # Object setup
    user_man = ImagedButtonWithText(main_win,
                                    'C:\\Users\\rferreira\\GitHub\\11Sixteen\\DatabaseManagentController\\GlobalResources\\Images_Icons\\user_man_icon.png',
                                    "LargeGroove", "User Management")
    prof_man = ImagedButtonWithText(main_win,
                                    'C:\\Users\\rferreira\\GitHub\\11Sixteen\\DatabaseManagentController\\GlobalResources\\Images_Icons\\profile_man_icon.png',
                                    "LargeGroove", "Profile Management")
    mon_leas = ImagedButtonWithText(main_win,
                                    'C:\\Users\\rferreira\\GitHub\\11Sixteen\\DatabaseManagentController\\GlobalResources\\Images_Icons\\monitor_leagues_icon.png',
                                    "LargeGroove", "Monitored Leagues")
    back_to_mm_btn = ColourSchemedButton(main_win, "PaleGreen", "Logout", width=18)

    # Object binding
    user_man.btn.config(command=open_user_man)
    prof_man.btn.config(command=open_prof_man)
    mon_leas.btn.config(command=open_mon_leagues)
    back_to_mm_btn.config(command=back_to_login)

    # Object placement
    back_to_mm_btn.grid(row=0, column=0, columnspan=2, padx=30, pady=20, sticky="nw")
    user_man.frame.grid(row=1, column=0, padx=30, pady=20)
    prof_man.frame.grid(row=1, column=1, padx=30, pady=20)
    mon_leas.frame.grid(row=1, column=2, padx=30, pady=20)

    main_win.mainloop()


if __name__ == "__main__":
    user = User()
    main_menu_window(None, user)

The error I am getting is as follows:

C:\Users\rferreira\AppData\Local\Programs\Python\Python39\python.exe C:/Users/rferreira/GitHub/11Sixteen/DatabaseManagentController/Login/LoginWindow.py
Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Users\rferreira\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1892, in __call__
    return self.func(*args)
  File "C:\Users\rferreira\GitHub\11Sixteen\DatabaseManagentController\Login\LoginWindow.py", line 19, in login_process
    validate_login(login_win, email_entry, password_entry, message_label)
  File "C:\Users\rferreira\GitHub\11Sixteen\DatabaseManagentController\Login\LoginValidation.py", line 54, in validate_login
    login_attempt()
  File "C:\Users\rferreira\GitHub\11Sixteen\DatabaseManagentController\Login\LoginValidation.py", line 42, in login_attempt
    MwW.main_menu_window(parent, user)
  File "C:\Users\rferreira\GitHub\11Sixteen\DatabaseManagentController\MainMenu\MainMenuWindow.py", line 41, in main_menu_window
    user_man = ImagedButtonWithText(main_win,
  File "C:\Users\rferreira\GitHub\11Sixteen\DatabaseManagentController\GlobalResources\GuiObjectsFactories.py", line 45, in __init__
    self.btn = LargeGroove(self.frame, image=image)
  File "C:\Users\rferreira\GitHub\11Sixteen\DatabaseManagentController\GlobalResources\GUIObjectsComponents.py", line 20, in __init__
    super().__init__(master=parent, width=130, height=130, relief="groove", **kw)
  File "C:\Users\rferreira\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 2650, in __init__
    Widget.__init__(self, master, 'button', cnf, kw)
  File "C:\Users\rferreira\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 2572, in __init__
    self.tk.call(
_tkinter.TclError: image "pyimage4" doesn't exist

I would love any help or ideas on how to kill this bug. Please let me know if you need more details or further code extracts.

For better context below are further code extracts for the objects called where the errors are triggered

class ImagedButtonWithText(tk.Frame):
    def __init__(self, parent, image_path, btn_type, text, **kw):
        self.frame = tk.Frame(parent)

        # Set up button
        image = PhotoImageComp(image_path)
        if btn_type == "MicroGroove":
            self.btn = MicroGroove(self.frame, image=image)
        if btn_type == "LargeGroove":
            self.btn = LargeGroove(self.frame, image=image)
        self.btn.image = image
        self.btn.grid(row=0, column=0)

        # Set up text
        self.label = tk.Label(self.frame, text=text)
        self.label.grid(row=1, column=0)

# --------------------- Buttons ---------------------------
class MicroGroove(tk.Button):
    def __init__(self, parent, **kw):
        super().__init__(master=parent, width=30, height=20, relief="groove", **kw)

class LargeGroove(tk.Button):
    def __init__(self, parent, **kw):
        super().__init__(master=parent, width=130, height=130, relief="groove", **kw)

1 Answers

Tkinter never forces you to pass in the master argument. You usually do it like in this:

self.label = tk.Label(self.frame, text=text)

self.frame is the master for the label. Similarly you should pass in the master parameter when creating PhotoImages like this:

image = PhotoImage(image_path, master=parent)

This is only necessary only if you have more than 1 Tk() instance, but I would advise always passing it.

If you don't pass it in, tkinter assumes that the master is the first instance of Tk() that you created. It is a problem because the different instances of Tk() can't talk to each other (most of the time) so the PhotoImage doesn't actually exist according to the second instance of Tk().

Related