2011-01-11 14 views
8

He creado una aplicación PyGTK que muestra un cuadro de diálogo cuando el usuario presiona un botón. El diálogo se carga en mi método __init__ con:¿Cómo mostrar repetidamente un cuadro de diálogo con PyGTK/Gtkbuilder?

builder = gtk.Builder() 
builder.add_from_file("filename") 
builder.connect_signals(self) 
self.myDialog = builder.get_object("dialog_name") 

En el controlador de eventos, el cuadro de diálogo se muestra con el comando self.myDialog.run(), pero esto sólo funciona una vez, porque después de run() el cuadro de diálogo se destruye automáticamente. Si hago clic en el botón por segunda vez, la aplicación se bloquea.

leí que hay una manera de utilizar show() en lugar de run() donde el diálogo no se destruye, pero siento que este no es el camino correcto para mí, porque me gustaría que el diálogo que se comporten de forma modal y para devolver el control a el código solo después de que el usuario lo haya cerrado.

¿Hay una manera simple de mostrar un diálogo repetidamente usando el método run() usando gtkbuilder? Intenté volver a cargar todo el cuadro de diálogo usando gtkbuilder, pero eso no parecía funcionar, al diálogo le faltaban todos los elementos secundarios (y preferiría tener que usar el generador solo una vez, al comienzo del programa).


[Solución] (editado)
Como ha señalado que la respuesta a continuación, utilizando hide() hace el truco. Primero pensé que aún necesitabas atrapar el "evento de eliminación", pero esto de hecho no es necesario. Un ejemplo sencillo que funciona es:


import pygtk 
import gtk 

class DialogTest: 

    def rundialog(self, widget, data=None): 
     self.dia.show_all() 
     result = self.dia.run() 
     self.dia.hide() 


    def destroy(self, widget, data=None): 
     gtk.main_quit() 

    def __init__(self): 
     self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) 
     self.window.connect("destroy", self.destroy) 

     self.dia = gtk.Dialog('TEST DIALOG', self.window, 
      gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT) 
     self.dia.vbox.pack_start(gtk.Label('This is just a Test')) 


     self.button = gtk.Button("Run Dialog")  
     self.button.connect("clicked", self.rundialog, None) 
     self.window.add(self.button) 
     self.button.show() 
     self.window.show() 



if __name__ == "__main__": 
    testApp = DialogTest() 
    gtk.main() 

Respuesta

4

En realidad, lea el documentation en Dialog.run(). El diálogo no se destruye automáticamente. Si lo hace hide() cuando sale el método run(), debe poder run() tantas veces como desee.

O bien, puede configurar el diálogo para que sea modal en su archivo de generador, y luego simplemente show(). Esto logrará un efecto similar, pero no exactamente igual que run() - porque run() crea una segunda instancia del bucle GTK principal.

EDITAR

La razón por la que está recibiendo un fallo de segmentación si no se conecta a la señal delete-event es que se está haciendo clic en el botón cerrar dos veces. Esto es lo que sucede:

  1. Haga clic en "Ejecutar Diálogo", esto llama al método run() del diálogo.
  2. Aparece el cuadro de diálogo modal y comienza su propio ciclo principal.
  3. Haga clic en el botón de cerrar. El ciclo principal del diálogo sale, pero dado que run() anula el comportamiento normal del botón de cierre, el diálogo no se cierra. Tampoco está oculto, por lo que se mantiene.
  4. Te preguntas por qué el cuadro de diálogo aún está allí y haz clic en el botón de cerrar nuevamente. Como run() ya no está activo, se desencadena el comportamiento normal del botón de cerrar: el diálogo se destruye.
  5. Vuelve a hacer clic en "Ejecutar cuadro de diálogo", que intenta llamar al método run() del cuadro de diálogo destruido. ¡Choque!

Así que si se asegura de que hide() el cuadro de diálogo después del paso 3, entonces todo debería funcionar. No es necesario conectarse a la señal delete-event.

+0

