pythontkintertcl

The `w.destroy()` method does not eradicate the `tkinter` widget?


I submitted the following commands in the Python interpreter and discovered that the w.destroy() method of a tkinter widget does not eradicate (i.e. wipe out the existence of) the tkinter widget.

>>> root=tk.Tk()
>>> a = ttk.Button(root, text="Button")
>>> a.grid(row=0, column=0)
>>> a
    <tkinter.ttk.Button object .!button>
>>> a.destroy()
>>> a
    <tkinter.ttk.Button object .!button>
>>> a.grid(row=0, column=0)
    Traceback (most recent call last):
      File "/usr/lib/python3.10/idlelib/run.py", line 578, in runcode
        exec(code, self.locals)
      File "<pyshell#23>", line 1, in <module>
      File "/usr/lib/python3.10/tkinter/__init__.py", line 2522, in grid_configure
        self.tk.call(
    _tkinter.TclError: bad window path name ".!button"
>>> b
    Traceback (most recent call last):
      File "/usr/lib/python3.10/idlelib/run.py", line 578, in runcode
        exec(code, self.locals)
      File "<pyshell#24>", line 1, in <module>
    NameError: name 'b' is not defined
>>> del a
>>> a
    Traceback (most recent call last):
      File "/usr/lib/python3.10/idlelib/run.py", line 578, in runcode
        exec(code, self.locals)
      File "<pyshell#26>", line 1, in <module>
    NameError: name 'a' is not defined
>>> root.destroy()
>>> root
    <tkinter.Tk object .>
>>> del root
>>> root
    Traceback (most recent call last):
      File "/usr/lib/python3.10/idlelib/run.py", line 578, in runcode
        exec(code, self.locals)
      File "<pyshell#30>", line 1, in <module>
    NameError: name 'root' is not defined
>>> 

Quoting documentation:

w.destroy()
Calling w.destroy() on a widget w destroys w and all its children.

In the above example, although a.destroy() did remove the appearance of the ttk.Button and not let a run other tkinter methods, yet a is still recognised as a <tkinter.ttk.Button object .!button> object, i.e. the existence of a was not eradicated from the Python Interpreter. Only after deleting a via the del a command caused the NameError: name 'a' is not defined exception when a was called. The same outcome is seen in root.destroy().

I think the tkinter package maintainers need to include a del w command (here, w refers to any tkinter widget) to its .destroy() method. If not, tkinter users presently need to run a del w command after every w.destroy() method to ensure cleanliness in their Python codes. Is my reasoning sound?

Following the thoughts in my comment, below is an example of a class method eradicating a class attribute:

>>> class App(tk.Frame):
        def __init__(self, master, **kw):
            super().__init__(master, bg="pink")
            self.master = master
            self.bn = tk.Button(self, text="Button", bg="cyan", width=10, height=2)
            self.bn.grid(row=0, column=0)
        def eradicate(self):
            del self.bn

>>> root = tk.Tk()
>>> root.geometry("150x150+10+10")
    ''
>>> app = App(root)
>>> app.grid(row=0, column=0)
>>> root.columnconfigure(0, weight=1)
>>> root.rowconfigure(0, weight=1)
>>> app.eradicate()
>>> app.bn
    Traceback (most recent call last):
      File "/usr/lib/python3.10/idlelib/run.py", line 578, in runcode
        exec(code, self.locals)
      File "<pyshell#116>", line 1, in <module>
    AttributeError: 'App' object has no attribute 'bn'

Solution

  • I think the tkinter package maintainers need to include a del w command (here, w refers to any tkinter widget) to its .destroy() method. If not, tkinter users presently need to run a del w command after every w.destroy() method to ensure cleanliness in their Python codes. Is my reasoning sound?

    No, your reasoning isn't sound. A object can't destroy the variables that reference the object. For example, a single object can be referenced by more than one variable, how is the code supposed to know which variable you want destroyed? And what if you don't want the reference destroyed? There are use cases where you want to keep the reference even though the widget itself has been destroyed.

    There should almost never be the need to call del on the variable that is holding a reference to a widget. Python has the ability to automatically destroy those objects when it detects they are no longer being used (called the garbage collector).