2013-06-11 21 views
5

Jak mam odradzać okna dla dzieci Toplevel() w tkinter, które nie zamykają się, gdy rodzic się zamknie?Stwórz okno z tekselami, które nie zamyka się z rodzicem.

Czy muszę mieć, aby rodzic zachowywał "liczbę referencyjną" okien potomnych, przechwytywać WM_DELETE_WINDOW i dzwonić pod numer root.destroy(), gdy wszystkie dzieci zniknęły?

Czy jest to dopuszczalna praktyka, aby spawnować inny proces wątku z własnym kodem mainloop?

A może jest bardziej elegancki sposób?

EDIT

Jestem obecnie robi rzeczy w ten sposób

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

gdzie App.__init__() dodaje widgety do root bez wywoływania Toplevel(), iw pewnym momencie ikra nowe okno z tej funkcji:

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

Zauważ, że root w new_window() to inna zmienna niż oryginalna root, uzyskana przez inne połączenie z Tk().

Wszystko to wydaje się robić właściwe rzeczy, tj. Okno potomne żyje niezależnie od rodzica, a proces python umiera po zamknięciu obu.

Moje pytanie brzmi, czy to ma sens, czy też robię coś okropnego tutaj?

Odpowiedz

1

Zamiast śledzić, które z nich są żywe, można użyć wartości weakref na wartowniku - tylko niektóre obiekty, które są przekazywane do każdego Tweenela i zapisane w pliku referencyjnym. Kiedy każdy Toplevel umiera (jest zamknięty), niech usuwa swoje odniesienie do wartownika. Kiedy ostatnie odwołanie do sentinela zostanie usunięte, wywołanie zwrotne weakref, self.no_sentinel zostanie wywołane automatycznie, z kolei wywołując dla ciebie 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() 

Alternatywnie, możesz mógłby użyć modułu multiprocessing na tarło innego procesu, aby kolejne okno tkinter i MainLoop, ale byłoby konsument więcej pamięci niż powyższego roztworu i będzie wymagać instalacji jakąś formę Interprocess komunikacja, jeśli chcesz, aby oddzielne procesy udostępniały informacje.

+0

Teraz uzyskuję nowy katalog główny z 'tk.Tk()' ale nie ponownie wywołuję 'mainloop()' - to wydaje się działać, chociaż jest znacznie prostsze - jakikolwiek pomysł, dlaczego tak się stało? Można by pomyśleć, że 'Tk()' będzie singletonem zwracającym tego samego mistrza, ale tworzenie widgetów na nim nie wydaje się wpływać na pierwsze okno. –

+0

Przy okazji zobaczę moją edycję na oryginalne pytanie - chodzi mi o proces, a nie wątek. –

+1

Nie jestem pewien, czy rozumiem twoje pytanie. Jest o wiele prostszy sposób niż to, co pokazałem powyżej - wystarczy użyć Toplevels i wywołać 'root.withdraw()' - ale wtedy możesz zamknąć wszystkie swoje Toplevels i program będzie nadal działał w tle. W ten sposób zbierzesz procesy zombie. Celem powyższego rigmarole jest spowodowanie, że program zakończy się po zamknięciu ostatniego Toplevel. Jeśli nie odpowiem na twoje pytanie, opublikuj swój kod. – unutbu

Powiązane problemy