2.4. Hola Mundo Paso a Paso

Ahora que ya conocemos la teoría que hay detrás de esto, vamos a aclarar el programa de ejemplo helloworld.py paso a paso.

Las líneas 7-74 definen la clase HelloWorld que contienen todas las retrollamadas como métodos de objeto y el método de inicialización de objetos. Vamos a examinar los métodos de retrollamada.

Las líneas 11-12 definen el método de retrollamada hello() que se llamará cuando el botón sea pulsado. Cuando se llama a este método, se imprime "Hello World" a la consola. En este ejemplo ignoramos los parámetros de la instancia del objeto, el control y los datos, pero la mayoría de las retrollamadas los usan. El parámetro data se define con un valor por defecto de None porque PyGTK no pasará ningún valor para los datos si no son incluidos en la llamada a connect(); esto lanzaría un error ya que la retrollamada espera tres argumentos y solo recibe dos. Definir un valor por defecto None permite llamar a la retrollamada con dos o tres parámetros sin ningún error. En este caso el parámetro de datos podría haberse omitido ya que el método hello() siempre será llamado con sólo dos parámetros (nunca se usan los datos de usuario). En el siguiente ejemplo usaremos el argumento de datos para saber que botón fue pulsado.

  def hello(self, widget, data=None):
      print "Hello World"

La siguiente retrollamada (líneas 14-24) es un poco especial. El evento "delete_event" se produce cuando el manejador de ventanas manda este evento al programa. Tenemos varias posibilidades en cuanto a qué hacer con estos eventos. Podemos ignorarlos, realizar algun tipo de respuesta, o simplemente cerrar el programa.

El valor que devuelvas en esta retrollamada le permite a GTK saber qué acción realizar. Devolviendo TRUE, le hacemos saber que no queremos que se emita la señal "destroy", y asi nuestra aplicación sigue ejecutandose. Devolviendo FALSE, pedimos que se emita la señal "destroy", que a su vez llamará a nuestro manejador de la señal "destroy". Nótese que se han quitado los comentarios para una mayour claridad.

  def delete_event(widget, event, data=None):
      print "delete event occurred"
      return gtk.FALSE

El método de retrollamada destroy() (líneas 27-28) hace que el programa termine mediante la lllamada a gtk.main_quit() . Esta función le dice a GTK que debe salir de la función gtk.main() cuando el control le sea transferido.

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

Las líneas 30-69 definen el método de inicialización de instancia __init__() del objeto HelloWorld, el cual crea la ventana y los controles que se usan en el programa.

La línea 32 crea una nueva ventana, pero no se muestra directamente hasta que le decimos a GTK que lo haga, casi al final del programa. La referencia a la ventana se guarda en un atributo de instancia (self.window) para poder acceder a ella después.

    self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)

Las líneas 39 y 44 ilustran dos ejemplos de cómo conectar un manejador de señal a un objeto, en este caso, a window. Aqui se capturan el evento "delete_event" y la señal "destroy". El primero se emite cuando cerramos la ventana a través del manejador de ventanas, o cuando usamos la llamada al método destroy() de GtkWidget. La segunda se emite cuando, en el manejador de "delete_event", devolvemos FALSE.

    self.window.connect("delete_event", self.delete_event)
    self.window.connect("destroy", self.destroy)

La línea 47 establece un atributo de un objeto contenedor (en este caso window) para que tenga un área vacía de 10 píxeles de ancho alrededor de él donde ningún control se situará. Hay otras funciones similares que se tratarán en la sección Poniendo Atributos de Controles

    self.window.set_border_width(10)

La línea 50 crea un nuevo botón y guarda una referencia a él en self.button. El botón tendrá la etiqueta "Hello World" cuando se muestre.

    self.button = gtk.Button("Hello World")

En la línea 55 conectamos un manejador de señal al botón para que cuando emita la señal "clicked", nuestro manejador de retrollamada hello() se llame. No estamos pasandole ningún dato a hello() asi que simplemente le pasamos None como dato. Obviamente la señal "clicked" se emite cuando hacemos clic en el botón con el cursor del ratón. El valor del parámetro de los datos None no es imprescindible y podría ser omitido. La retrollamada se llamará con un parámetro menos.

    self.button.connect("clicked", self.hello, None)

También vamos a usar este botón para salir de nuestro programa. La línea 60 muestra como la señal "destroy" puede venir del manejador de ventanas, o de nuestro programa. Cuando hacemos clic en el botón, al igual que antes, se llama a la retrollamada hello() primero, y después a la siguiente en el orden en el que son configuradas. Puedes tener todas las retrollamadas que necesistes y se ejecutarán en el orden en el que las conectaste.

Como queremos usar el método destroy() de la clase GtkWidget que acepta un argumento (el control que se va a destruir - en este caso window), utilizamos el método connect_object() y le pasamos la referencia a la ventana. El método connect_object() organiza el primer argumento de la retrollamada para que sea window en vez de el botón.

Cuando se llama el método destroy() de la clase GtkWidget esto provoca que se emita la señal "destroy" desde la ventana que a su vez provocará que se llame el método destroy() de la clase HelloWorld para terminar el programa.

    self.button.connect_object("clicked", gtk.Widget.destroy, self.window)

La línea 63 es una llamada de colocación, que será explicada en profundidad más tarde en Colocando Controles . Pero es bastante fácil de entender. Simplemente le dice a GTK que el botón debe situarse en la ventana donde se mostrará. Ten en cuenta que un contenedor GTK sólo puede contener un control. Hay otros controles, que se describen después, que están diseñados para posicionar varios controles de diferentes maneras.

    self.window.add(self.button)

Ahora lo tenemos todo configurado como queremos. Con todos los manejadores de señales, y el botón situado en la ventana donde debería estar, le pedimos a GTK (líneas 65 y 69) que muestre los controles en la pantalla. El control de ventana es mostrado en último lugar para que la ventana entera aparezca de una vez en vez de ver aparecer la ventana, y luego el botón dentro de ella. Sin embargo, con este ejemplo tan simple, nunca apreciarias la diferencia.

    self.button.show()

    self.window.show()

Las líneas 71-74 definen el método main() que llamam a la función gtk.main()

    def main(self):
        gtk.main()

Las líneas 78-80 permiten al programa ejecutarse automáticamente si es llamado directamente o como un argumento al intérprete de python. La línea 79 crea una instancia de la clase HelloWorld y guarda una referencia a ella en la variable hello. La línea 80 llama al método main() de la clase HelloWorld para empezar el bucle de procesamiento de eventos GTK.

    if __name__ == "__main__":
        hello = HelloWorld()
        hello.main()

Ahora, cuando hagamos clic con el botón del ratón en el botón GTK, el control emitirá una señal "clicked". Para poder usar esta información, nuestro programa configura un manejador de señal para que capture esta señal, la cual llama a la función que decidamos. En nuestro ejemplo, cuando el botón que hemos creado es pulsado, se llama el método hello() con un argumento None, y después se llama el siguiente manejador para esta señal. El siguiente manejador llama a la función destroy() del control con la ventana como su argumento y de esta manera causa a la ventana que emita la señal "destroy", que es capturada, y llama a nuestro método destroy() de la clase HelloWorld

Otra función de los eventos es usar el manejador de ventanas para matar la ventana, lo cual causará que se emita "delete_event". Esto llamará nuestro manejador de "delete_event". Si devolvemos TRUE aqui, la ventana se quedará como si nada hubiera pasado. Devolviendo FALSE hará que GTK emita la señal "destroy" que llama a la retrollamada "destroy" de la clase HelloWorld cerrando GTK.