2012-05-23 8 views
5

Tworzę niektóre okna dialogowe za pomocą TkInter i muszę mieć możliwość otwarcia podmenu podrzędnego (modalnego lub niemodalnego) po kliknięciu przycisku w obiekcie nadrzędnym. Wówczas dziecko umożliwiłoby utworzenie rekordu danych, a te dane (albo rekord lub jeśli operacja została anulowana) muszą zostać przekazane z powrotem do okna macierzystego. Do tej pory mam:Jak stworzyć okno potomne i komunikować się z rodzicem w TkInter

import sel_company_dlg 

from Tkinter import Tk 

def main(): 
    root = Tk() 
    myCmp = sel_company_dlg.SelCompanyDlg(root) 
    root.mainloop() 

if __name__ == '__main__': 
    main() 

To wywołuje okno dialogowe najwyższego poziomu, które pozwala użytkownikowi wybrać firmę. Okno wyboru firma wygląda następująco:

class SelCompanyDlg(Frame): 
    def __init__(self, parent): 
     Frame.__init__(self, parent) 
     self.parent_ = parent 
     self.frame_ = Frame(self.parent_) 
     // .. more init stuff .. 
     self.btNew_ = Button(self.frame_, text="New ...", command=self.onNew) 

    def onNew(self): 
     root = Toplevel() 
     myCmp = company_dlg.CompanyDlg(root) 

Po kliknięciu na przycisk ... Nowy, zostanie wyświetlone okno dialogowe Tworzenie Firma, która pozwala użytkownikowi wypełnić dane firmy i kliknąć na utworzenie lub anulować. Oto nieco otwarcie, że:

class CompanyDlg(Frame): 
    def __init__(self, parent): 
     Frame.__init__(self, parent) 
     // etc. 

jestem zmaga się z najlepszego sposobu wywoływania okna dziecko w onNew() - co mam prace, ale nie jestem przekonany, że jest to najlepsze podejście, a także, mogę” • zobacz, jak przekazać szczegóły do ​​i z okna dialogowego podrzędnego.

Próbowałem przeglądać samouczki online/referencje, ale to, co znalazłem, jest albo zbyt uproszczone, albo skupia się na takich rzeczach jak tkMessageBox.showinfo(), co nie jest tym, czego chcę.

Odpowiedz

7

Istnieje co najmniej kilka sposobów rozwiązania problemu. Albo twoje okno dialogowe może bezpośrednio wysyłać informacje do głównej aplikacji, albo twoje okno dialogowe może wygenerować zdarzenie, które mówi głównej aplikacji, że dane naprawdę mają zostać wyciągnięte z okna dialogowego. Jeśli okno dialogowe po prostu zmienia wygląd czegoś (na przykład okno dialogowe czcionki), zwykle generuję zdarzenie. Jeśli okno dialogowe tworzy lub usuwa dane, zazwyczaj je przekazuję do aplikacji.

Zwykle mam obiekt aplikacji, który działa jako kontroler dla GUI jako całości. Często jest to ta sama klasa co okno główne, lub może być oddzielną klasą lub nawet zdefiniowaną jako mixin. Ten obiekt aplikacji ma metody, które dialogi mogą wywoływać w celu przesłania danych do aplikacji.

Na przykład:

class ChildDialog(tk.Toplevel): 
    def __init__(self, parent, app, ...) 
     self.app = app 
     ... 
     self.ok_button = tk.Button(parent, ..., command=self.on_ok) 
     ... 
    def on_ok(self): 
     # send the data to the parent 
     self.app.new_data(... data from this dialog ...) 

class MainApplication(tk.Tk): 
    ... 

    def on_show_dialog(self): 
     dialog = ChildDialog(self) 
     dialog.show() 

    def new_data(self, data): 
     ... process data that was passed in from a dialog ... 

Podczas tworzenia okna, należy przekazać w odniesieniu do obiektu aplikacji. Okno dialogowe wie wtedy, jak wywołać określoną metodę dla tego obiektu, aby wysłać dane z powrotem do aplikacji.

Jeśli nie interesuje Cię model/widok/kontroler, możesz równie łatwo przekazać funkcję, a nie obiekt, skutecznie mówiąc w oknie dialogowym "zadzwoń do tej funkcji, gdy chcesz podać mi dane".

