How to determine if tkinter Toplevel widget exists.?

Question:

I have a need to destroy a Toplevel widget if it already exists before creating it. I’ve researched this and every response I can find here and elsewhere suggest using winfo_exists; however, the following code demonstrates that doesn’t work. It fails with the error: object has no attribute ‘toplevel’

I can use a brute force try:/except: to destroy the widget, but surely there’s a better way.

BTW: I am on Windows 10 running Python 3.11.1

# does_widger_exists.py

import tkinter as tk


class TL_GUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.geometry('300x100')
        self.root.title('Tkinter Test')
        button0 = tk.Button(self.root,text='Create Toplevel',command=self.makeToplevel).pack(pady=20)
        self.root.mainloop()
 
    def makeToplevel(self):
        if tk.Toplevel.winfo_exists(self.toplevel):
            self.toplevel.destroy()
        self.toplevel = tk.Toplevel(self.root)
        self.toplevel.geometry('500x100')
        self.toplevel.title('I am a TopLevel')
        text0 = tk.Label(self.toplevel,text='Hello World',height=1,width=25,borderwidth=5).pack()
 

if __name__ == '__main__':
    TL_GUI()
Asked By: Pragmatic_Lee

||

Answers:

You’ll need to initialize self.toplevel to some value in your __init__ method so the first call to makeToplevel doesn’t fail when it gets to the conditional; as written, self.toplevel doesn’t exist when the check is first run. With this fix, subsequent calls to makeToplevel will work as expected

def __init__(self):
    self.root = tk.Tk()
    self.root.geometry('300x100')
    self.root.title('Tkinter Test')

    self.toplevel = None  # initialize this instance variable

    button0 = tk.Button(
        self.root,
        text='Create Toplevel',
        command=self.makeToplevel
    ).pack(pady=20)
    self.root.mainloop()

You should also update the condition checked by the if statement as follows:

def makeToplevel(self):
    if self.toplevel and self.toplevel.winfo_exists():  # make this change
        self.toplevel.destroy()
    self.toplevel = tk.Toplevel(self.root)
    self.toplevel.geometry('500x100')
    self.toplevel.title('I am a TopLevel')
    text0 = tk.Label(
        self.toplevel,
        text='Hello World',
        height=1,
        width=25,
        borderwidth=5
    ).pack()

Oh, and one final note: using pack() like you are on button0 and text0 can cause trouble if you’re not careful! As written, button0 and text0 will both always evaluate to None since that’s the value returned by pack(). If you want to refer to these widgets anywhere else, you should pack() them on a separate line, e.g.:

button0 = tk.Button(...)  # now 'button0' is equal to the Button object, not None
button0.pack(...)

text0 = tk.Label(...)  # now 'text0' is equal to the Label object, not None
text0.pack(...)
Answered By: JRiggles
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.