Bueno, la documentación no es muy clara aquí. Parece que el método "ejecutar" en sí no destruye el diálogo, pero si lo cierra presionando el control "cerrado" proporcionado por el administrador de ventanas (generalmente una pequeña "X" en la parte superior derecha de la ventana ", el el diálogo se destruye si no detecta el "evento de eliminación" – Julian

+0

@Julian, es al revés: consulte el segundo párrafo de la documentación. El diálogo no se destruirá aunque cierre la ventana. – ptomato

+0

Sí, lo leí pero estoy un poco confundido: si en el ejemplo que publiqué arriba, comente la línea 'self.dia.connect (" delete-event ", self.closedialog)' Recibo una falla de segmentación la segunda vez que hago clic en el botón.Entonces ALGO ha sido destruido sin que yo lo haya hecho, ¿verdad? Gracias por sus aclaraciones por cierto. – Julian

0

Su diálogo solo debería ejecutarse una vez. Suponiendo un elemento de menú activa el diálogo, el código debería ser algo como esto:

def on_menu_item_clicked(self, widget, data=None): 
    dialog = FunkyDialog() 
    response = dialog.run() 

    if response = gtk.RESPONSE_OK: 
     // do something with the dialog data 

    dialog.destroy() 

dialog.run() principal es un bucle de bloqueo que devuelve cuando el diálogo de enviar una respuesta. Esto se hace normalmente a través de los botones Ok y Cancel. Cuando esto sucede, el diálogo finaliza y necesita ser destruido.

Para mostrar el cuadro de diálogo varias veces, el usuario debe seguir el mismo flujo de trabajo (en el ejemplo anterior, que haría clic en un elemento del menú). El diálogo es responsable, en el __init__, para establecerse. Si hide() el cuadro de diálogo, tiene el problema de comunicarse con ese cuadro de diálogo para que se mantenga actualizado con el resto de la aplicación incluso cuando está oculta.

Uno de los motivos por los que algunas personas quieren "ejecutar el cuadro de diálogo varias veces" es porque el usuario ha ingresado información no válida y desea darle al usuario la oportunidad de corregirla. Esto debe tratarse en el manejador de señal de respuesta del diálogo. El orden de los acontecimientos en un cuadro de diálogo es:

  1. usuario pulsa el botón Aceptar físicamente
  2. diálogo envía la respuesta gtk.RESPONSE_OK (-5)
  3. diálogo llama al manejador de la señal de respuesta
  4. llama al diálogo controlador para el botón Aceptar
  5. diálogo run() método devuelve la respuesta

para prevenir los pasos 4 y 5 suceden, el controlador de respuesta debe suprimir la señal de respuesta. Esto se logra de la siguiente manera:

def on_dialog_response(self, dialog, response, data=None: 
    if response == gtk.RESPONSE_OK: 
     if data_is_not_valid: 
      # Display an error message to the user 

      # Suppress the response 
      dialog.emit_stop_by_name('response') 
+0

Bueno, pero utilizo GtkBuilder para obtener el diálogo y una vez que se destruye el diálogo, se queda destruido, no pude encontrar la manera de volver a configurarlo. – Julian

+0

destroy() hace exactamente eso, destruye el objeto. Si desea volver a mostrar el mismo cuadro de diálogo, debe crear otra instancia. Lea http://www.pygtk.org/docs/pygtk/class-gtkwidget.html#method-gtkwidget--destroy para obtener más información sobre destroy(), más notablemente: "Si el widget es un nivel superior (derivado de gtk. Ventana), se eliminará de la lista de niveles, y la referencia que PyGTK mantiene se eliminará ". – Jon

2

Acabo de pasar un tiempo averiguando esto. Volver a buscar el mismo objeto desde un generador no creará una nueva instancia del objeto, sino que solo devolverá una referencia al objeto antiguo (destruido). Sin embargo, si creas una nueva instancia de compilador y cargas tu archivo en el nuevo compilador, creará una nueva instancia.

Así que mi función de creación de diálogo se ve algo como esto:

def create(): 
    builder = gtk.Builder() 
    builder.add_from_file('gui/main.ui') 

    dlg = builder.get_object('new_dialog') 

    def response_function(dialog, response_id): 
     ... do stuff ... 
     dialog.destroy() 

    dlg.connect('response', response_function) 
    dlg.show_all() 

Tenga en cuenta que yo no estoy bloqueando una respuesta con run() en este caso porque estoy usando retorcida, pero debe ser equivalente.

Cuestiones relacionadas