2012-05-23 15 views
5

estoy creando algunos cuadros de diálogo utilizando TkInter y la necesidad de ser capaz de abrir una sub-ventana secundaria (modal o no) al hacer clic en un botón en la matriz. El niño permitiría entonces crear un registro de datos y estos datos (ya sea el registro o si la operación se canceló) deben ser comunicados nuevamente a la ventana principal. Hasta ahora tengo:Cómo crear ventana secundaria y comunicarse con los padres en 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() 

Esto invoca el diálogo de nivel superior que permite al usuario seleccionar una empresa. El diálogo de selección de la empresa es el siguiente:

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) 

Al hacer clic en el botón ...Nueva, se muestra un cuadro de diálogo Crear Company, que permite al usuario para completar datos de la empresa y haga clic en crear o cancelar. Aquí está la parte de apertura de eso:

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

Estoy luchando con la mejor manera de invocar el diálogo de niño en onNew() - lo que tengo obras, pero no estoy convencido de que es el mejor enfoque y también, puedo' t ver cómo comunicar los detalles ay desde el cuadro de diálogo secundario.

He intentado buscar tutoriales en línea/referencias, pero lo que he encontrado es demasiado simplista o se centra en cosas como tkMessageBox.showinfo() que no es lo que quiero.

Respuesta

7

Hay por lo menos un par de maneras de resolver su problema. O bien su diálogo puede enviar información directamente a la aplicación principal, o su diálogo puede generar un evento que le dice a la aplicación principal que los datos realmente se extraerán del diálogo. Si el diálogo simplemente cambia la apariencia de algo (por ejemplo, un diálogo de fuente) generalmente genero un evento. Si el cuadro de diálogo crea o borra datos, normalmente lo hago enviar información a la aplicación.

I tienen típicamente un objeto de aplicación que actúa como el controlador para la interfaz gráfica de usuario como un todo. A menudo esta es la misma clase que la ventana principal, o puede ser una clase separada o incluso definida como mixin. Este objeto de aplicación tiene métodos a los que los diálogos pueden llamar para enviar datos a la aplicación.

Por ejemplo:

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 ... 

Al crear el diálogo, se pasa una referencia al objeto de aplicación. El diálogo luego sabe llamar a un método específico en este objeto para enviar datos a la aplicación.

Si no está familiarizado con todo el modelo/vista/controlador, puede pasar fácilmente una función en lugar de un objeto, diciendo efectivamente al diálogo "llamar a esta función cuando quiera darme datos".

+0

Muchas gracias por esto, que realmente ayudó. –

+0

# bryan-oakley Buen resumen de técnicas. ¿Recomiendan más referencias de detalles sobre tales técnicas (Internet o recursos de libros) sobre el intercambio de datos entre padres e hijos? : - la generación de eventos vuelve al elemento principal - devolviendo los datos al elemento primario - ... – AJN

2

En uno de mis proyectos estaba intentando verificar dentro de una ventana tk.Toplevel (child1) de mi ventana raíz (self), si el usuario creó una ventana tk.Toplevel (child2) desde dentro de la raíz ventana, y si esta ventana (child2) está presente en la pantalla de los usuarios en este momento.

Si esto no sería el caso, la nueva ventana tk.Toplevel debería consigue creado por la ventana secundaria (child1) de la ventana raíz, en lugar de la ventana raíz en sí. Y si ya fue creado por la ventana raíz y actualmente está presente en la pantalla de los usuarios, se debe obtener el foco() en lugar de ser reinicializado por "child1".

La ventana raíz estaba envuelto dentro de una clase llamada App() y las dos ventanas "niños" fueron creados por métodos dentro de la clase raíz de la aplicación().

Tuve que inicializar "child2" en modo silencioso si un argumento dado al método era True. Supongo que ese fue el error enredado. El problema ocurrió en Windows 7 64 bit, si eso es significativo.

yo probamos este (ejemplo):

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

Aquí viene el problema: yo no era capaz de comprobar si "child2" ya está presente. Ha obtenido un error: _tkinter.TclError: mala ventana de nombre de ruta

Solución:

La única manera de obtener el derecho de 'ventana de nombre de ruta' era, en lugar de llamar al método winfo_exists() directamente en la ventana "child2" , llamando al maestro de la ventana "niño1" y agregando los atributos correspondientes seguidos por los atributos de la ventana maestra que desea usar.

Ejemplo (edición del método 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) 
Cuestiones relacionadas