How should I spawn child Toplevel()
windows in tkinter that don't close when the parent closes?
Do I need to have the parent keep a 'reference count' of child windows, intercept WM_DELETE_WINDOW
and only call root.destroy()
when all the children are gone?
Or is it acceptable practice to spawn another thread process with its own tk mainloop
?
Or is there a more elegant way?
EDIT
I am currently doing things this way
root = Tk()
app = App(root) # doesn't call Toplevel()
root.mainloop()
where App.__init__()
adds widgets to root
without calling Toplevel()
, and at some point spawns a new window with this function:
def new_window():
root = Tk()
window = App2(root) # doesn't call Toplevel() either
Note that root
in new_window()
is a different variable to the original root
, obtained by another call to Tk()
.
All this seems to Do The Right Thing i.e. the child window lives independently of the parent and the python process dies after both are closed.
So my question becomes, does that make sense or am I doing something horribly wrong here?
Instead of having to keep track of which Toplevels are alive, you could use a weakref on a sentinel -- just some object that is passed to each Toplevel and saved in a reference. When each Toplevel dies (is closed), let it delete its reference to the sentinel. When the last reference to the sentinel is deleted, the weakref callback, self.no_sentinel
will be called automatically, in turn calling root.destroy
for you.
import Tkinter as tk
import weakref
class Sentinel(object):
pass
class Window(tk.Toplevel):
def __init__(self, master, sentinel, **kwargs):
title = kwargs.pop('title')
self.sentinel = sentinel
tk.Toplevel.__init__(self, master, **kwargs)
self.protocol("WM_DELETE_WINDOW", self.ondelete)
self.label = tk.Label(self, text=title)
self.label.pack(padx=10, pady=10)
def ondelete(self):
self.destroy()
del self.sentinel
class App(object):
def __init__(self, master, **kwargs):
self.master = master
sentinel = Sentinel()
parent = Window(master, sentinel, title='Parent')
child = Window(master, sentinel, title='Child')
self._ref = weakref.ref(sentinel, self.no_sentinel)
# When we exit `__init__` only two strong references to sentinal
# remain -- in parent and child. When both strong references are
# deleted, `self.no_sentinel` gets called.
def no_sentinel(self, *args):
self.master.destroy()
root = tk.Tk()
root.withdraw()
app = App(root)
root.mainloop()
Alternatively, you could use the multiprocessing
module to spawn another process to make another Tkinter window and mainloop, but would be consumer more memory than the solution above, and would require you to setup some form of interprocess communication if you wanted the separate processes to share information.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With