Hoy voy a explicar como dar los primeros pasos con Python y GTK+. Ésto surge a raíz de la escasez de un tutorial de pygtk en español, y con la esperanza de que a alguien le pueda servir. Cabe destacar que no soy un experto en PyGTK ni nada de eso, es mas, estoy aprendiendo mientras escribo esta entrada. Así que, por favor haganme saber sobre cualquier error que haya.

Antes que nada, cabe destacar que para crear nuestra interfaz de usuario (GUI), utilizaremos una herramienta llamada Glade. Lo que hace esta herramienta, es crearnos un archivo XML donde estará almacenada toda la información necesaria respecto a la disposición de los elementos en nuestra pantalla, así como también las señales que emiten (mas sobre esto luego) y ciertas propiedades que especificaran el funcionamiento de nuestra interfaz. Luego, desde nuestro código en Python, cargamos este archivo XML, hacemos todas las modificaciones necesarias (como mostrar datos, conectar señales, etc.) y ya estamos listos para presentar nuestra interfaz.

Vamos a necesitar tener instaladas los siguientes paquetes:

  • Python (>= 2.4)
  • Python GTK (>= 2.10)
  • Python Glade (>= 2.10)
  • Glade (>= 3)

En Ubuntu, podremos instalarlos con un simple:

$ sudo aptitude install python-glade2 glade-3

Si, con eso será suficiente, ya que los otros paquetes son dependencias de este. Si usan otra distribución de linux, busquen en su gestor de paquetes por algún nombre similar. Si usan el sistema de Redmond, les voy a hacer un favor y invitándolos a hacer click aquí, y luego de haber recibido e instalado su CD prosigan con el tutorial (hablo enserio!).

Ya todo preparado, procedemos a abrir Glade 3:

http://farm3.static.flickr.com/2397/2471595695_aba3705ea1.jpg

Ahora vamos a crear nuestro Hola Mundo: En el panel de la izquierda, seleccionamos. Toplevels > Window, luego en el panel Properties a la derecha, dentro del tab General, ponemos en Window Title, Hola Mundo, en Name ponemos winMain y dentro del tab Common, ponemos YES en Visible. Por último, guardamos el archivo como hola.glade.

Ya tenemos lista nuestra interfaz, ahora procedemos al codigo Python para utilizarla. Para esto, creamos un archivo llamado hola.py con el siguiente contenido:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import gtk
import gtk.glade

class HolaMundoGTK(object):
    def __init__(self):
        # cargamos el archivo glade
        self.w_tree = gtk.glade.XML('hola.glade')
        self.winMain = self.w_tree.get_widget('winMain')
        self.winMain.connect('destroy', gtk.main_quit)

if __name__ == '__main__':
    app = HolaMundoGTK()
    gtk.main()

Bien, como verán eso es bastante simple, pero hay mucho para aprender en estas lineas. Primero, importamos los módulos necesarios para toda aplicación que utilize las librerías PyGTK y Glade.

Luego, definimos una clase que representará nuestra aplicación. En el método __init__ de esta clase, cargamos nuestro archivo XML, y lo asignamos al atributo w_tree. Este atriubto -- llamado así por Widget Tree -- es, como su nombre en ingles lo indica, un árbol de widgets. Que es un widget? Un widget es cada uno de los componentes que forman nuestra GUI: por ejemplo, en la siguiente linea, solicitamos el widget llamado winMain a nuestro árbol de widgets, y lo almacenamos como el atributo window. Aquellos grandes observadores, habrán notado que winMain es el nombre por defecto que tenia nuestra ventana cuando la creamos en Glade. Luego, conectamos el evento llamado destroy emitido por el widget self.window con la función gtk.main_quit, en otras palabras, solicitamos que cuando intentemos cerrar la ventana mediante el botón X o similar, terminemos con la aplicación GTK por completo.

Por último creamos una instancia de la aplicación. Con esto logramos crear nuestra interfaz, pero no seremos capaces de verla hasta que GTK entre en su Loop principal.

GTK, al igual que muchas otros GUI Toolkits trabaja con un sistema de eventos o señales. Volviendo al caso del evento llamado destroy, esto significa que, por ejemplo: cuando hacemos click en el boton X para cerrar la aplicación, no llamamos directamente a nuestra función gtk.main_quit, sino, que conectamos esta función a la señal destroy como signal handler. Entonces, la próxima vez que se emita esta señal, se ejecutaran todos los signal handlers conectados a la misma. El encargado de que estas señales sean emitidas, es el loop principal de GTK, el cual ejecutamos cuando llamamos a gtk.main. A partir de ese momento, la aplicación se bloqueará hasta que solicitemos salir del loop.

Ya con estos conceptos incorporados, nos sera mas fácil el viaje a través de PyGTK. Ejecutamos nuestro programa normalmente:

$ python hola.py

Y si hemos seguido las instrucciones correctamente, obtendríamos algo así:

http://farm4.static.flickr.com/3136/2472586894_c0d4b74c47_o.png

