2.4. Hola Mundo Paso a Paso

Ahora que ya conocemos la teoría general, vamos a aclarar el programa de ejemplo helloworld.py paso a paso.

Las líneas 9-76 definen la clase HelloWorld, que contiene todas las retrollamadas como métodos de objeto y el método de inicialización de objetos. Examinemos los métodos de retrollamada:

Las líneas 13-14 definen el método de retrollamada hello() que será llamado al pulsar el botón. Cuando se llama a este método, se imprime "Hello World" en la consola. En el 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 predeterminado igual a None porque PyGTK no pasará ningún valor para los datos si no son incluidos en la llamada a connect(); esto produciría un error ya que la retrollamada espera tres argumentos y únicamente recibe dos. La definición de 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 qué botón fue pulsado.

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

La siguiente retrollamada (líneas 16-26) 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 se devuelva en esta retrollamada le permite a GTK+ saber qué acción realizar. Si devolvemos TRUE hacemos saber que no queremos que se emita la señal "destroy", y así nuestra aplicación sigue ejecutándose. Si devolvemos 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 mayor claridad.

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

El método de retrollamada destroy() (líneas 29-30) hace que el programa termine mediante la lllamada a gtk.main_quit(). Esta función indica 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 32-71 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 34 crea una nueva ventana, pero no se muestra directamente hasta que comunicamos 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 41 y 46 ilustran dos ejemplos de cómo conectar un manejador de señal a un objeto, en este caso a window. Aquí se captura 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 gtk.Widget. 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 49 establece un atributo de un objeto contenedor (en este caso window) para que tenga un área vacía de 10 píxeles de ancho a su alrededor en donde no se sitúe ningún control. Hay otras funciones similares que se tratarán en la sección Establecimiento de Atributos de Controles

    self.window.set_border_width(10)

La línea 52 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 57 conectamos un manejador de señal al botón, de forma que, cuando emita la señal "clicked", se llame a nuestro manejador de retrollamada hello(). No pasamos ningún dato a hello() así que simplemente se entrega None como dato. Obviamente, la señal "clicked" se emite al hacer 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 omitirse. 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 del programa. La línea 62 muestra cómo la señal "destroy" puede venir del manejador de ventanas, o de nuestro programa. Al hacer clic en el botón, al igual que antes, se llama primero a la retrollamada hello(), y después a la siguiente en el orden en el que han sido configuradas. Se pueden tener todas las retrollamadas que sean necesarias, y se ejecutarán en el orden en el que se hayan conectado.

Como queremos usar el método destroy() de la clase gtk.Widget 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 del botón.

Cuando se llama el método destroy() de la clase gtk.Widget se emite la señal "destroy" desde la ventana, lo que a su vez provocará la llamada al método destroy() de la clase HelloWorld que termina el programa.

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

La línea 65 es una llamada de colocación, que se explicará en profundidad más tarde, en Colocación de Controles , aunque es bastante fácil de entender. Simplemente indica a GTK+ que el botón debe situarse en la ventana en donde se va a mostrar. Ha de tenerse en cuenta que un contenedor GTK+ únicamente puede contener un control. Otros controles, descritos más adelante, están diseñados para posicionar varios controles de otras 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, pedimos a GTK (líneas 66 y 69) que muestre los controles en pantalla. El control de ventana se muestra en último lugar, para que la ventana entera aparezca de una vez y no primero la ventana y luego el botón dentro de ella dibujándose. Sin embargo, con un ejemplo tan simple, sería difícil apreciar la diferencia.

    self.button.show()

    self.window.show()

Las líneas 73-75 definen el método main() que llama a la función gtk.main()

    def main(self):
        gtk.main()

Las líneas 80-82 permiten al programa ejecutarse automáticamente si es llamado directamente o como argumento del intérprete de python. La línea 81 crea una instancia de la clase HelloWorld y guarda una referencia a ella en la variable hello. La línea 82 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 que capture esta señal, la cual llama a la función que decidamos. En nuestro ejemplo, cuando se pulsa el botón que hemos creado, 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 que la ventana emita la señal "destroy", que es capturada y llama al método destroy() de la clase HelloWorld

Otra función de los eventos es usar el manejador de ventanas para eliminar la ventana, lo que causará que se emita "delete_event". Esto llamará a nuestro manejador de "delete_event". Si devolvemos TRUE aquí, 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+.