Python Tkinter - closing a child window with an exit button

15,550

Solution 1

If you put the new_window into your App2 then you should be fine.

self.newWindow.destroy() 

Destroys the window. This is the right call. The window is closed then and all widgets in the window also get destroyed.

quit() will stop the mainloop() In this case the the program ends at the last line and also destroys everything.

You definitely want to use destroy.

class App2:

    newWindow = None

    def close_window(self):
        print('Close Child window')
        if self.newWindow:
            try: self.newWindow.destroy()   
            except (): pass # fill in the error here
            self.newWindow = None

   def new_window(self):
        print('New Window')
        self.close_window()
        self.newWindow = tk.Toplevel(self.master)
        self.app = App2(self.newWindow)
        self.newWindow.grab_set()

    @classmethod
    def start_app(cls):
        window = tk.Tk(self.master)
        app = App2(window)
        return app

You should not acces Tkinter from threads. Have a look at alternatives

I was confused by quit and destroy, too, when I was starting with Tkinter.

Solution 2

In Tk windows are destroyed using the destroy method. So if you have a dialog toplevel and you want to get rid of it you call its destroy() method. Or you can withdraw it in which case the object continues to exist but is no longer on-screen and to re-show it you deiconify() the toplevel frame. It's more common to destroy them though. Here is a simple example creating and destroying a child dialog:

import sys
from Tkinter import *

class App(Frame):
    def __init__(self, parent = None):
        Frame.__init__(self, parent)
        self.grid()

        self.button = Button(self, text = "Create Dialog", command=self.CreateDialog)
        self.button.grid()

    def CreateDialog(self):
        dialog = Toplevel(self)
        dialog.wm_title("Dialog window")
        dialog.wm_transient(self)
        dialog.wm_protocol("WM_DELETE_WINDOW", lambda: self.onDeleteChild(dialog))
        button = Button(dialog, text="Close", command=lambda: self.onDeleteChild(dialog))
        button.grid()

    def onDeleteChild(self, w):
        w.destroy()


def main():
    app = App()
    app.mainloop()

if __name__ == "__main__":
    sys.exit(main())

You should also consider looking at using a timer in the code to drive the LED loop rather than a while loop. Take a look at this answer using Tk's after function to run code after an interval. If you re-schedule another after call in the handler, then you can arrange a function to be run at regular intervals and avoid blocking the event handling without requiring additional threads.

Share:
15,550
blast_uk
Author by

blast_uk

I've been an engineer for many years now. Dealing with electrical, electronic, and mechanical projects. Its only over the past few years that I've been trying to get my head into programming.

Updated on June 05, 2022

Comments

  • blast_uk
    blast_uk almost 2 years

    I have a Raspberry Pi with the Piface adaptor board. I have made a GUI which controls the LED's on the Piface board. One button on the GUI opens a new window which, on the press of a button, starts and stops running a small piece of code to make the LED's run up and down continuously, like Knight Riders car, using a While loop in a thread. In this new window I have added a EXIT button. I want to add a piece of code that will close the new window when I click the EXIT button, and then return to the main window. I have looked up many examples but just can't quite see what I should put or where. I have tried the 'quit' but it closed the whole program. Having looked at many examples I maybe creating my new window in not quite the right way so feel free to tell me if there are better ways.

    So is there a better way of doing it? Any pointers would be appreciated.

    Thanks in advance.

    Heres a piece of the code....

       def new_window(self):
            print('New Window')
            self.newWindow = tk.Toplevel(self.master)
            self.app = App2(self.newWindow)
            self.newWindow.grab_set()   # I added this line to stop opening multiple new windows
    
    class App2:
    
    
        def __init__(self, master):
    
                frame = Frame(master)
                frame.pack()
                Label(frame, text='Turn LED ON').grid(row=0, column=0)
                Label(frame, text='Turn LED OFF').grid(row=0, column=1)
    
                self.button0 = Button(frame, text='Knight Rider OFF', command=self.convert0)
                self.button0.grid(row=2, column=0)
                self.LED0 = Label(frame, image=logo2)
                self.LED0.grid(row=2, column=1)
    
                self.button9 = Button(frame, text='Exit', command=self.close_window)
                self.button9.grid(row=3, column=0)
    
    
        def convert0(self, tog=[0]):
    
            tog[0] = not tog[0]
            if tog[0]:
                print('Knight Rider ON')
                self.button0.config(text='Knight Rider ON')
                t=threading.Thread(target=self.LED)
                t.start()
                self.signal = True    #added to stop thread
                self.LED0.config(image = logo)
            else:
                print('Knight Rider OFF')
                self.button0.config(text='Knight Rider OFF')
                self.signal = False   #added to stop thread
                self.LED0.config(image = logo2)
    
        def LED(self):
                while self.signal:   #added to stop thread
    
                    a=0
    
                    while self.signal:   #added to stop thread
                            pfio.digital_write(a,1) #turn on
                            sleep(0.05)
                            pfio.digital_write(a,0) #turn off
                            sleep(0.05)
                            a=a+1
    
                            if a==7:
                                    break
    
                    while self.signal:   #added to stop thread
    
                            pfio.digital_write(a,1) #turn on
                            sleep(0.05)
                            pfio.digital_write(a,0) #turn off
                            sleep(0.05)
                            a=a-1
    
                            if a==0:
                                    break
    
        def close_window(self):
            print('Close Child window')
            #self.newWindow.destroy()   Not sure what to put here?
    
  • blast_uk
    blast_uk about 10 years
    I'm using the new_window piece of code to start the App2. If i move it into App2 then App2 doesn't start. I feel that i maybe using the wrong way to start App2.
  • blast_uk
    blast_uk about 10 years
    I've seen many examples like this one but I can't see how to fit it in my code. I think i need to look at how I'm starting up the new window, the new window is started from a previous class, and the examples I've seen are all in one class.
  • User
    User about 10 years
    I added a new method for starting the app. App2.start_app() Interesting Idea to let the app start itself. I would not have thought of that.
  • blast_uk
    blast_uk about 10 years
    Sorry but i'm lost now, newbie syndrome. Unfortunately I've confused myself and I haven't explained it clearly. Thanks for the advice and the links to the thread alternatives.