2013-06-11 2 views
5

Как я должен порождать дочерние объекты Toplevel() окна в tkinter, которые не закрываются при закрытии родителя?Сделать окно tkinter toplevel, которое не закрывается с родителем

Должен ли я иметь родительскую учетную запись «дочерних окон», перехватывать WM_DELETE_WINDOW и звонить только root.destroy(), когда все дети ушли?

Или это приемлемая практика, чтобы порождать другую нить процесс со своим собственным tk mainloop?

Или есть более элегантный способ?

EDIT

Я сейчас делаю вещи таким образом

root = Tk() 
app = App(root) # doesn't call Toplevel() 
root.mainloop() 

где App.__init__() добавляет виджеты к root без вызова Toplevel(), и в какой-то момент порождает новое окно с помощью этой функции:

def new_window(): 
    root = Tk() 
    window = App2(root) # doesn't call Toplevel() either 

Отметьте, что root в new_window() - это другая переменная к исходному root, полученная другим звонком до Tk().

Все это, кажется, делает правильную вещь, то есть дочернее окно живет независимо от родителя, и процесс python умирает после того, как оба они закрыты.

Так что мой вопрос становится, это имеет смысл или я делаю что-то ужасно неправильно здесь?

ответ

1

Вместо того, чтобы отслеживать, какие Toplevels являются живыми, вы можете использовать weakref на дозорном аппарате - только некоторый объект, который передается каждому верхнему уровню и сохраняется в ссылке. Когда каждый Toplevel умирает (закрывается), давайте удалим его ссылку на дозор. Когда последняя ссылка на дозор будет удалена, будет вызван метод callback, вызванный weakref, self.no_sentinel, в свою очередь вызывающий root.destroy для вас.

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() 

В качестве альтернативы, вы могли использовать multiprocessing модуль, чтобы породить другой процесс, чтобы сделать еще одно окно Tkinter и MainLoop, но был бы потребитель больше памяти, чем выше решение, и может потребовать от вас установки той или иной форме межпроцессного если вы хотите, чтобы отдельные процессы обменивались информацией.

+0

Теперь я получаю новый корень с помощью 'tk.Tk()', но не вызываю 'mainloop()' снова - это, кажется, работает, будучи намного проще - любая идея, почему это может быть? Вы думаете, что 'Tk()' будет singleton, возвращая тот же самый мастер, но создание виджета на нем, похоже, не влияет на первое окно. –

+0

Btw см. Мое редактирование на исходный вопрос - я имел в виду процесс не поток. –

+1

Я не совсем уверен, что понимаю ваш вопрос. Существует гораздо более простой способ, чем то, что я показываю выше - просто используйте Toplevels и назовите 'root.withdraw()' - но тогда вы можете закрыть все свои Toplevels, и программа все равно будет работать в фоновом режиме. Таким образом, вы будете накапливать процессы зомби. Цель вышеперечисленного rigmarole заключается в том, что программа заканчивается, когда последний Toplevel закрыт. Если я не отвечу на ваш вопрос, отправьте свой код. – unutbu

Смежные вопросы