Ahora vamos a hacer algo mas interesante, pero antes, necesitamos saber como funciona la disposición de widgets en GTK. En GTK, algunos widgets son contenedores, es decir, son capaces de contener a otros widgets dentro de si mismos, los dos contenedores mas comunes (y los que utilizaremos en este momento) son HBox y VBox.

Volvamos a Glade, seleccionemos Horizontal Box en el panel de la izquierda, luego hacemos click sobre nuestro widget winMain (es decir, el cuadrado con rayitas que vemos en el centro de la ventana), y seleccionamos 2 items en vez de los 3 que nos propone por defecto. Nos debería quedar algo así:

http://farm3.static.flickr.com/2224/2471880157_23d33f12d8.jpg

Bien, como verán, a simple vista tenemos dos "huecos" donde albergar nuestros widgets. La acción de colocar un widget dentro de otro widget se llama packing , y entre otras cosas, cuando queremos hacer packing, podemos seleccionar la posición o "hueco" donde queremos hacerlo. Procederemos ahora a colocar un Vertical box con 2 items en el espacio de la derecha y un Text Entry con nombre (Name) eNombre en la izquierda. Nos deberá quedar algo como esto:

http://farm4.static.flickr.com/3111/2472189957_70a2a68e23_o.png

Ahora agregaremos dos Buttons (botones), en la parte superior derecha de nuestra ventana: al primero, mediante el panel de propiedades a la derecha en el tab General, le daremos como nombre (Name): btnSaludar, y como etiqueta (Label): Saludar. Al segundo, le ponemos como nombre: btnLimpiar y como etiqueta: Limpiar. Deberíamos tener algo así:

http://farm4.static.flickr.com/3060/2472203295_ac903c4794.jpg

Ahora guardamos, y procedemos a ejecutar nuevamente el programa. Como verán, ya podemos tener una idea de lo que será nuestra interfaz en tiempo de ejecución. Pero por ahora, lo único "útil" que podemos hacer con este programa, es cerrarlo.

http://farm3.static.flickr.com/2115/2473050298_4bf17b6d16_o.png

Nuestra aplicación será muy simple, queremos que cuando presionemos en el botón Saludar, una ventana emergente "salude" a la persona cuyo nombre estará escrito en el campo de texto que hemos agregado, y si presionamos Limpiar, el contenido de este campo de texto deberá ser borrado.

Comenzaremos por el botón Limpiar, antes que nada recordemos, el nombre de nuestro botón es btnLimpiar, y el de nuestro campo de texto: eNombre.

Volvamos a Glade, seleccionemos nuestro botón Limpiar, y luego en el panel de la derecha, en el tab Signals agregamos una nueva señal llamada on_btnLimpiar_clicked en el evento clicked del objeto GtkButton como se muestra a continuación.

http://farm3.static.flickr.com/2261/2473127168_20e806a93d_o.png

Ahora en nuestro código Python debemos conectar a esta señal, un signal handler que se encargue de borrar el texto de eNombre. Modifiquemos nuestro método __init__ para que luzca de esta manera:

1
2
3
4
5
6
7
8
def __init__(self):
    # cargamos el archivo glade
    self.w_tree = gtk.glade.XML('hola.glade')
    self.winMain = self.w_tree.get_widget('winMain')
    self.eNombre = self.w_tree.get_widget('eNombre')
    self.w_tree.signal_autoconnect({
        'on_winMain_destroy': gtk.main_quit,
        'on_btnLimpiar_clicked': self.btnLimpiar_clicked })

Primeramente, del mismo modo que antes hemos hecho con winMain, asignamos al attributo eNombre nuestro widget llamado eNombre. Luego, mediante self.w_tree.signal_autoconnect hemos conectado la señal on_btnLimpiar_clicked con el signal handler self.btnLimpiar_clicked detallado a continuación:

1
2
def btnLimpiar_clicked(self, widget):
    self.eNombre.set_text('')

No tiene tanto secreto este signal handler, simplemente recibimos como argumento el widget sobre el cual se ejecuto el evento que disparó la señal, y luego hacemos nuestro trabajo: en este caso, para "limpiar" el contenido de nuestro campo de texto, la solución es asignarle un string vacío como contenido mediante el método set_text de nuestro objeto self.eNombre, instancia de la clase gtk.Entry. Para mas información sobre esto, ver la Referencia de PyGTK.

También hemos aprovechado y reemplazado el previo método para conectarse con gtk.main_quit por uno mas "bonito", pero para que esto funcione, necesitamos -- desde Glade -- configurar la emisión de la señal on_winMain_destroy para el evento destroy del objeto GtkObject en nuestro widget llamado winMain:

http://farm3.static.flickr.com/2267/2473257312_b208b1567c_o.png

Hecho esto, procederemos a ejecutar nuestro programa y... voila! Funciona! Solo resta trabajar con el boton Saludar. Primero, agregamos como ya lo hemos hecho previamente la señal on_btnSaludar_clicked al evento clicked del objeto GtkButton en nuestro widget llamado btnSaludar, y luego, conectamos desde Python esta señal con el signal handler btnSaludar_clicked (método de nuestra clase HolaMundoGTK) detallado a continuación:

