Search code examples
python-3.xinheritancetkinterdestroy

Can't destroy inherited tkinter.Tk() window


I'm having a ton of trouble killing a tkinter window created using the below fashion. I'm getting the error shown below. I'm pretty new to Python, so any help would be greatly appreciated.

class InspectWindow(tk.Frame):
    def __init__(self, sender_email, recipient_email, email_body, 
                   master = None):
        super().__init__(master)
        self.create_widgets()

def create_widgets(self):
    self.yes = tk.Button(self)
    self.yes['text'] = 'send me!'
    self.yes['command'] = self.send_email()

def send_email(self):
    root.destroy()


root = tk.Tk()
popup = InspectWindow(sender_email, recipient_email, email_body,
                        master=root)
popup.mainloop()



Traceback (most recent call last):
  File "spam.py", line 108, in <module>
    master=root)
  File "spam.py", line 16, in __init__
    self.create_widgets()
  File "AutomateFellowshipEmails.py", line 23, in create_widgets
    self.yes['command'] = self.send_email()
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/tkinter/__init__.py", line 1486, in __setitem__
    self.configure({key: value})
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/tkinter/__init__.py", line 1479, in configure
    return self._configure('configure', cnf, kw)
  File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/tkinter/__init__.py", line 1470, in _configure
    self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: invalid command name ".!inspectwindow.!button"

Solution

  • The problem is this line of code:

    self.yes['command'] = self.send_email()
    

    It is the exact same as if you did this:

    result = self.send_email()
    self.yes['command'] = reuslt
    

    Since self.send_email() destroys the root window and then returns None, it's the same as doing this:

    root.destroy()
    self.yes['command'] = None
    

    Once you destroy the root widget -- which causes all other widgets to be destroyed -- you will get an error anytime you try to call a method on a widget. When you try to configure self.yes, self.yes no longer exists so you get an error.

    The solution is to pass a reference to the function of a button so that you don't immediately call it. You do it like this:

    self.yes['command'] = self.send_email
    

    Notice the lack of parenthesis on self.send_email. Instead of calling the function immediately, you are telling tk "here is the name of a function that I want you to call when the user clicks the button".