Syntactic Fructose
Syntactic Fructose

Reputation: 20076

Tkinter destroying a toplevel window also destroys other windows

I'm running into this really strange behavior with Toplevel windows, I have some logic like so:

Did user click 'properties button?'
    yes? bind <ButtonPress-1> to window that will call properties(mouselocation)

properties(mouselocation):
    find location of cursor
    create property window at cursor

The problem I'm running into is that somehow, when I create multiple windows and attempt to close one, they all close. I've managed to create a minimal example which highlights my issue, the PropertiesDialog class is exactly what I'm using minus a couple small changes so it can work for this example:


from Tkinter import *

class PropertyDialog(Toplevel):
    def __init__(self, root, string):
        Toplevel.__init__(self)
        self.wm_overrideredirect(1)
        self.\
             geometry('+%d+%d' %
                      (root.winfo_pointerx(),
                       root.winfo_pointery()))
        try:
            self.tk.call('::Tk::unsupported::MacWindowStyle',
                                         'style', self._w,
                                         'help', 'noActivates')
        except TclError:
            pass
        window_frame = Frame(self)
        window_frame.pack(side=TOP, fill=BOTH, expand=True)
        exit_frame = Frame(window_frame, background='#ffffe0')
        exit_frame.pack(side=TOP, fill=X, expand=True)
        button = Button(exit_frame, text='x', width=3, command=self.free,
               background='#ffffe0', highlightthickness=0, relief=FLAT)
        button.pack(side=RIGHT)
        text_frame = Frame(window_frame)
        text_frame.pack(side=TOP, fill=BOTH, expand=True)
        label = Label(text_frame, text=string, justify=LEFT,
                      background='#ffffe0',
                      font=('tahoma', '8', 'normal'))
        label.pack(ipadx=1)

    def free(self):
        self.destroy()

def bind():
    """
    toggle property window creation mode
    """
    root.bind('<ButtonPress-1>', create)


def create(event):
    """
    Create actual window upon mouse click
    """
    t = PropertyDialog(root, 'help me')
    return

root = Tk()
root.geometry('%dx%d' % (300,400))

Button(root, text='create', command=bind).pack()

root.mainloop()

Now if you run this application, click create and click on random areas of the window then attempt to close one. For one reason, every window closes upon attempting to destroy just one. What am I doing wrong to create this behavior?

Upvotes: 1

Views: 552

Answers (1)

TigerhawkT3
TigerhawkT3

Reputation: 49320

The other windows aren't being destroyed, they're simply being hidden by the main window. You can see them if you create some popups, close one of them, then move the main window.

To fix this, lower the main window below all the others. The changed areas are marked with #CHANGED#:

from Tkinter import *

class PropertyDialog(Toplevel):
    def __init__(self, root, string):
        Toplevel.__init__(self)
        self.wm_overrideredirect(1)
        self.root = root #CHANGED# save a reference to the root
        self.\
             geometry('+%d+%d' %
                      (root.winfo_pointerx(),
                       root.winfo_pointery()))
        try:
            self.tk.call('::Tk::unsupported::MacWindowStyle',
                                         'style', self._w,
                                         'help', 'noActivates')
        except TclError:
            pass
        window_frame = Frame(self)
        window_frame.pack(side=TOP, fill=BOTH, expand=True)
        exit_frame = Frame(window_frame, background='#ffffe0')
        exit_frame.pack(side=TOP, fill=X, expand=True)
        button = Button(exit_frame, text='x', width=3, command=self.free,
               background='#ffffe0', highlightthickness=0, relief=FLAT)
        button.pack(side=RIGHT)
        text_frame = Frame(window_frame)
        text_frame.pack(side=TOP, fill=BOTH, expand=True)
        label = Label(text_frame, text=string, justify=LEFT,
                      background='#ffffe0',
                      font=('tahoma', '8', 'normal'))
        label.pack(ipadx=1)

    def free(self): #CHANGED# this method significantly
        self.destroy() # first we destroy this one
        for val,widget in enumerate(dialogs): # go through the dialogs list
            if widget is self: # when we find this widget
                dialogs.pop(val) # pop it out
                break # and stop searching
        if dialogs: # if there are any dialogs left:
            for widget in dialogs: # go through each widget
                widget.lift(aboveThis=self.root) # and lift it above the root

def bind():
    """
    toggle property window creation mode
    """
    root.bind('<ButtonPress-1>', create)


def create(event): #CHANGED# this to store the widget in a list
    """
    Create actual window upon mouse click
    """
    dialogs.append(PropertyDialog(root, 'help me'))

root = Tk()
dialogs = [] #CHANGED# to initialize a list
root.geometry('%dx%d' % (300,400))

Button(root, text='create', command=bind).pack()

root.mainloop()

Upvotes: 2

Related Questions