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