+0

Dzięki bardzo za to, to naprawdę pomogło. –

+0

# bryan-oakley Ładne podsumowanie technik. Czy polecasz dalsze szczegóły dotyczące takich technik (Internet lub książki) na temat wymiany danych między rodzicami a oknami potomnymi? : - generowanie zdarzeń z powrotem do rodzica - przesyłanie danych z powrotem do rodzica - ... – AJN

2

W jednym z moich projektów próbowałem sprawdzić w oknie podrzędnym tk.Thoplevel (child1) mojego okna głównego (self), czy okno tk.Toplevel (child2) zostało utworzone przez użytkownika z poziomu root'a okno i jeśli to okno (dziecko2) jest obecne na ekranie użytkownika.

Jeśli tak nie jest, nowe okno tk.Toplevel powinno zostać utworzone przez okno potomne (element potomny1) okna głównego zamiast samego okna głównego. A jeśli został już utworzony przez okno główne i jest aktualnie obecny na ekranie użytkownika, powinien uzyskać focus() zamiast ponownego zainicjowania przez "child1".

Okno główne zostało zapakowane wewnątrz klasy o nazwie App(), a oba okna "dzieci" zostały utworzone metodami wewnątrz aplikacji klasy głównej().

Musiałem zainicjować "child2" w trybie cichym, jeśli argument dotyczący metody był prawdziwy. Przypuszczam, że to był splątany błąd. Problem wystąpił w 64-bitowym systemie Windows 7, jeśli jest to znaczące.

Próbowałem ten (przykład):

import tkinter as tk 
from tkinter import ttk 
class App(tk.Tk): 
    def __init__(self): 
     tk.Tk.__init__(self) 
     top = self.winfo_toplevel() 
     self.menuBar = tk.Menu(top) 
     top['menu'] = self.menuBar 
     self.menuBar.add_command(label='Child1', command=self.__create_child1) 
     self.menuBar.add_command(label='Child2', command=lambda: self.__create_child2(True)) 
     self.TestLabel = ttk.Label(self, text='Use the buttons from the toplevel menu.') 
     self.TestLabel.pack() 
     self.__create_child2(False) 

    def __create_child1(self): 
     self.Child1Window = tk.Toplevel(master=self, width=100, height=100) 
     self.Child1WindowButton = ttk.Button(self.Child1Window, text='Focus Child2 window else create Child2 window', command=self.CheckForChild2) 
     self.Child1WindowButton.pack() 

    def __create_child2(self, givenarg): 
     self.Child2Window = tk.Toplevel(master=self, width=100, height=100) 
     if givenarg == False: 
      self.Child2Window.withdraw() 
      # Init some vars or widgets 
      self.Child2Window = None 
     else: 
      self.Child2Window.TestLabel = ttk.Label(self.Child2Window, text='This is Child 2') 
      self.Child2Window.TestLabel.pack() 

    def CheckForChild2(self): 
     if self.Child2Window: 
      if self.Child2Window.winfo_exists(): 
       self.Child2Window.focus() 
      else: 
       self.__create_child2(True) 
     else: 
      self.__create_child2(True) 

if __name__ == '__main__': 
    App().mainloop() 

tu pojawia się problem: nie byłem w stanie sprawdzić, czy "child2" jest już obecny. Dostaje błąd: _tkinter.TclError: Bad okna nazwę ścieżki

Rozwiązanie:

Jedynym sposobem, aby uzyskać właściwą nazwę „okno ścieżka” był, zamiast wywoływania winfo_exists() metoda bezpośrednio na oknie „child2” , wywołanie wzorca okna "child1" i dodanie odpowiednich atrybutów, po których następuje atrybut okna głównego, którego chcesz użyć.

Przykład (edycja metody CheckForChild2):

def CheckForChild2(self): 
     if self.Child2Window: 
      if self.Child1Window.master.Child2Window.winfo_exists(): 
       self.Child1Window.master.Child2Window.focus() 
      else: 
       self.__create_child2(True) 
     else: 
      self.__create_child2(True) 
Powiązane problemy