1
2
3
4
5
6
def btnSaludar_clicked(self, widget):
    msg = 'Hola %s, bienvenido a PyGTK!' % self.eNombre.get_text()
    dlg = gtk.MessageDialog(self.winMain, buttons=gtk.BUTTONS_CLOSE,
            message_format=msg)
    dlg.run()
    dlg.destroy()

En este signal handler, obtenemos el valor actual de nuestro campo de texto self.eNombre y generamos un mensaje que luego será mostrado en nuestra ventana de dialogo (MessageDialog) dlg, luego, ejecutamos el loop de este widget, que funciona de la misma manera que el Loop Principal de GTK -- es decir, se ejecutará hasta que se solicite salir de el mediante algún evento. Y luego, destruimos (dlg.destroy()) nuestro MessageDialog. Para mas información sobre esto, ver la Referencia de PyGTK.

Ya podemos ejecutar y probar nuestra primera aplicación en PyGTK.

http://farm3.static.flickr.com/2323/2472489051_d4b6634b4c_o.png

Por ultimo, podemos modificar varios detalles de la apariencia y packing de nuestros widgets mediante el panel de propiedades en Glade. Por ejemplo, aquí cambiamos el tipo de los botones a Stock, agregamos un ícono para la aplicación, y modificamos el packing de nuestro campo de texto para alinearlo en la parte superior:

http://farm3.static.flickr.com/2275/2473334808_6be3bec1c8_o.png

Aquí el código completo de nuestro archivo hola.py para nuestra primera aplicación en PyGTK:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/usr/bin/env python
import gtk
import gtk.glade

class HolaMundoGTK(object):
    def __init__(self):
        # cargamos el archivo glade
        self.w_tree = gtk.glade.XML('hola.glade')
        self.winMain = self.w_tree.get_widget('winMain')
        self.eNombre = self.w_tree.get_widget('eNombre')
        self.w_tree.signal_autoconnect({
            'on_winMain_destroy': gtk.main_quit,
            'on_btnLimpiar_clicked': self.btnLimpiar_clicked,
            'on_btnSaludar_clicked': self.btnSaludar_clicked })

    def btnLimpiar_clicked(self, widget):
        self.eNombre.set_text('')

    def btnSaludar_clicked(self, widget):
        msg = 'Hola %s, bienvenido a PyGTK!' % self.eNombre.get_text()
        dlg = gtk.MessageDialog(self.winMain, buttons=gtk.BUTTONS_CLOSE,
                message_format=msg)
        dlg.run()
        dlg.destroy()


if __name__ == '__main__':
    app = HolaMundoGTK()
    gtk.main()

Y aquí el contenido de nuestro archivo hola.glade:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.2 on Wed May  7 05:06:57 2008 -->
<glade-interface>
  <widget class="GtkWindow" id="winMain">
    <property name="visible">True</property>
    <property name="title" translatable="yes">Hola Mundo</property>
    <property name="icon_name">evolution</property>
    <signal name="destroy" handler="on_winMain_destroy"/>
    <child>
      <widget class="GtkHBox" id="hbox1">
        <property name="visible">True</property>
        <child>
          <widget class="GtkVBox" id="vbox2">
            <property name="visible">True</property>
            <child>
              <widget class="GtkEntry" id="eNombre">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="activates_default">True</property>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="padding">3</property>
              </packing>
            </child>
            <child>
              <placeholder/>
            </child>
          </widget>
          <packing>
            <property name="padding">3</property>
          </packing>
        </child>
        <child>
          <widget class="GtkVBox" id="vbox1">
            <property name="visible">True</property>
            <property name="spacing">2</property>
            <child>
              <widget class="GtkButton" id="btnSaludar">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="label" translatable="yes">gtk-ok</property>
                <property name="use_stock">True</property>
                <property name="response_id">0</property>
                <signal name="clicked" handler="on_btnSaludar_clicked"/>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
              </packing>
            </child>
            <child>
              <widget class="GtkButton" id="btnLimpiar">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="label" translatable="yes">gtk-clear</property>
                <property name="use_stock">True</property>
                <property name="response_id">0</property>
                <signal name="clicked" handler="on_btnLimpiar_clicked"/>
              </widget>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">1</property>
              </packing>
            </child>
          </widget>
          <packing>
            <property name="position">1</property>
          </packing>
        </child>
      </widget>
    </child>
  </widget>
</glade-interface>

Y con esto termina nuestro primer tutorial, gracias a Cristian Boujon por la inspiración, y a esta persona por haber escrito un tutorial similar a este (pero en inglés) con el cual aprendí.

Posted by k0001 under Programming. Created on May 6, 2008 18:25 Last modified on Apr 19, 2009 21:46

Language: en Comments: 5 Pingbacks: 1 Tags: free-software, gtk, python

Pingbacks

  • ...Estando en la lista de correo de PyAr (python Argentina) encontre este tutorial de pyGTK en español realizado por Renzo Carbonara. Ejemplos buenos de manuales y/o tutoriales en nuestro idioma no son muchos, por eso les comento este....