Maxxx
Maxxx

Reputation: 107

Why isn't root.quit() or root.destroy() working but only the combination of both?

In my program I implemented a Popup with some entries for user input. That Popup has an 'Enter' button to confirm and collect the input and then close the Popup automatically. For closing the Popup I found the root.destroy() and root.quit() methods (in my code self.master instead of root). I thought I kind of understand how to use them but apparently I didn't. :/


import tkinter as tk


root = tk.Tk()
window = MainWindow(root)
root.mainloop()


class MainWindow(tk.Frame):

    def __init__(self, master):

        tk.Frame.__init__(self, master)
        self.pack()

        #some irrelevant widgets

        btn_new_element = tk.Button(self, text='new', command=self.new_element)
        btn_new_element.pack()


    def new_element(self):

        root = tk.Toplevel()
        popup = Pupup(root, someData, otherData)
        root.mainloop()

        #do some other stuff



class Popup(tk.Frame):

    def __init__(self, master, var1, var2):

        tk.Frame.__init__(self, master)
        self.pack()

        #some widgets

        btn = tk.Button(self, text='Enter', command=lambda a=var1, b=var2: self.foo(a, b))
        btn.pack()


    def foo(self, a, b):

        #do something with widget data

        self.master.quit()
        self.master.destroy()

In foo() first I only used destroy(). It closes the Popup but the program didnt continue with the code in new_element(). (It seemed as if the mainloop() was still looping.) Using quit() produced the opposite result: the rest of the code in new_element() was executed but the Popup was still there. Only using quit() and destroy() in that particular order gave me the result I wanted.

According to thousands of examples on the internet it should be possible using only one of the two methods. Why isn't it working in my code?

EDIT: deleted var1 and var2 from the MainWindow constructor (copy and paste mistake during creation of this question.

EDIT: Also I get the feeling the the problem might be the scope of the root varibale. In new_element() do I create a local variable called root or do I overwrite the global root variable?

Upvotes: 2

Views: 3976

Answers (3)

MatthewG
MatthewG

Reputation: 815

I believe the problem is you have created a second loop, to fix this you can just remove the second loop, and then the .destroy() function should work just fine.

Like this

class MainWindow(tk.Frame):
    def __init__():
        # your init code

    def new_element(self):
        root = tk.Toplevel()
        popup = Popup(root, 1, 0)

class Popup(tk.Frame):

    def __init__(self, master, var1, var2):
        # your init code

    def foo(self, a, b):
        self.master.destroy()

Another comment I will make is that I believe the way you have structured this windows is misleading, and could cause confusion (keep in mind this is just preference and is no longer about "real" problems).

I believe instead of having a Popup(Frame) class, you should create a Popup(Toplevel) class.

Like this

import tkinter as tk

class MainWindow(tk.Frame):

    def __init__(self, master):

        tk.Frame.__init__(self, master)
        self.pack()
        btn_new_element = tk.Button(self, text='new', command=self.new_element)
        btn_new_element.pack()
        self.master = master
        self.pack()

    def new_element(self):
        popup = Popup(self.master, 1, 0)


class Popup(tk.Toplevel):
    def __init__(self, master, var1, var2):
        super().__init__(master)
        #some widgets
        btn = tk.Button(self, text='Enter', command=lambda a=var1, b=var2: self.foo(a,b))
        btn.pack()

    def foo(self, a, b):
        self.destroy()

if __name__ == '__main__':
    root = tk.Tk()
    window = MainWindow(root)
    root.mainloop()

When you create a Toplevel element it needs a master, the master will be the main window in this instance. When you use self.destroy within the Toplevel class, you are not destroying the Toplevels master, but instead you are destroying the Toplevel itself.

Upvotes: 0

Bryan Oakley
Bryan Oakley

Reputation: 386010

The root of the problem is that you're calling mainloop more than once. You should not do that. It causes problems very much like the one that you're having.

If your goal is to create a popup window, and then wait for the user to dismiss the popup window, tkinter has a method specifically for that. It's called wait_window, and it won't return until the window it is waiting on has been destroyed.

top = tk.Toplevel()
popup = Popup(top, "some data", "other data")
top.wait_window(top)        

You can put additional code after top.wait_window(top), and it won't execute until the popup has been destroyed.

Within your popup code, your button simply needs to call destroy on the popup window. You shouldn't call quit.

def foo(self, a, b):

    #do something with widget data

    self.master.destroy()

Upvotes: 3

mr_mo
mr_mo

Reputation: 1528

There are many things wrong with your example (copy paste not working), but after arranging things to work (giving values and stuff), here is your problem:

    def new_element(self):
        root = tk.Toplevel()
        popup = Pupup(root, someData, otherData)
        # root.mainloop() # < ------ This generates a new main loop, with is destroyed.

here is a complete code that works:

import tkinter as tk


class Popup(tk.Frame):

    def __init__(self, master, var1, var2):

        tk.Frame.__init__(self, master)
        self.pack()

        #some widgets

        btn = tk.Button(self, text='Enter', command=lambda a=var1, b=var2: self.foo(a, b))
        btn.pack()


    def foo(self, a, b):

        #do something with widget data

        self.master.quit()
        # self.master.destroy()


class MainWindow(tk.Frame):

    def __init__(self, master, var1, var2):

        tk.Frame.__init__(self, master)
        self.pack()

        #some irrelevant widgets

        btn_new_element = tk.Button(self, text='new', command=self.new_element)
        btn_new_element.pack()


    def new_element(self):

        root = tk.Toplevel()
        popup = Popup(root, 'a', 'b')
        # root.mainloop()

        #do some other stuff

root = tk.Tk()
window = MainWindow(root,1,2)
root.mainloop()

Upvotes: 0

Related Questions