Search code examples
pythonclasstkinter

Python tkinter cloase first window while opening second window


I'm trying to close the first window as the second window opens. Both windows close, or the first window closes and the second window never opens.

This question has a similar problem but was solved by addressing the imported libraries: Tkinter is opening a second windows when the first one is closing

Here's my code, which was taken from here https://www.pythontutorial.net/tkinter/tkinter-toplevel/

import tkinter as tk
from tkinter import ttk


class Window(tk.Toplevel):
    def __init__(self, parent):
        super().__init__(parent)

        self.geometry('300x100')
        self.title('Toplevel Window')

        ttk.Button(self,
                text='Close',
                command=self.destroy).pack(expand=True)


class App(tk.Tk):
    def __init__(self):
        super().__init__()

        self.geometry('300x200')
        self.title('Main Window')

        # place a button on the root window
        ttk.Button(self,
                text='Open a window',
                command=self.open_window).pack(expand=True)

    def open_window(self):
        window = Window(self)
        window.grab_set()
        self.destroy()


if __name__ == "__main__":
    app = App()
    app.mainloop()

I added in self.destroy() in the function def open_window(self). But it doesn't close the window created by this class: class App(tk.Tk).


Solution

  • You would need, under your design, to begin the mainloop of the second window somehow. Running into this problem before, what I found most helpful was to first start both windows and then withdraw them when they were not needed before and after they were shown and to run the .update() method on both windows to keep them running simultaneously. Don't be dismayed, withdrawn windows does not mean a minimized windows and they are not easily accessible by the end user, but they are easily accessible to you with the .deiconify method. As for destroying windows, I have found that in tkinter in python, that this was very, very finicky.

    So, in your case:

    import tkinter as tk
    from tkinter import ttk
    
    
    class Window(tk.Toplevel):
        def __init__(self, parent):
            super().__init__(parent)
            self.parent = parent
    
            self.geometry('300x100')
            self.title('Toplevel Window')
            self.withdraw()
    
            ttk.Button(self,
                    text='Close',
                    command=self.parent.destroy).pack(expand=True)
    
    
    class App(tk.Tk):
        def __init__(self):
            super().__init__()
    
            self.geometry('300x200')
            self.title('Main Window')
    
        def bar(self, window_unwithdraw_fn):
            # place a button on the root window
            ttk.Button(self,
                    text='Open a window',
                    command=window_unwithdraw_fn).pack(expand=True)
    
    
    
    if __name__ == "__main__":
        app = App()
        window = Window(app)
    
        def foobar():
            window.deiconify()
            window.grab_set()
            app.withdraw()
        
        app.bar(foobar)
    
        while True:
            app.update()
            window.update()
    

    Note a couple of things:

    • Once all windows are destroyed, the tcl interpreter does not quit and so therefore neither does the python interpreter. You could use the .quit() method, but that's up to what you want
    • This is a quick sketch up to show how the underlying logic would work, it's up to you to make it fit your project structure, so you could conceivably move a couple things in or out of the __init__() methods, or make new classes, etc.
    • This works and if you wanted to mess with the window that's focused, I would recommend checking out win32gui or your corresponding OS's GUI library
    • I have not fully fleshed out this little code snippet so while it runs according to how you described in your post, be careful with the self.parent.destroy implementation in Window as it may have some unexpected consequences such as Widgets not behaving properly
    • As described above this does not destroy your App or Window only withdraw them until the final close button is clicked
    • Finally, I was unsure whether the first window should open again after the second window's close button is clicked, but if it should it should be simple to instead of destroy the parent in Window, to deiconify it and withdraw the current Window, self, in some new foo() function