Python y GTK
Python y GTK
Python y GTK
3
John Finlay
Rafael Villar Burke
Lorenzo Gil Snchez
Iigo Serna
Fernando San Martn Woerner
Resumen
Este tutorial describe el uso del mdulo de Python PyGTK.
Tabla de contenidos
1. Introduccin
1.1. Exploracin de PyGTK
2. Primeros Pasos
2.1. Hola Mundo en PyGTK
2.2. Teora de Seales y Retrollamadas
2.3. Eventos
2.4. Hola Mundo Paso a Paso
3. Avanzando
3.1. Ms sobre manejadores de seales
3.2. Un Hola Mundo Mejorado
4. Empaquetado de Controles
4.1. Teora de Cajas Empaquetadoras
4.2. Las Cajas en detalle
4.3. Programa de Ejemplo de Empaquetado
4.4. Uso de Tablas para el Empaquetado
4.5. Ejemplo de Empaquetado con Tablas
5. Perspectiva General de Controles
5.1. Jerarqua de Controles
5.2. Controles sin Ventana
6. El Control de Botn
6.1. Botones Normales
6.2. Botones Biestado (Toggle Buttons)
6.3. Botones de Activacin (Check Buttons)
6.4. Botones de Exclusin Mtua (Radio Buttons)
7. Ajustes
7.1. Creacin de un Ajuste
7.2. Utilizacin de Ajustes de la Forma Fcil
7.3. Interioridades de un Ajuste
8. Controles de Rango
8.1. Barras de Desplazamiento
8.2. Controles de Escala
8.2.1. Creacin de un Control de Escala
8.2.2. Mtodos y Seales (bueno, al menos mtodos)
8.3. Mtodos Comunes de los Rangos
8.3.1. Establecimiento de la Poltica de Actualizacin
8.3.2. Obtencin y Cambio de Ajustes
8.4. Atajos de Teclas y Ratn
8.5. Ejemplo de Control de Rango
9. Miscelnea de Controles
9.1. Etiquetas
9.2. Flechas (Arrow)
9.3. El Objeto Pistas (Tooltip)
9.4. Barras de Progreso (ProgressBar)
9.5. Dilogos
9.6. Imgenes
9.6.1. Pixmaps
9.7. Reglas
9.8. Barras de Estado
9.9. Entradas de Texto (Entry)
9.10. Botones Aumentar/Disminuir
9.11. Lista Desplegable (Combo)
9.12. Calendario
9.13. Seleccin de Color
9.14. Selectores de Fichero
9.15. Dilogo de Seleccin de Fuentes
10. Controles Contenedores
10.1. La Caja de Eventos (EventBox)
10.2. El control Alineador
10.3. Contenedor Fijo (Fixed)
10.4. Contenedor de Disposicin (Layout)
10.5. Marcos (Frame)
10.6. Marcos Proporcionales (AspectFrame)
10.7. Controles de Panel (HPaned y VPaned)
10.8. Vistas (Viewport)
10.9. Ventanas de Desplazamiento (ScrolledWindow)
10.10. Cajas de Botones (ButtonBoxes)
10.11. Barra de Herramientas (Toolbar)
10.12. Fichas (Notebook)
10.13. Elementos incrustables y puntos de conexin (Plugs y Sockets)
10.13.1. Elementos incrustables (Plugs)
10.13.2. Puntos de Conexin (Sockets)
11. Control Men
11.1. Creacin Manual de Mens
11.2. Ejemplo de Men Manual
11.3. Uso de la Factoria de Elementos
11.4. Ejemplo de Factoria de Elementos - ItemFactory
12. rea de Dibujo
12.1. Contexto Grfico
12.2. Mtodos de Dibujo
13. Control de Vista de Texto
13.1. Perspectiva general de la Vista de Texto
13.2. Vistas de Texto
13.3. Buffers de Texto
13.3.1. Informacin de estado de un Buffer de Texto
13.3.2. Creacin de Iteradores de Texto
13.3.3. Insercin, Obtencin y Eliminacin de Texto
13.3.4. Marcas de Texto (TextMark)
13.3.5. Creacin y Uso de Etiquetas de Texto
13.3.6. Insercin de Imgenes y Controles
13.4. Iteradores de Texto
13.4.1. Atributos de los Iteradores de Texto
13.4.2. Atributos de Texto de un Iterador de Texto
13.4.3. Copiar un Iterador de Texto
13.4.4. Recuperar Texto y Objetos
13.4.5. Comprobar Condiciones en un Iterador de Texto
13.4.6. Comprobar la posicin en un Texto
13.4.7. Movimiento a travs del Texto
Lista de figuras
2.1. Ventana Simple PyGTK
2.2. Programa de ejemplo: Hola Mundo
3.1. Ejemplo mejorado de Hola Mundo
4.1. Empaquetado: Cinco variaciones
4.2. Empaquetado con Spacing y Padding
4.3. Empaquetado con pack_end()
4.4. Empaquetado haciendo uso de una Tabla
6.1. Botn con Pixmap y Etiqueta
6.2. Ejemplo de Botn Biestado
6.3. Ejemplo de Botn de Activacin
6.4. Ejemplo de Botones de Exclusin Mtua
8.1. Ejemplo de Controles de Rango
9.1. Ejemplos de Etiquetas
9.2. Ejemplos de Botones con Flechas
9.3. Ejemplo de Pistas
9.4. Ejemplo de Barra de Progreso
9.5. Ejemplo de Imgenes en Botones
9.6. Ejemplo de Pixmap en un Botn
9.7. Ejemplo de Ventana con Forma
9.8. Ejemplo de Reglas
9.9. Ejemplo de Barra de Estado
9.10. Ejemplo de Entrada
9.11. Ejemplo de Botn Aumentar/Disminuir
9.12. Ejemplo de Calendario
9.13. Ejemplo de Dilogo de Seleccin de Color
9.14. Ejemplo de Seleccin de Ficheros
9.15. Dilogo de Seleccin de Fuentes
10.1. Ejemplo de Caja de Eventos
10.2. Ejemplo de Fijo
10.3. Ejemplo de Disposicin
10.4. Ejemplo de Marco
10.5. Ejemplo de Marco Proporcional
10.6. Ejemplo de Panel
10.7. Ejemplo de Ventana de Desplazamiento
10.8. Ejemplo de Barra de Herramientas
10.9. Ejemplo de Fichas
11.1. Ejemplo de Men
11.2. Ejemplo de Factoria de Elementos
12.1. Ejemplo de rea de Dibujo
13.1. Ejemplo bsico de Vista de Texto
13.2. Ejemplo de Vista de Texto
14.1. Programa elemental de ejemplo de TreeView
14.2. TreeViewColumns con CellRenderers
14.3. Funcin de Datos de Celda
14.4. Ejemplo de Listado de Archivos Utilizando Funciones de Datos de Celda
14.5. Etiquetas de Marcado para CellRendererText
14.6. Celdas Editables y Activables
14.7. Flecha de Expansin en la segunda Columna
14.8. Ejemplo de Arrastrar y Soltar en TreeView
14.9. Ejemplo de TreeModelSort
14.10. Ejemplo de Visibilidad en TreeModelFilter
14.11. Programa de Ejemplo de Modelo de rbol Genrico
15.1. Programa de ejemplo de Portapapeles
16.1. Ejemplo Simple de Accin
16.2. Ejemplo Bsico de Accin
16.3. Ejemplo de Acciones
16.4. Ejemplo de ActionGroup
Captulo 1. Introduccin
Tabla de contenidos
1.1. Exploracin de PyGTK
PyGTK 2.0 es un conjunto de mdulos que componen una interfaz Python para GTK+ 2.0. En el resto de este
documento cuando se menciona PyGTK se trata de la versin 2.0 o posterior de PyGTK, y en el caso de GTK+, tambin
a su versin 2.0 y siguientes. El sitio web de referencia sobre PyGTK es www.pygtk.org. El autor principal de PyGTK
es:
que es ayudado por los desarrolladores citados en el archivo AUTHORS de la distribucin PyGTK y por la comunidad
PyGTK.
Python es un lenguaje de programacin interpretado, ampliable y orientado a objetos que se distribuye con un amplio
conjunto de mdulos que permiten el acceso a un gran nmero de servicios del sistema operativo, servicios de internet
(como HTML, XML, FTP, etc.), grficos (incluidos OpenGL, TK, etc.), funciones de manejo de cadenas, servicios de
correo (IMAP, SMTP, POP3, etc.), multimedia (audio, JPEG) y servicios de criptografa. Existen adems multitud de
mdulos proporcionados por terceros que aaden otros servicios. Python se distribuye bajo trminos similares a los de la
licencia GPL y est disponible para los sistemas operativos Linux, Unix, Windows y Macintosh. En www.python.org
hay ms informacion disponible sobre Python. Su autor principal es:
GTK+ (GIMP Toolkit) es una librera que permite crear interfaces grficas de usuario. Se distribuye bajo la licencia
LGPL, por lo que posibilita el desarrollo de software abierto, software libre, e incluso software comercial no libre que
use GTK sin necesidad de pagar licencias o derechos.
Se le conoce como el toolkit de GIMP porque originalmente se escribi para desarrollar el Programa de Manipulacin de
Imgenes de GNU GIMP, pero GTK+ se usa ya en numerosos proyectos de software, includo el proyecto de escritorio
GNOME (Entorno de Modelo de Objetos orientados a Red). GTK+ est diseada sobre GDK (Kit de Dibujo de GIMP)
que, bsicamente, es una abstraccin de las funciones de bajo nivel que acceden al sistema de ventanas (Xlib en el caso
del sistema de ventanas X). Los principales autores de GTK+ son:
GTK+ es fundamentalmente un interfaz orientada a objetos para programadores de aplicaciones (API). Aunque est
escrita completamente en C, est implementada usando la idea de clases y funciones de retrollamada (punteros a
funcin).
Existe un tercer componente, llamado Glib, que contiene diversas funciones que reemplazan algunas llamadas estandard,
as como funciones adicionales para manejar listas enlazadas, etc. Las funciones de reemplazo se usan para aumentar la
portabilidad de GTK+ ya que algunas de las funciones que implementa no estn disponibles o no son estndar en otros
UNIX, tales como g_strerror(). Otras incluyen mejoras a las versiones de libc, tales como g_malloc, que posee
capacidades de depuracin mejoradas.
Desde su versin 2.0, GLib incluye el sistema de tipos que forma la base de la jerarqua de clases de GTK+, el sistema
de seales usado en sta, una API de hebras que abstrae las diferentes APIs nativas para programacin multihilo en las
diversas plataformas, y la capacidad de cargar mdulos.
Como ltimo componente, GTK+ usa la librera Pango para la salida de texto internacionalizado.
Este tutorial describe la interfaz de Python con GTK+ y est basado en el tutorial de GTK+ 2.0 escrito por Tony Gale e
Ian Main. En l se intenta documentar en la medida posible todo PyGTK, pero en ningn caso es completo.
Este tutorial presupone algn conocimiento previo de Python, as de cmo se crean y ejecutan programas escritos en
Python. Si no se est familiarizado con Python, es recomendable previamente la lectura del Tutorial de Python. Este
tutorial no presupone ningn conocimiento previo sobre GTK+ y si se utiliza PyGTK para aprender GTK+ sera
interesante recibir comentarios acerca de este tutorial, y qu aspectos resultan problemticos. Este tutorial no describe
cmo compilar o instalar Python, GTK+ o PyGTK.
Este tutorial est basado en:
Hola Mundo!
>>> b.set_label("Hola a todos")
>>>
En este ejemplo se crea una ventana que contiene un botn que imprime un mensaje ('Hola Mundo!') cuando se hace
clic en l. El programa permite probar as fcilmente los diversos controles de GTK+ y sus interfaces PyGTK.
Tambin es til el programa desarrollado por Brian McErlean para la receta de Activestate 65109 junto con algunas
modificaciones para que funcione con PyGTK 2.X. En este curso lo llamamos gpython.py y funciona de forma parecida
al programa pygtkconsole.py.
Nota
Estos dos programas no funcionan en Microsoft Windows porque necesitan funciones especficas de
Unix.
Captulo 2. Primeros Pasos
Tabla de contenidos
2.1. Hola Mundo en PyGTK
2.2. Teora de Seales y Retrollamadas
2.3. Eventos
2.4. Hola Mundo Paso a Paso
Para empezar nuestra introduccin a PyGTK, comenzaremos con el programa ms simple posible. Este programa
(base.py) crear una ventana de 200x200 pxeles y no es posible salir de l excepto terminando el proceso desde la
consola.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/env python
# example base.py
import pygtk
pygtk.require('2.0')
import gtk
class Base:
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.show()
def main(self):
gtk.main()
print __name__
if __name__ == "__main__":
base = Base()
base.main()
10
En este caso, la lnea 1 pedir al intrprete de Python que ejecute base.py. Las lneas 5-6 ayudan a diferenciar entre las
distintas versiones de PyGTK que puedan estar instaladas en el equipo. Estas lneas indican que se desea usar la versin
2.0 de PyGTK, que comprende todas las versiones de PyGTK con 2 como nmero principal. Ello impide que el
programa utilice versiones anteriores de PyGTK, en caso de que se encuentren instaladas en el sistema. Las lneas 18-20
comprueban si la variable __name__ es "__main__", lo cual indica que el programa est siendo ejecutado
directamente por python y no est siendo importado en un intrprete Python. En el primer caso el programa crea una
nueva instancia de la clase Base y guarda una referencia a ella en la variable base. Despus llama la funcin main()
para iniciar el bucle de procesamiento de eventos de GTK.
Una ventana similar a Figura 2.1, Ventana Simple PyGTK debera aparecer en tu pantalla.
Figura 2.1. Ventana Simple PyGTK
La primera lnea permite al programa base.py ser invocado desde una consola Linux o Unix asumiendo que python se
encuentre en el PATH. Esta lnea aparecer como primera lnea en todos los programas de ejemplo.
Las lneas 5-7 importan el mdulo PyGTK 2 e inicializan el entorno GTK+. El mdulo PyGTK define las interfaces
Python de las funciones GTK+ que se usarn en el programa. Para quienes estn familiarizados con GTK+ hay que
advertir que la inicializacin incluye la llamada a la funcin gtk_init(). Tambin se configuran algunas cosas por
nosotros, tales como el visual por defecto, el mapa de colores, manejadores de seales predeterminados. Asimismo
comprueba los argumentos que se pasan al programa desde la lnea de comandos, en busca de alguno entre:
--gtk-module
--g-fatal-warnings
--gtk-debug
--gtk-no-debug
--gdk-debug
--gdk-no-debug
--display
--sync
--name
--class
En este caso, los borra de la lista de argumentos y deja los no coincidentes que no reconoce para que el programa lo
procese o ignore. El anterior conjunto de argumentos son los que aceptan de forma estndar todos los programas GTK+.
Las lneas 9-15 definen una clase de Python llamada Base que define un mtodo de inicializacin de instancia
__init__(). La funcin __init__() crea una ventana de nivel superior (lnea 11) y ordena a GTK+ que la muestre
(lnea 12). La gtk.Window se crea en la lnea 11 con el argumento gtk.WINDOW_TOPLEVEL que indica que se
desea una ventana sometida a las decoraciones y posicionamiento del manejador de ventanas. En vez de crear una
ventana de tamao 0x0, una ventana sin hijos tiene un tamao predeterminado de 200x200 de forma que se pueda
manipular.
11
Las lneas 14-15 definen el mtodo main() que llama a la funcin PyGTK main(), que invoca el bucle principal de
procesamiento de eventos de GTK+ para manejar eventos de ratn y de teclado, as como eventos de ventana.
Las lneas 18-20 permiten al programa comenzar automticamente si es llamado directamente o pasado como argumento
al intrprete de Python. En estos casos, el nombre de programa que hay en la variable __name__ ser la cadena
"__main__" y el cdigo entre las lneas 18-20 se ejecutar. Si el programa se carga en un intrprete de Python en
ejecucin, las lneas 18-20 no sern ejecutadas.
La lnea 19 crea una instancia de la clase Base llamada base. Crea una gtk.Window y la muestra como resultado.
La lnea 20 llama al mtodo main() de la clase Base, la cual comienza el bucle de procesamiento de eventos de GTK+.
Cuando el control llega a este punto, GTK+ se dormir a la espera de eventos de las X (como pulsaciones de teclas o
botones), alarmas, o notificaciones de entrada/salida de ficheros. En el ejemplo, sin embargo, los eventos son ignorados.
2.1. Hola Mundo en PyGTK
Ahora seguimos con un programa con un control (un botn). Es la versin PyGTK del clsico programa hola mundo
(helloworld.py ).
1
#!/usr/bin/env python
2
3
# ejemplo helloworld.py
4
5
import pygtk
6
pygtk.require('2.0')
7
import gtk
8
9
class HelloWorld:
10
11
# Esta es una funcin de retrollamada. Se ignoran los argumentos de
12
# datos en este ejemplo. Ms sobre retrollamadas ms abajo.
13
def hello(self, widget, data=None):
14
print "Hello World"
15
16
def delete_event(self, widget, event, data=None):
17
# Si se devuelve FALSE en el gestor de la seal "delete_event",
18
# GTK emitir la seal "destroy". La devolucin de TRUE
19
# significa que no se desea la destruccin de la ventana.
20
# Esto sirve para presentar dilogos como: 'Est seguro de que
21
# desea salir?'
22
print "delete event occurred"
23
24
# Si se cambia FALSE a TRUE la ventana principal no se
25
# destruir con "delete_event".
26
return gtk.FALSE
27
28
# Otra retrollamada
29
def destroy(self, widget, data=None):
30
gtk.main_quit()
31
32
def __init__(self):
33
# se crea una ventana nueva
34
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
35
36
# Cuando se enva a una ventana la seal "delete_event" (esto lo
hace
37
# geralmente el gestor de ventanas, usualmente con "cerrar", o
con el icono
12
38
# de la ventana de ttulo), pedimos que llame la funcin
delete_event ()
39
# definida arriba. Los datos pasados a la retrollamada son
40
# NULL y se ignoran en la funcin de retrollamada.
41
self.window.connect("delete_event", self.delete_event)
42
43
# Conectamos el evento "destroy" a un manejador de seal.
44
# Este evento sucede cuando llamamos gtk_widget_destroy() para
la ventana,
45
# o si devolvemos FALSE en la retrollamada "delete_event".
46
self.window.connect("destroy", self.destroy)
47
48
# Establece el grosor del borde de la ventana.
49
self.window.set_border_width(10)
50
51
# Crea un nuevo botn con la etiqueta "Hello World".
52
self.button = gtk.Button("Hello World")
53
54
# Cuando el botn recibe la seal "clicked", llamar la
55
# funcin hello() a la que pasa None como argumento. La funcin
hello()
56
# se define ms arriba.
57
self.button.connect("clicked", self.hello, None)
58
59
# Esto causar la destruccin de la ventana al llamar a
60
# gtk_widget_destroy(window) cuando se produzca "clicked". De
nuevo,
61
# la seal podra venir de aqu o del gestor de ventanas.
62
self.button.connect_object("clicked", gtk.Widget.destroy,
self.window)
63
64
# Esto empaqueta el botn en la ventana (un contenedor de GTK+).
65
self.window.add(self.button)
66
67
# El paso final es mostrar el control recin creado.
68
self.button.show()
69
70
# y la ventana
71
self.window.show()
72
73
def main(self):
74
# Todas las aplicaciones de PyGTK deben tener una llamada a
gtk.main(). Aqu se deja
75
# el control y se espera que suceda un evento (como un evento de
teclado o ratn).
76
gtk.main()
77
78
# Si el programa se ejecuta directamente o se pasa como argumento al
intrprete
79
# de Python, entonces se crea una instancia de HelloWorld y se muestra
80
if __name__ == "__main__":
81
hello = HelloWorld()
82
hello.main()
Figura 2.2, Programa de ejemplo: Hola Mundo muestra la ventana creada por helloworld.py.
Figura 2.2. Programa de ejemplo: Hola Mundo
13
Las variables y funciones que se definen en el mdulo PyGTK se llaman de la forma gtk.*. Por ejemplo, el programa
helloworld.py usa:
gtk.FALSE
gtk.mainquit()
gtk.Window()
gtk.Button()
del mdulo PyGTK. En futuras secciones no se especificar el prefijo del mdulo gtk, pero se dar por asumido.
Naturalmente, los programas de ejemplo usarn los prefijos del mdulo.
2.2. Teora de Seales y Retrollamadas
Nota
En la versin 2.0 de GTK+, el sistema de seales se ha movido de GTK+ a GLib. No entraremos en
detalles sobre las extensiones que GLib 2.0 tiene en relacin con el sistema de seales de GTK 1.2. Las
diferecias no deberan notarse en el uso de PyGTK.
Antes de entrar en detalle en helloworld.py, discutiremos las seales y las retrollamadas. GTK+ es una biblioteca
orientada a eventos, lo que significa que se dormir en la funcin gtk.main() hasta que un evento ocurra y el control
pase a la funcin apropiada.
Esta delegacin del control se realiza usando la idea de "seales". (Ntese que estas seales no son las mismas que las
seales de los sistemas Unix, y no se implementan usando stas, aunque la terminologa es casi idntica) Cuando ocurre
un evento, como cuando presionamos un botn del ratn, la seal apropiada se "emite" por el el control que fu
presionado. As es cmo GTK+ hace la mayora de su trabajo til. Hay seales que todos los controles heredan, como
"destroy", y hay seales que son especficas de cada control, como "toggled" en el caso de un botn de activacin.
Para hacer que un botn realice una accin, debemos configurar un manejador de seales que capture estas seales y
llame a la funcin apropiada. Esto se hace usando un mtodo de gtk.Widget (heredado de la clase GObject) como
por ejemplo:
handler_id = object.connect(name, func, func_data)
donde object es la instancia de gtk.Widget (un control) que estar emitiendo la seal, y el primer argumento
name es una cadena que contiene el nombre de la seal que se desea capturar. El segundo argumento, func, es la
funcin que se quiere llamar cuando se produce el evento. El tercer argumento, func_data, son los datos que se
desean pasar a la funcin func. El mtodo devuelve un handler_id que se puede usar para desconectar o bloquear el uso
del manejador.
La funcin especificada en el tercer argumento se llama "funcin de retrollamada", y generalmente tiene la forma:
def callback_func(widget, callback_data):
donde el primer argumento ser una referencia al widget (control) que emiti la seal, y el segundo
(callback_data) una referencia a los datos dados como ltimo argumento en el mtodo connect() mostrado antes.
Si la funcin de retrollamada es un mtodo de un objeto entonces tendr la forma general siguiente:
def callback_meth(self, widget, callback_data):
14
donde self es la instancia del objeto que invoca este mtodo. Esta es la forma usada en el programa de ejemplo
helloworld.py.
Nota
La forma anterior de declaracin de una funcin de retrollamada a seales es slo una gua general, ya
que las seales especficas de los distintos controles generan diferentes parmetros de llamada.
Otra llamada que se usa en el ejemplo helloworld.py es:
handler_id = object.connect_object(name, func, slot_object)
connect_object() es idntica a connect(), exceptuando que una funcin de retrollamada slo usa un argumento, y
un mtodo de retrollamada, dos argumentos:
def callback_func(object)
def callback_meth(self, object)
donde object normalmente es un control. connect_object() permite usar los mtodos de controles PyGTK qu
solo admiten un argumento (self) como manejadores de seales.
2.3. Eventos
Adems del mecanismo de seales descrito anteriormente, hay un conjunto de eventos que reflejan el mecanismo de
eventos de X. Las retrollamadas tambin se pueden conectar a estos eventos. Estos eventos son:
event
button_press_event
button_release_event
scroll_event
motion_notify_event
delete_event
destroy_event
expose_event
key_press_event
key_release_event
enter_notify_event
leave_notify_event
configure_event
focus_in_event
focus_out_event
map_event
unmap_event
property_notify_event
selection_clear_event
selection_request_event
selection_notify_event
proximity_in_event
proximity_out_event
visibility_notify_event
client_event
no_expose_event
window_state_event
Para conectar una funcin de retrollamada a uno de estos eventos se usa el mtodo connect(), como se ha dicho
anteriormente, usando uno de los nombres de eventos anteriores en el parmetro name. La funcin (o mtodo) de
retrollamada para eventos es ligeramente diferente de la usada para seales:
15
16
Las APIs de seleccin y arrastrar y soltar de GDK tambin emiten unos cuantos eventos que se reflejan en GTK+ por
medio de seales. Consulta Seales en el Control de Orgen y Seales en el Control de Destino para obtener ms detalles
sobre la sintaxis de las funciones de retrollamada para estas seales:
selection_received
selection_get
drag_begin_event
drag_end_event
drag_data_delete
drag_motion
drag_drop
drag_data_get
drag_data_received
17
La lnea 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 despus.
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
Las lneas 41 y 46 ilustran dos ejemplos de cmo conectar un manejador de seal a un objeto, en este caso a window.
Aqu se captura el evento "delete_event" y la seal "destroy". El primero se emite cuando cerramos la ventana a travs
del manejador de ventanas o cuando usamos la llamada al mtodo 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 lnea 49 establece un atributo de un objeto contenedor (en este caso window) para que tenga un rea vaca de 10
pxeles de ancho a su alrededor en donde no se site ningn control. Hay otras funciones similares que se tratarn en la
seccin Establecimiento de Atributos de Controles
self.window.set_border_width(10)
La lnea 52 crea un nuevo botn y guarda una referencia a l en self.button. El botn tendr la etiqueta "Hello
World" cuando se muestre.
self.button = gtk.Button("Hello World")
En la lnea 57 conectamos un manejador de seal al botn, de forma que, cuando emita la seal "clicked", se llame a
nuestro manejador de retrollamada hello(). No pasamos ningn dato a hello() as que simplemente se entrega None
como dato. Obviamente, la seal "clicked" se emite al hacer clic en el botn con el cursor del ratn. El valor del
parmetro de los datos None no es imprescindible y podra omitirse. La retrollamada se llamar con un parmetro
menos.
self.button.connect("clicked", self.hello, None)
Tambin vamos a usar este botn para salir del programa. La lnea 62 muestra cmo la seal "destroy" puede venir del
manejador de ventanas, o de nuestro programa. Al hacer clic en el botn, al igual que antes, se llama primero a la
retrollamada hello(), y despus a la siguiente en el orden en el que han sido configuradas. Se pueden tener todas las
retrollamadas que sean necesarias, y se ejecutarn en el orden en el que se hayan conectado.
Como queremos usar el mtodo destroy() de la clase gtk.Widget que acepta un argumento (el control que se va a
destruir - en este caso window), utilizamos el mtodo connect_object() y le pasamos la referencia a la ventana. El
mtodo connect_object() organiza el primer argumento de la retrollamada para que sea window en vez del botn.
Cuando se llama el mtodo destroy() de la clase gtk.Widget se emite la seal "destroy" desde la ventana, lo que a
su vez provocar la llamada al mtodo destroy() de la clase HelloWorld que termina el programa.
self.button.connect_object("clicked", gtk.Widget.destroy, self.window)
La lnea 65 es una llamada de colocacin, que se explicar en profundidad ms tarde, en Colocacin de Controles ,
aunque es bastante fcil de entender. Simplemente indica a GTK+ que el botn 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 ms adelante, estn diseados para posicionar varios controles de otras maneras.
self.window.add(self.button)
Ahora lo tenemos todo configurado como queremos. Con todos los manejadores de seales, y el botn situado en la
ventana donde debera estar, pedimos a GTK (lneas 66 y 69) que muestre los controles en pantalla. El control de
18
ventana se muestra en ltimo lugar, para que la ventana entera aparezca de una vez y no primero la ventana y luego el
botn dentro de ella dibujndose. Sin embargo, con un ejemplo tan simple, sera difcil apreciar la diferencia.
self.button.show()
self.window.show()
Las lneas 73-75 definen el mtodo main() que llama a la funcin gtk.main()
def main(self):
gtk.main()
Las lneas 80-82 permiten al programa ejecutarse automticamente si es llamado directamente o como argumento del
intrprete de python. La lnea 81 crea una instancia de la clase HelloWorld y guarda una referencia a ella en la
variable hello. La lnea 82 llama al mtodo 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 botn del ratn en el botn GTK, el control emitir una seal "clicked". Para poder
usar esta informacin, nuestro programa configura un manejador de seal que capture esta seal, la cual llama a la
funcin que decidamos. En nuestro ejemplo, cuando se pulsa el botn que hemos creado, se llama el mtodo hello()
con un argumento None, y despus se llama el siguiente manejador para esta seal. El siguiente manejador llama a la
funcin destroy() del control con la ventana como su argumento y de esta manera causa que la ventana emita la seal
"destroy", que es capturada y llama al mtodo destroy() de la clase HelloWorld
Otra funcin 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 seal "destroy", que llama a la retrollamada
"destroy" de la clase HelloWorld cerrando GTK+.
Captulo 3. Avanzando
Tabla de contenidos
3.1. Ms sobre manejadores de seales
3.2. Un Hola Mundo Mejorado
3.1. Ms sobre manejadores de seales
Veamos otra vez la llamada a connect() .
object.connect(name, func, func_data)
El valor de retorno de connect() es un nmero entero que identifica la retrollamada. Como ya se ha mencionado, es
posible disponer de tantas retrollamadas por seal como sea necesario, y cada una de ellas se ejecutar por turnos, en el
mismo orden de conexin.
Este identificador permite eliminar la retrollamada de la lista de retrollamadas activas mediante el mtodo:
object.disconnect(id)
As, pasando el identificador devuelto por los mtodos de conexin, es posible desconectar un manejador de seal.
19
Tambin es posible deshabilitar temporalmente un manejador de seal mediante los mtodos handler_block() y
handler_unblock().
object.handler_block(handler_id)
object.handler_unblock(handler_id)
3.2. Un Hola Mundo Mejorado
1
#!/usr/bin/env python
2
3
# Ejemplo helloworld2.py
4
5
import pygtk
6
pygtk.require('2.0')
7
import gtk
8
9
class HelloWorld2:
10
11
# La retrollamada mejorada. Los datos que se pasan a esta funcin
12
# se imprimen por la salida estndar.
13
def callback(self, widget, data):
14
print "Hello again - %s was pressed" % data
15
16
# otra retrollamada
17
def delete_event(self, widget, event, data=None):
18
gtk.main_quit()
19
return gtk.FALSE
20
21
def __init__(self):
22
# Creamos una ventana
23
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
24
25
# Esta llamada establece el ttulo de la
26
# ventana como "Hello Buttons!"
27
self.window.set_title("Hello Buttons!")
28
29
# Aqu establecemos un manejador para delete_event que
30
# sale inmediatamente de GTK+.
31
self.window.connect("delete_event", self.delete_event)
32
33
# Establece el grosor del borde de la ventana
34
self.window.set_border_width(10)
35
36
# Creamos una caja en la que empaquetar los controles. Esto se
describe detalladamente
37
# en la seccin de "empaquetado". La caja no es visible en
realidad sino que simplemente
38
# facilita la organizacin de los controles.
39
self.box1 = gtk.HBox(gtk.FALSE, 0)
40
41
# Introducimos la caja en la ventana principal
42
self.window.add(self.box1)
43
44
# Crea un nuevo botn con la etiqueta "Button 1".
45
self.button1 = gtk.Button("Button 1")
46
47
# Ahora, cuando se pulsa el botn, llamamos al mtodo "callback"
48
# con un puntero a "button 1" como argumento
49
self.button1.connect("clicked", self.callback, "button 1")
20
50
51
# En vez de usar add(), empaquetamos este botn en la caja
visible
52
# que ha sido introducida en la ventana.
53
self.box1.pack_start(self.button1, gtk.TRUE, gtk.TRUE, 0)
54
55
# Hay que recordar siempre este paso, que indica a GTK+ que la
preparacin del
56
# botn ha terminado y que ya es posible mostrarlo.
57
self.button1.show()
58
59
# Seguimos los mismos pasos para crear el segundo botn
60
self.button2 = gtk.Button("Button 2")
61
62
# Llamamos la misma retrollamada pero con un argumento
diferente,
63
# haciendo referencia a "button 2" esta vez.
64
self.button2.connect("clicked", self.callback, "button 2")
65
66
self.box1.pack_start(self.button2, gtk.TRUE, gtk.TRUE, 0)
67
68
# El orden en que mostramos los botones no es muy importante,
pero es recomendable
69
# mostrar la ventana en ltimo lugar, puesto que as aparece
todo de una vez.
70
self.button2.show()
71
self.box1.show()
72
self.window.show()
73
74
def main():
75
gtk.main()
76
77
if __name__ == "__main__":
78
hello = HelloWorld2()
79
main()
Al ejecutar helloworld2.py se genera la ventana de la Figura 3.1, Ejemplo mejorado de Hola Mundo.
Figura 3.1. Ejemplo mejorado de Hola Mundo
Esta vez se puede ver que no hay forma fcil de salir del programa, y resulta necesario usar el gestor de ventanas o la
lnea de comandos para eliminarlo. Un buen ejercicio para el lector sera insertar un tercer botn "Salir" que cerrara el
programa. Sera interesante jugar con las opciones de pack_start() al tiempo que se lee la siguiente seccin, as como
probar a cambiar de tamao la ventana y observar qu sucede.
Como nota, hay que mencionar otra constante til para gtk.Window() - WINDOW_DIALOG. Este tipo de ventana
interacta de forma distinta con el gestor de ventanas y debe usarse en ventanas de uso transitorio.
A continuacin se describen en orden las pequeas diferencias del cdigo respecto a la versin inicial del programa
"Hola Mundo":
Como ya se ha dicho, no existe manejador del evento "destroy" en esta versin mejorada de "Hola Mundo".
21
Las lneas 13-14 definen un mtodo de retrollamada similar a la retrollamada hello() del ejemplo inicial. La diferencia
reside en que ahora la retrollamada imprime un mensaje que incluye los datos que se le suministran.
La lnea 27 pone ttulo a la barra de ttulo de la ventana (vase la Figura 3.1, Ejemplo mejorado de Hola Mundo).
La lnea 39 crea una caja horizontal (gtk.HBox) que almacena los dos botones que se crean en las lneas 45 y 60. La
lnea 42 aade la caja horizontal al contenedor de la ventana.
Las lneas 49 y 64 conectan el mtodo callback() a la seal "clicked" de los botones. Y cada botn establece una
cadena diferente que se pasa al mtodo callback() al ser invocado.
Las lneas 53 y 66 empaquetan los botones en la caja horizontal. Y, finalmente, las lneas 57 y 70 indican a GTK+ que
muestre los botones.
Las lneas 71-72 piden finalmente a GTK+ que muestre la caja y la ventana.
Captulo 4. Empaquetado de Controles
Tabla de contenidos
4.1. Teora de Cajas Empaquetadoras
4.2. Las Cajas en detalle
4.3. Programa de Ejemplo de Empaquetado
4.4. Uso de Tablas para el Empaquetado
4.5. Ejemplo de Empaquetado con Tablas
Normalmente, cuando se crea un programa, se desea poner ms de un control en la ventana. Nuestro primer ejemplo
"Hola Mundo" usaba un nico control para poder llamar simplemente al mtodo add() de la clase gtk.Container
para "empaquetar" el control en la ventana. Sin embargo, en cuanto se quiera poner ms de un control en una ventana,
cmo se determina la posicin en la que se sita el control?. Aqu es donde el "empaquetado" de controles entra en
juego.
4.1. Teora de Cajas Empaquetadoras
La mayora del empaquetado se realiza utilizando cajas. stas son contenedores invisibles de controles y son de dos
tipos: cajas horizontales y cajas verticales. En el primer tipo los los objetos se insertan horizontalmente, de izquierda a
derecha o de derecha a izquierda, en funcin de la llamada que se use; mientras que en el segundo tipo, las cajas
verticales, los controles se empaquetan de arriba a abajo o viceversa. Es posible utilizar combinaciones de cajas
insertadas en otras cajas y obtener cualquier efecto que se desee.
Para crear una nueva caja horizontal se usa la llamada gtk.HBox(), y con cajas verticales gtk.VBox() . Los mtodos
pack_start() y pack_end() se utilizan para colocar los objetos dentro de estos contenedores. El primer mtodo,
pack_start(), inserta los objetos yendo de arriba hacia abajo en una caja vertical, y de izquierda a derecha en una
caja horizontal. El mtodo pack_end() muestra el comportamiento opuesto, empaqueta de abajo hacia arriba en una
caja vertical, y de derecha a izquierda en una caja horizontal. Con estos mtodos se pueden alinear a la derecha o a la
izquierda los controles, de tal forma que se consiga el efecto buscado. A lo largo de los ejemplos de este tutorial se usar
fundamentalmente el mtodo pack_start(). Un objeto puede ser adems bien otro contenedor o bien un control. De
hecho, muchos controles son en realidad tambin contenedores, como ocurre con los botones, aunque normalmente se
use slo una etiqueta en su interior.
Con las llamadas anteriores se indica a GTK+ cmo ha de situar los controles y as es capaz de cambiar su tamao y
otras propiedades interesantes de forma automtica. Y, como es de esperar, dicho mtodo proporciona adems gran
flexibilidad a la hora de situar y crear controles.
4.2. Las Cajas en detalle
22
A causa de esta flexibilidad, el empaquetado de cajas puede resultar confuso al principio, dado que admite muchas
opciones cuyo funcionamiento conjunto no resulta obvio. Sin embargo, existen bsicamente cinco estilos. La Figura 4.1,
Empaquetado: Cinco variaciones muestra el resultado de la ejecucin del programa packbox.py con un argumento de
1:
Figura 4.1. Empaquetado: Cinco variaciones
Cada lnea contiene una caja horizontal (hbox) con varios botones. La llamada a pack es una copia de la llamada a pack
en cada uno de los botones de la Hbox. Cada botn se empaqueta en la hbox de la misma manera (con los mismos
argumentos al mtodo pack_start() ).
Este es un ejemplo del mtodo pack_start():
box.pack_start(child, expand, fill, padding)
box es la caja donde se empaqueta el objeto. El primer argumento, child, es el objeto que se va a empaquetar. Por
ahora los objetos sern botones, con lo que estaramos empaquetando botones dentro de cajas.
El argumento expand de pack_start() y pack_end() controla si los controles se disponen de forma que ocupen
todo el espacio extra de la caja y, de esta manera, sta se expande hasta ocupar todo el rea reservada para ella (TRUE);
o si se encoge para ocupar el espacio justo de los controles (FALSE). Poner expand a FALSE permite justificar a la
derecha y a la izquierda los controles. Si no, se expandirn para llenar la caja, y el mismo efecto podra obtenerse usando
slo o pack_start() o pack_end().
El argumento fill controla si el espacio extra se utiliza en los propios objetos (TRUE) o como espacio extra en la caja
alrededor de los objetos (FALSE). Slo tiene efecto si el argumento expand tambin es TRUE.
Python permite definir un mtodo o funcin con valores de argumento predeterminados y argumentos con nombre. A lo
largo de este tutorial se ver la definicin de las funciones y mtodos con valores predeterminados y argumentos con
nombre cuando sean de aplicacin. Por ejemplo, el mtodo pack_start se define as:
box.pack_start(child, expand=gtk.TRUE, fill=gtk.TRUE, padding=0)
box.pack_end(child, expand=gtk.TRUE, fill=gtk.TRUE, padding=0)
child, expand, fill y padding son palabras clave (argumentos con nombre). Los argumentos expand, fill y
padding tienen los valores predeterminados (o "por defecto") mostrados arriba. El argumento child debe
especificarse obligatoriamente al no tener un valor predeterminado.
Las funciones que nos permiten crear una caja nueva son:
23
La Figura 4.3, Empaquetado con pack_end() ilustra el uso del mtodo pack_end() (pasa un argumento de 3 a
packbox.py). La etiqueta "end" se empaqueta con el mtodo pack_end(). Se mantendr en el borde derecho de la
ventana cuando sta se redimensione.
Figura 4.3. Empaquetado con pack_end()
#!/usr/bin/env python
# ejemplo packbox.py
import pygtk
24
6 pygtk.require('2.0')
7 import gtk
8 import sys, string
9
10 # Funcin auxiliar que crea una nueva HBox llena de botones con etiqueta.
Los argumentos
11 # de las variables en las que tenemos inters se pasan a esta funcin. No
mostramos la
12 # caja pero s todo lo que contiene.
13
14 def make_box(homogeneous, spacing, expand, fill, padding):
15
16
# Creamos una nueva HBox con los parmetros homogeneous
17
# y spacing adecuados.
18
box = gtk.HBox(homogeneous, spacing)
19
20
# Creamos una serie de botones con los parmetros adecuados
21
button = gtk.Button("box.pack")
22
box.pack_start(button, expand, fill, padding)
23
button.show()
24
25
button = gtk.Button("(button,")
26
box.pack_start(button, expand, fill, padding)
27
button.show()
28
29
# Creamos un botn con una etiqueta que depende del valor de
30
# expand.
31
if expand == gtk.TRUE:
32
button = gtk.Button("TRUE,")
33
else:
34
button = gtk.Button("FALSE,")
35
36
box.pack_start(button, expand, fill, padding)
37
button.show()
38
39
# Aqu hacemos lo mismo que en la creacin del botn de "expand"
40
# anterior, pero usa la forma abreviada.
41
button = gtk.Button(("FALSE,", "TRUE,")[fill==gtk.TRUE])
42
box.pack_start(button, expand, fill, padding)
43
button.show()
44
45
padstr = "%d)" % padding
46
47
button = gtk.Button(padstr)
48
box.pack_start(button, expand, fill, padding)
49
button.show()
50
return box
51
52 class PackBox1:
53
def delete_event(self, widget, event, data=None):
54
gtk.main_quit()
55
return gtk.FALSE
56
57
def __init__(self, which):
58
59
# Creamos nuestra ventana
60
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
61
62
# Siempre debemos recordar la conexin de la seal delete_event
25
63
# a la ventana principal. Esto es muy importante de cara a un
comportamiento
64
# intuitivo adecuado
65
self.window.connect("delete_event", self.delete_event)
66
self.window.set_border_width(10)
67
68
# Creamos una caja vertical (vbox) en la que empaquetar las cajas
horizontales.
69
# Esto nos permite apilar las cajas horizontales llenas de
botones
70
# una encima de otra en esta vbox.
71
box1 = gtk.VBox(gtk.FALSE, 0)
72
73
# qu ejemplo mostramos. stos se corresponden a las imgenes
anteriores.
74
if which == 1:
75
# creamos una etiqueta nueva.
76
label = gtk.Label("HBox(FALSE, 0)")
77
78
# Alineamos la etiqueta al lado izquierdo. Comentaremos este
y otros
79
# mtodos en la seccin sobre Atributos de los Controles.
80
label.set_alignment(0, 0)
81
82
# Empaquetamos la etiqueta en la caja vertical (vbox box1).
Recurdese que
83
# los controles que se aaden a una caja vertical se apilan
uno encima del otro
84
# en orden.
85
box1.pack_start(label, gtk.FALSE, gtk.FALSE, 0)
86
87
# Mostramos la etiqueta
88
label.show()
89
90
# Llamamos a nuestra funcin de crear caja - homogeneous =
FALSE, spacing = 0,
91
# expand = FALSE, fill = FALSE, padding = 0
92
box2 = make_box(gtk.FALSE, 0, gtk.FALSE, gtk.FALSE, 0)
93
box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)
94
box2.show()
95
96
# Llamamos a nuestra funcin de crear caja - homogeneous =
FALSE, spacing = 0,
97
# expand = TRUE, fill = FALSE, padding = 0
98
box2 = make_box(gtk.FALSE, 0, gtk.TRUE, gtk.FALSE, 0)
99
box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)
100
box2.show()
101
102
# Los argumentos son: homogeneous, spacing, expand, fill,
padding
103
box2 = make_box(gtk.FALSE, 0, gtk.TRUE, gtk.TRUE, 0)
104
box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)
105
box2.show()
106
107
# Crea un separador, que veremos qu hacen ms adelante,
108
# aunque son muy simples.
109
separator = gtk.HSeparator()
110
111
# Empaquetamos el separador en la vbox. Recurdese que
empaquetamos todos estos
26
112
113
114
115
116
117
118
119
120
121
122
123
padding
124
125
126
127
128
padding
129
130
131
132
133
134
135
136
137
138
139
140
creada
141
142
143
144
145
146
147
padding
148
149
150
151
152
padding
153
154
155
156
157
158
159
160
161
162
163
164
165
166
27
167
168
# Los argumentos son: homogeneous, spacing, expand, fill,
padding
169
box2 = make_box(gtk.FALSE, 0, gtk.TRUE, gtk.FALSE, 10)
170
box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)
171
box2.show()
172
173
# Los argumentos son: homogeneous, spacing, expand, fill,
padding
174
box2 = make_box(gtk.FALSE, 0, gtk.TRUE, gtk.TRUE, 10)
175
box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)
176
box2.show()
177
178
separator = gtk.HSeparator()
179
# Los ltimos 3 argumentos de pack_start son:
180
# expand, fill, padding.
181
box1.pack_start(separator, gtk.FALSE, gtk.TRUE, 5)
182
separator.show()
183
184
elif which == 3:
185
186
# Esto ilustra la posibilidad de usar pack_end() para
187
# alinear los controles a la derecha. Primero creamos una
caja nueva, como antes.
188
box2 = make_box(gtk.FALSE, 0, gtk.FALSE, gtk.FALSE, 0)
189
190
# Creamos la etiqueta que pondremos al final.
191
label = gtk.Label("end")
192
# La empaquetamos con pack_end(), por lo que se pone en el
extremo derecho
193
# de la hbox creada en la llamada a make_box().
194
box2.pack_end(label, gtk.FALSE, gtk.FALSE, 0)
195
# Mostramos la etiqueta.
196
label.show()
197
198
# Empaquetamos la box2 en box1
199
box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)
200
box2.show()
201
202
# Un separador para la parte de abajo.
203
separator = gtk.HSeparator()
204
205
# Esto establece explcitamente el ancho del separador a 400
pxeles y 5
206
# pxeles de alto. As la hbox que creamos tambin tendra
400
207
# pxeles de ancho, y la etiqueta "end" estar separada de
las otras
208
# de la hbox. En otro caso, todos los controles de la
209
# hbox estaran empaquetados lo ms juntos posible.
210
separator.set_size_request(400, 5)
211
# empaquetamos el separador en la vbox (box1) creada cerca
del principio
212
# de __init__()
213
box1.pack_start(separator, gtk.FALSE, gtk.TRUE, 5)
214
separator.show()
215
216
# Creamos otra hbox nueva. Recordemos que podramos usar cuantas
queramos!
217
quitbox = gtk.HBox(gtk.FALSE, 0)
28
218
219
# Nuestro botn de salida.
220
button = gtk.Button("Quit")
221
222
# Configuramos la seal que finalice el programa al pulsar el
botn
223
button.connect("clicked", lambda w: gtk.main_quit())
224
# Empaquetamos el botn en la quitbox.
225
# Los 3 ltimos argumentos de pack_start son:
226
# expand, fill, padding.
227
quitbox.pack_start(button, gtk.TRUE, gtk.FALSE, 0)
228
# empaquetamos la quitbox en la vbox (box1)
229
box1.pack_start(quitbox, gtk.FALSE, gtk.FALSE, 0)
230
231
# Empaquetamos la vbox (box1), que ahora contiene todos los
controles,
232
# en la ventana principal.
233
self.window.add(box1)
234
235
# Y mostramos todo lo que queda
236
button.show()
237
quitbox.show()
238
239
box1.show()
240
# Mostrando la ventana al final de forma que todo aparezca de una
vez.
241
self.window.show()
242
243 def main():
244
# y, naturalmente, el bucle de eventos principal.
245
gtk.main()
246
# El control se devuelve a este punto cuando se llama a main_quit().
247
return 0
248
249 if __name__ =="__main__":
250
if len(sys.argv) != 2:
251
sys.stderr.write("usage: packbox.py num, where num is 1, 2, or
3.\n")
252
sys.exit(1)
253
PackBox1(string.atoi(sys.argv[1]))
254
main()
El pequeo tour por el cdigo packbox.py empieza por las lineas 14-50 que definen una funcin auxiliar make_box
que crea una caja horizontal y la rellena con botones segn los parmetros especficados. Devuelve una referencia a la
caja horizontal.
Las lineas 52-241 definen el mtodo de inicializacin __init__() de la clase PackBox1 que crea una ventana y una
caja vertical en ella que se rellena con una configuracin de controles que depende del argumento que recibe. Si se le
pasa un 1, las lineas 75-138 crean una ventana que muestra las cinco nicas posibilidades que resultan de variar los
parmetros homogeneous, expand y fill. Si se le pasa un 2, las lineas 140-182 crean una ventana que muesstra las
diferentes combinaciones de fill con spacing y padding. Finalmente, si se le pasa un 3, las lineas 188-214 crean una
ventana que muestra el uso del mtodo pack_start() para justificar los botones a la izquierda y el mtodo
pack_end() para justificar una etiqueta a la derecha. Las lineas 215-235 crean una caja horizontal que contiene un
botn que se empaqueta dentro de la caja vertical. La seal 'clicked' del botn est conectada a la funcin PyGTK
gtk.main_quit() para terminar el programa.
Las lineas 250-252 comprueban los argumentos de la lnea de comandos y terminan el programa usando la funcin
sys.exit() si no hay exactamente un argumento. La lnea 251 crea una instancia de PackBox1. La lnea 253 llama a
la funcin gtk.main() para empezar el bucle de procesamiento de eventos GTK+.
29
En este programa de ejemplo, las referencias a los controles (excepto a la ventana) no se guardan en los atributos de
instancia del objeto porque no se necesitan ms tarde.
4.4. Uso de Tablas para el Empaquetado
Veamos otra manera de empaquetado. Mediante tablas. Pueden ser extremadamente tiles en determinadas situaciones.
Al usar tablas, creamos una rejilla en donde podemos colocar los controles, que pueden ocupar tantos espacios como
especifiquemos.
Lo primero que debemos mirar es, obviamente, la funcin gtk.Table() :
table = gtk.Table(rows=1, columns=1, homogeneous=FALSE)
El primer argumento es el nmero de filas de la tabla, mientras que el segundo, obviamente, es el nmero de columnas.
El argumento homogeneous tiene que ver con el tamao de las celdas de la tabla. Si homogeneous es TRUE, las
celdas de la tabla tienen el tamao del mayor control en ella. Si homogeneous es FALSE, el tamao de las celdas
viene dado por el control ms alto de su misma fila, y el control ms ancho de su columna.
Las filas y las columnas se disponen de 0 a n, donde n es el nmero que se especific en la llamada a gtk.Table().
Por tanto, si se especifica rows (filas) = 2 y columns (columnas) = 2, la dispoisicin quedara as:
0
1
2
0+----------+----------+
|
|
|
1+----------+----------+
|
|
|
2+----------+----------+
Obsrvese que el sistema de coordenadas tiene su orgien en la esquina superior izquierda. Para introducir un control en
una caja, se usa el siguiente mtodo:
table.attach(child, left_attach, right_attach, top_attach, bottom_attach,
xoptions=EXPAND|FILL, yoptions=EXPAND|FILL, xpadding=0,
ypadding=0)
La instancia table es la tabla que se cre con gtk.Table(). El primer parmetro ("child") es el control que se desea
introducir en la tabla.
Los argumentos left_attach, right_attach, top_attach y bottom_attach especifican dnde situar el
control, y cuntas cajas usar. Si se quiere poner un botn en la esquina inferior derecha de una tabla 2x2, y se quiere que
ocupe SLO ese espacio, left_attach sera = 1, right_attach = 2, top_attach = 1, bottom_attach = 2.
Ahora, si se desea que un control ocupe la fila entera de la tabla 2x2, se pondra left_attach = 0, right_attach
= 2, top_attach = 0, bottom_attach = 1.
Los argumentos xoptions e yoptions se usan para especificar opciones de colocacin y pueden unirse mediante la
operacin OR, permitiendo as mltiples opciones.
Estas opciones son:
FILL
Si la caja es ms grande que el control, y se especifica FILL, el control se expandir hasta usar todo el espacio
disponible.
SHRINK
Si se le asigna menos espacio a la tabla del que solicit (normalmente porque el usuario ha redimensionado la
ventana), entonces los controles normalmente sera empujados a la parte inferior de la ventana y
30
#!/usr/bin/env python
# ejemplo table.py
import pygtk
pygtk.require('2.0')
import gtk
class Table:
# Nuestra retrollamada.
31
11
estndar.
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
tabla.
49
50
51
52
53
54
55
56
57
58
59
60
tabla.
61
62
63
64
65
66
67
32
68
# Cuando se pulsa el botn llamamos a la funcin main_quit y
69
# el programa se termina
70
button.connect("clicked", lambda w: gtk.main_quit())
71
72
# Insertamos el botn de salida en los dos cuadrantes inferiores
de la tabla
73
table.attach(button, 0, 2, 1, 2)
74
75
button.show()
76
77
table.show()
78
self.window.show()
79
80 def main():
81
gtk.main()
82
return 0
83
84 if __name__ == "__main__":
85
Table()
86
main()
La clase Table se define en las lneas 9-78. Las lneas 12-13 definen el mtodo callback() que se llama al hacer
"click" en dos de los botones. La retrollamada slo imprime un mensaje en la consola indicando qu botn se puls
usando los datos que se le pasan.
Las lineas 16-18 definen el mtodo delete_event() que se llama cuando el manejador de ventanas le pide a la
ventana que se elimine.
Las lneas 20-78 definen el constructor de la clase Table __init__(). Crea una ventana (lnea 22), le pone el ttulo
(lnea 25), conecta la retrollamada delete_event() a la seal "delete_event" (lnea 29), y le pone el ancho al borde
(lnea 32). Se crea una gtk.Table en la lnea 35 y se aade a la ventana en la lnea 38.
Los dos botones superiores se crean en las lneas 41 y 55, sus seales "clicked" se conectan al mtodo callback() en
las lneas 45 y 59; y se aaden a la tabla en la primera fila en las lneas 49 y 61. Las lneas 66-72 crean el botn "Quit",
conectan su seal "clicked" a la funcin mainquit() y lo aaden a la tabla ocupando la fila inferior completa.
Captulo 5. Perspectiva General de Controles
Tabla de contenidos
5.1. Jerarqua de Controles
5.2. Controles sin Ventana
Los pasos generales para usar un control (widget) en PyGTK son:
Se llama a gtk.* (una de las mltiples funciones para crear un nuevo control, y que se detallan en esta seccin.
Se conectan todas la seales y eventos que queramos usar a los manejadores apropiados.
Se establecen los atributos del control.
Se empaqueta el control dentro de un contenedor usando una llamada como gtk.Container.add() o
gtk.Box.pack_start().
Se llama a gtk.Widget.show() en el control.
show() le permite saber a GTK que hemos terminado de configurar los atributos del control, y est listo para ser
mostrado. Tambin se puede usar gtk.Widget.hide() para que desaparezca otra vez. El orden en el que se muestran
los controles no es importante, pero es conveniente que se muestre la ventana al final de modo que la toda la ventana
aparezca de una vez y no se vea como van apareciendo los controles individuales en la ventana a medida que se van
formando. Los hijos de un control (una ventana tambin es un control) no se mostrarn hasta que la propia ventana se
muestre usando el mtodo show() .
33
34
35
36
37
STOCK_GOTO_TOP
STOCK_GO_BACK
STOCK_GO_DOWN
STOCK_GO_FORWARD
STOCK_GO_UP
STOCK_HELP
STOCK_HOME
STOCK_INDEX
STOCK_ITALIC
STOCK_JUMP_TO
STOCK_JUSTIFY_CENTER
STOCK_JUSTIFY_FILL
STOCK_JUSTIFY_LEFT
STOCK_JUSTIFY_RIGHT
STOCK_MISSING_IMAGE
STOCK_NEW
STOCK_NO
STOCK_OK
STOCK_OPEN
STOCK_PASTE
STOCK_PREFERENCES
STOCK_PRINT
STOCK_PRINT_PREVIEW
STOCK_PROPERTIES
STOCK_QUIT
STOCK_REDO
STOCK_REFRESH
STOCK_REMOVE
STOCK_REVERT_TO_SAVED
STOCK_SAVE
STOCK_SAVE_AS
STOCK_SELECT_COLOR
STOCK_SELECT_FONT
STOCK_SORT_ASCENDING
STOCK_SORT_DESCENDING
STOCK_SPELL_CHECK
STOCK_STOP
STOCK_STRIKETHROUGH
STOCK_UNDELETE
STOCK_UNDERLINE
STOCK_UNDO
STOCK_YES
STOCK_ZOOM_100
STOCK_ZOOM_FIT
STOCK_ZOOM_IN
STOCK_ZOOM_OUT
El programa de ejemplo buttons.py proporciona un ejemplo del uso de gtk.Button() para crear un botn con una
imagen y una etiqueta en l. Se ha separado el cdigo para crear una caja del resto para que se pueda usar en ms
programas. Hay ms ejemplos del uso de imgenes ms adelante en el tutorial. La figura Figura 6.1, Botn con Pixmap
y Etiqueta muestra la ventana con un botn que incluye una imagen y una etiqueta:
Figura 6.1. Botn con Pixmap y Etiqueta
38
39
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
Las lneas 12-34 definen la funcin auxiliar xpm_label_box() que crea una caja horizontal con un borde de ancho 2
(lineas 14-15) y le pone una imagen (lineas 22-23) y una etiqueta (linea 26).
Las lneas 36-70 definen la clase Buttons. Las lneas 41-70 definen el mtodo de inicializacin de instancia que crea
una ventana (linea 43), le pone el ttulo (linea 45), le conecta las seales "delete_event" y "destroy" (lineas 48-49). La
linea 55 crea el botn sin etiqueta. Su seal "clicked" se conecta al mtodo callback() en la linea 58. La funcin
xpm_label_box() se llama en la linea 61 para crear la imagen y la etiqueta que se pondrn en el botn en la linea 64.
La funcin xpm_label_box() podra usarse para empaquetar archivos xpm y etiquetas en cualquier control que pueda
ser un contenedor.
El control Botn tiene las siguientes seales:
pressed - se emite cuando el botn del puntero se presiona en el control
Botn
released - se emite cuando el botn del puntero se suelta en el control
Botn
clicked - se emite cuando el botn del puntero se presiona y luego se
suelta sobre el control Botn
enter - se emite cuando el puntero entra en el control Botn
leave - se emite cuando el puntero sale del control Botn
6.2. Botones Biestado (Toggle Buttons)
Los Botones Biestado derivan de los botones normales y son muy similares, excepto que siempre estn en uno de dos
estados, alternndolos con un clic. Puedan estar presionados, y cuando se vuelva a hacer clic, volvern a su estado
inicial, levantados. Se hace clic otra vez y volvern a estar presionados.
Los botones Biestado son la base para los botones de activacin y los botones de exclusin mtua, y por ello, muchas de
las llamadas usadas con los botones biestado son heredados por los botones de activacin y los botones de exclusin
mtua. Volveremos a destacar este hecho cuando tratemos esos botones.
Creacin de un nuevo botn biestado:
toggle_button = gtk.ToggleButton(label=None)
40
Como se puede imaginar, estas llamadas funcionan igual que las llamadas al control de botn normal. Si no se especifica
etiqueta el botn estar vacio. El texto de la etiqueta se analiza para comprobar si contiene caracteres mnemotcnicos
con el prefijo '_'
Para obtener el estado de un botn biestado, incluyendo los botones de exclusin mtua y los botones de activacin, se
utiliza el mecanismo del ejemplo anterior. As se comprueba el estado del biestado, llamando al mtodo get_active()
del objeto botn biestado. La seal que nos interesa que emiten los botones biestado (el botn biestado, el botn de
activacin y el botn de exclusin mtua) es la seal "toggled". Para comprobar el estado de estos botones, se configura
un manejador de seales para capturar la seal toggled, y se accede a los atributos del objeto para determinar su estado.
La retrollamada ser parecida a:
def toggle_button_callback(widget, data):
if widget.get_active():
# Si estamos aqui, el botn biestado est pulsado
else:
# Si estamos aqui, el botn biestado est levantado
Para forzar el estado de un botn biestado, y de sus hijos, el botn de exclusin mtua y el botn de activacin, se utiliza
este mtodo:
toggle_button.set_active(is_active)
El mtodo anterior puede usarse para forzar el estado del botn biestado, y sus hijos los botones de activacin y de
exclusin mtua. Especificando un argumento TRUE o FALSE para el parmetro is_active indicamos si el botn
debera estar pulsado o levantado. Cuando el botn biestado se crea, su valor predeterminado es levantado o FALSE.
Hay que fijarse en que, cuando se usa el mtodo set_active(), y cambia realmente el estado del botn, se produce la
emisin de las seales "clicked" y "toggled" por ste.
toggle_button.get_active()
Este mtodo devuelve el estado actual del botn biestado como un valor booleano TRUE o FALSE.
El programa togglebutton.py proporciona un ejemplo simple del uso de botones biestado. La figura Figura 6.2,
Ejemplo de Botn Biestado ilustra la ventana resultante con el segundo botn biestado activo:
Figura 6.2. Ejemplo de Botn Biestado
#!/usr/bin/env python
# ejemplo togglebutton.py
import pygtk
pygtk.require('2.0')
import gtk
41
8
9 class ToggleButton:
10
# Nuestra retrollamada.
11
# Los datos pasados a este mtodo se imprimen a la salida estndar
12
def callback(self, widget, data=None):
13
print "%s was toggled %s" % (data, ("OFF",
"ON")[widget.get_active()])
14
15
# Esta retrollamada termina el programa
16
def delete_event(self, widget, event, data=None):
17
gtk.main_quit()
18
return gtk.FALSE
19
20
def __init__(self):
21
# Crear una nueva ventana
22
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
23
24
# Establece el ttulo de la ventana
25
self.window.set_title("Toggle Button")
26
27
# Set a handler for delete_event that immediately
28
# exits GTK.
29
self.window.connect("delete_event", self.delete_event)
30
31
# Establece el ancho del borde de la ventana
32
self.window.set_border_width(20)
33
34
# Crea una caja vertical
35
vbox = gtk.VBox(gtk.TRUE, 2)
36
37
# Inserta vbox en la ventana principal
38
self.window.add(vbox)
39
40
# Crea el primer botn
41
button = gtk.ToggleButton("toggle button 1")
42
43
# cuando se conmuta el botn llamamos el mtodo "callback"
44
# con un puntero a "button" como argumento
45
button.connect("toggled", self.callback, "toggle button 1")
46
47
48
# Insertar el botn 1
49
vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 2)
50
51
button.show()
52
53
# Crear el segundo botn
54
55
button = gtk.ToggleButton("toggle button 2")
56
57
# Cuando se conmuta el botn llamamos el mtodo "callback"
58
# con un puntero a "button 2" como argumento
59
button.connect("toggled", self.callback, "toggle button 2")
60
# Insertamos el botn 2
61
vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 2)
62
63
button.show()
64
65
# Crear el botn "Quit"
66
button = gtk.Button("Quit")
42
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
Las lineas interesantes son la 12-13, que definen el mtodo callback() que imprime la etiqueta del botn biestado y
su estado cuando es activado. Las lineas 45 y 59 conectan la seal "toggled" de los botones biestado al mtodo
callback().
6.3. Botones de Activacin (Check Buttons)
Los botones de activacin heredan muchas propiedades y mtodos de los botones biestado vistos anteriormente, pero su
apariencia es un poco diferente. En vez de ser botones con texto dentro de ellos, son pequeas cajas con un texto a su
derecha. Normalmente se utilizan para opciones que pueden estar activadas o desactivadas en las aplicaciones.
El mtodo de creacin es similar al de los botones normales.
check_button = gtk.CheckButton(label=None)
Si el argumento label se especifica, el mtodo crea un botn de activacin con una etiqueta a su lado. El texto label
de la etiqueta se analiza en busca de caracteres mnemotcnicos con prefijo '_'
Ver y modificar el estado de un botn de activacin es igual que en un botn biestado.
El programa checkbutton.py proporciona un ejemplo del uso de los botones de activacin. La figura Figura 6.3,
Ejemplo de Botn de Activacin ilustra la ventana resultante:
Figura 6.3. Ejemplo de Botn de Activacin
#!/usr/bin/env python
43
2
3 # ejemplo checkbutton.py
4
5 import pygtk
6 pygtk.require('2.0')
7 import gtk
8
9 class CheckButton:
10
# Nuestra retrollamada
11
# Los datos pasados a este mtodo se imprimen en la salida estndar
12
def callback(self, widget, data=None):
13
print "%s was toggled %s" % (data, ("OFF",
"ON")[widget.get_active()])
14
15
# Esta retrollamada termina el programa
16
def delete_event(self, widget, event, data=None):
17
gtk.main_quit()
18
return gtk.FALSE
19
20
def __init__(self):
21
# Crear una nueva ventana
22
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
23
24
# Establecer el ttulo de la ventana
25
self.window.set_title("Check Button")
26
27
# Fijar un manejador para delete_event que
28
# salga inmediatamente de GTK.
29
self.window.connect("delete_event", self.delete_event)
30
31
# Fijamos el borde de la ventana
32
self.window.set_border_width(20)
33
34
# Creamos una caja vertical vbox
35
vbox = gtk.VBox(gtk.TRUE, 2)
36
37
# Insertamos vbox en la ventana principal
38
self.window.add(vbox)
39
40
# Creamos el primer botn
41
button = gtk.CheckButton("check button 1")
42
43
# Cuando se conmuta el botn llamamos el mtodo "callback"
44
# con un puntero a "button" como argumento
45
button.connect("toggled", self.callback, "check button 1")
46
47
48
# Insertamos el botn 1
49
vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 2)
50
51
button.show()
52
53
# Creamos un segundo botn
54
55
button = gtk.CheckButton("check button 2")
56
57
# Cuando se conmuta el botn llamamos el mtodo "callback"
58
# con un puntero a "button 2" como argumento
59
button.connect("toggled", self.callback, "check button 2")
60
# Insertamos el botn 2
44
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
45
El programa de ejemplo radiobuttons.py crea un grupo de botones de exclusin mtua con tres botones. La figura
Figura 6.4, Ejemplo de Botones de Exclusin Mtua ilustra la ventana resultante:
Figura 6.4. Ejemplo de Botones de Exclusin Mtua
46
40
button.connect("toggled", self.callback, "radio button 2")
41
button.set_active(gtk.TRUE)
42
box2.pack_start(button, gtk.TRUE, gtk.TRUE, 0)
43
button.show()
44
45
button = gtk.RadioButton(button, "radio button3")
46
button.connect("toggled", self.callback, "radio button 3")
47
box2.pack_start(button, gtk.TRUE, gtk.TRUE, 0)
48
button.show()
49
50
separator = gtk.HSeparator()
51
box1.pack_start(separator, gtk.FALSE, gtk.TRUE, 0)
52
separator.show()
53
54
box2 = gtk.VBox(gtk.FALSE, 10)
55
box2.set_border_width(10)
56
box1.pack_start(box2, gtk.FALSE, gtk.TRUE, 0)
57
box2.show()
58
59
button = gtk.Button("close")
60
button.connect_object("clicked", self.close_application,
self.window,
61
None)
62
box2.pack_start(button, gtk.TRUE, gtk.TRUE, 0)
63
button.set_flags(gtk.CAN_DEFAULT)
64
button.grab_default()
65
button.show()
66
self.window.show()
67
68 def main():
69
gtk.main()
70
return 0
71
72 if __name__ == "__main__":
73
RadioButtons()
74
main()
El cdigo es bastante simple de seguir. Las lineas 63-64 hacen que el botn "close" sea el control por defecto, de manera
que, al pulsar la tecla "Enter" cuando la ventana est activa, el botn "close" emitir la seal "clicked".
Captulo 7. Ajustes
Tabla de contenidos
7.1. Creacin de un Ajuste
7.2. Utilizacin de Ajustes de la Forma Fcil
7.3. Interioridades de un Ajuste
GTK tiene varios controles que pueden ser ajustados visualmente por el usuario usando el ratn o el teclado, tales como
los controles de rango, descritos en la seccin Controles de Rango. Tambin hay unos cuantos controles que visualizan
una parte ajustable de un rea de datos mayor, tales como el control de texto y el control de vista.
Obviamente, una aplicacin necesita ser capaz de reaccionar ante los cambios que el usuario realiza en los controles de
rango. Una forma de hacer esto sera que cada control emitiera su propio tipo de seal cuando su ajuste cambiara y, o
bien pasar el nuevo valor al manejador de seal, o requerir que se mire dentro de la estructura de datos del control para
ver el nuevo valor. Pero puede que tambin se quiera conectar los ajustes de varios controles juntos, para que ajustando
uno se ajusten los otros. El ejemplo ms obvio de esto es conectar una barra de desplazamiento a una vista o a un rea de
texto desplazable. Si cada control tuviera su propia manera de manipular el valor del ajuste, entonces el programador
47
tendra que escribir sus propios manejadores de seales para traducir entre la salida de la seal de un control y la entrada
del mtodo de ajuste de otro control.
GTK soluciona este problema mediante el objeto Adjustment, que no es un control sino una manera de que los
controles almacenen y pasen la informacin de ajuste de una forma abstracta y flexible. El uso ms obvio de
Adjustment es almacenar los parmetros de configuracin y los valores de los controles de rango, tales como las
barras de desplazamiento y los controles de escala. Sin embargo, como la clase Adjustments deriva de Object,
tambin tiene unas caractersticas especiales ms alla de ser estructuras de datos normales. La ms importante es que
pueden emitir seales, como los controles, y estas seales no slo pueden ser usadas para permitir a tus programas
reaccionar a la entrada de usuario en controles ajustables, sino que pueden propagar valores de ajuste de una forma
transparente entre controles ajustables.
Se ver como encajan los ajustes en todo el sistema cuando se vean los controles que los incorporan: Barras de Progreso,
Vistas, Ventanas de Desplazamiento, y otros.
7.1. Creacin de un Ajuste
Muchos de los controles que usan ajustes lo hacen automticamente, pero ms tarde se mostrarn casos en los que puede
ser necesario crearlos. Es posible crear un ajuste usando:
adjustment = gtk.Adjustment(value=0, lower=0, upper=0, step_incr=0,
page_incr=0, page_size=0)
El argumento value es el valor inicial que se quiere dar al ajuste, y normalmente corresponde a la posicin superior o
la posicin ms a la izquierda de un control ajustable. El argumento lower especifica el valor ms bajo que puede
tomar el ajuste adjustment. El argumento step_incr especifica el incremento ms pequeo de los dos
incrementos por los que el usuario puede cambiar el valor, mientras que el argumento page_incr es el ms grande de
los dos. El argumento page_size normalmente se corresponde de alguna manera con el rea visible de un control
desplazable. El argumento upper se usa para representar la coordenada inferior o la ms a la derecha de un control
incluido en otro control desplazable. Por tanto no es siempre el nmero ms grande que el valor puede tomar, ya que el
page_size de tales controles normalmente es distinto de cero.
7.2. Utilizacin de Ajustes de la Forma Fcil
Los controles ajustables pueden dividirse ms o menos en aquellos que usan y requieren unidades especficas para estos
valores, y aquellos que los tratan como nmero arbitrarios. El grupo que trata los valores como nmeros arbitrarios
incluye los controles de rango (barras de desplazamiento y escalas, la barra de progreso y los botones de
aumentar/disminuir). Todos estos controles normalmente se ajustan directamente por el usuario con el ratn o el teclado.
Tratarn los valores inferior y superior de un ajuste como un rango dentro del cual el usuario puede manipular el valor
del ajuste. Por defecto, solo modificarn el valor de un ajuste.
El otro grupo incluye el control de texto, el control de vista, el control de lista compuesta y el control de ventana de
desplazamiento. Todos ellos usan valores de pxeles para sus ajustes. Estos controles normalmente se ajustan
indirectamente usando barras de desplazamiento. Aunque todos los controles que usan ajustes pueden crear sus propios
ajustes o usar los que se les proporcione, normalmente se les querr dejar la tarea de crear sus propios ajustes.
Habitualmente, sobreescribirn todos los valores de configuracin de los ajustes que se les proporcionen, salvo el propio
valor de ajuste, aunque los resultados son, en general, impredecibles. (Lo que significa que se tendr que leer el cdigo
fuente para descubrirlo, y puede variar entre controles).
Ahora, probablemente se piense... puesto que los controles de texto y las vistas insisten en establecer todos sus
parmetros de configuracin excepto el valor de ajuste mientras que las barras de desplazamiento solamente tocan el
valor del ajuste, si se comparte un objeto ajuste entre una barra de desplazamiento y un control de texto, al manipular la
barra de desplazamiento, se ajustar automgicamente el control de texto? Por supuesto que lo har! De la siguiente
manera:
# crea sus propios ajustes
viewport = gtk.Viewport()
# usa los ajustes recin creados para la barra de desplazamiento tambin
48
vscrollbar = gtk.VScrollbar(viewport.get_vadjustment())
7.3. Interioridades de un Ajuste
Bien, se pensar, eso est bien, pero qu sucede si se desea crear unos manejadores propios que respondan cuando el
usuario ajuste un control de rango o un botn aumentar/disminuir, y cmo se obtiene el valor de un ajuste en estos
manejadores? Para contestar a estas preguntas y a otras ms, empezaremos echando un vistazo a los atributos de la
propia clase gtk.Adjustment:
lower
upper
value
step_increment
page_increment
page_size
Dada una instancia adj de la clase gtk.Adjustment, cada uno de los atributos se obtienen o modifican usando
adj.lower, adj.value, etc.
Ya que, cuando se determina el valor de un ajuste, generalmente tambin se quiere que el cambio afecte a todos los
controles que usan este ajuste, PyGTK proporciona un mtodo para hacer esto mismo:
adjustment.set_value(value)
Como se mencion anteriormente, Adjustment es una subclase de Object, al igual que los dems controles, y, por
tanto, es capaz de emitir seales. Esto es la causa, claro est, de por qu las actualizaciones ocurren automgicamente
cuando se comparte un objeto ajuste entre una barra de desplazamiento y otro control ajustable; todos los controles
ajustables conectan manejadores de seales a la seal "value_changed" de sus ajustes, como podra hacerlo cualquier
programa. Esta es la definicin de la retrollamada de esta seal:
def value_changed(adjustment):
Los diversos controles que usan el objeto Adjustment emitirn esta seal en un ajuste siempre que cambien su valor.
Esto ocurre tanto cuando el usuario hace que el deslizador se mueva en un control de rango, como cuando el programa
explcitamente cambia el valor con el mtodo set_value(). As, por ejemplo, si se tiene un control de escala, y se
quiere que cambie la rotacin de una imagen siempre que su valor cambie, se podra crear una retrollamada como esta:
def cb_rotate_picture(adj, picture):
set_picture_rotation (picture, adj.value)
...
y conectarla al ajuste del control de escala as:
adj.connect("value_changed", cb_rotate_picture, picture)
Y qu pasa cuando un control reconfigura los campos upper (superior) o lower (inferior) de su ajuste, tal y como
sucede cuando un usario aade ms texto al control de texto? En este caso, se emite la seal "changed", que es as:
def changed(adjustment):
Los controles Range normalmente conectan un manejador para esta seal, el cual cambia su apariencia para reflejar el
cambio - por ejemplo, el tamao del deslizador de una barra de desplazamiento aumentar o se reducir en proporcin
inversa a la diferencia entre el valor superior e inferior de su ajuste.
Probablemente nunca ser necesario que se haga la conexin de un manejador a esta seal, salvo que se est escribiendo
un nuevo tipo de control de rango. En cualquier caso, si se cambia directamente alguno de los valores de un
Adjustment, se debe emitir esta seal para reconfigurar los controles que lo usan. Tal que as:
49
adjustment.emit("changed")
Captulo 8. Controles de Rango
Tabla de contenidos
8.1. Barras de Desplazamiento
8.2. Controles de Escala
8.2.1. Creacin de un Control de Escala
8.2.2. Mtodos y Seales (bueno, al menos mtodos)
8.3. Mtodos Comunes de los Rangos
8.3.1. Establecimiento de la Poltica de Actualizacin
8.3.2. Obtencin y Cambio de Ajustes
8.4. Atajos de Teclas y Ratn
8.5. Ejemplo de Control de Rango
La categora de los controles de rango incluye el ubcuo control de barra de desplazamiento y el menos comn control de
escala. Aunque estos dos tipos de controles se usan generalmente para propsitos diferentes, son bastante similares en
funcin e implementacin. Todos los controles de rango comparten un conjunto de elementos grficos, cada uno de los
cuales tiene su propia ventana X y recibe eventos. Todos ellos contienen una gua o canal y un deslizador. Arrastrar el
deslizador con el puntero del ratn hace que se mueva hacia adelante y hacia atrs en el canal, mientras que haciendo
clic en el canal avanza el deslizador hacia la localizacin del clic, ya sea completamente, o con una cantidad designada,
dependiendo del botn del ratn que se use.
Como se mencion en Adjustments ms arriba, todos los controles de rango estn asociados con un objeto ajuste, a
partir del cual se calcula la longitud del deslizador y su posicin con respecto al canal. Cuando el usuario manipula el
deslizador, el control de rango cambiar el valor del ajuste.
8.1. Barras de Desplazamiento
Estas son las barras de desplazamiento estndar. Deberan usarse nicamente para desplazar otro control, tal como una
lista, una caja de texto, o una vista (viewport), aunque, generalmente, es ms fcil de usar la ventana de desplazamiento
en la mayora de los casos. Para otros propsitos, se deberan usar los controles de escala, ya que son ms agradables y
tienen ms funciones.
Existen tipos separados para las barras de desplazamiento horizontales y verticales. No hay demasiado que decir sobre
ellos. Los puedes crear con los siguientes mtodos:
hscrollbar = gtk.HSscrollbar(adjustment=None)
vscrollbar = gtk.VSscrollbar(adjustment=None)
y eso es todo. El argumento adjustment puede ser, o bien una referencia a un Adjustment existente, o bien nada,
en cuyo caso se crear uno. Especificar nada puede ser til en el caso en el que se quiera pasar el ajuste recin creado al
constructor de otro control que lo configurar por uno, tal como podra ocurrir con una caja de texto.
8.2. Controles de Escala
Los controles Scale (Escala) se usan para permitir al usuario seleccionar y manipular visualmente un valor dentro de
un rango especfico. Se puede usar un control de escala, por ejemplo, para ajustar el nivel de zoom en una
previsualizacin de una imagen, o para controlar el brillo de un color, o para especificar el nmero de minutos de
inactividad antes de que el protector de pantalla se active.
8.2.1. Creacin de un Control de Escala
Al igual que con las barras de desplazamiento, hay controles separados para los controles de escala horizontales y
verticales. (La mayora de los programadres parecen usar los controles de escala horizontales.) Ya que esencialmente
50
funcionan de la misma manera, no hay necesidad de tratarlos por separado aqu. Los siguientes mtodos crean controles
de escala verticales y horizontales, respectivamente:
vscale = gtk.VScale(adjustment=None)
hscale = gtk.HScale(adjustment=None)
El argumento adjustment puede ser bien un ajuste que ya haya sido creado con gtk.Adjustment(), o bien nada,
en cuyo caso se crea un Adjustment annimo con todos sus valores puestos a 0.0 (lo cual no es demasiado til). Para
evitar confusiones, probablemente sea mejor crear un ajuste con un valor page_size de 0.0 para que su valor upper
realmente corresponda con el valor ms alto que el usuario puede seleccionar. (Si esto resulta confuso, la seccin sobre
Ajustes da una explicacin detallada sobre qu hacen exactamente los ajustes y cmo crearlos y manipularlos.)
8.2.2. Mtodos y Seales (bueno, al menos mtodos)
Los controles de escala pueden visualizar su valor como un nmero al lado del canal. El comportamiento por defecto es
mostrar el valor, pero esto se puede cambiar con el mtodo:
scale.set_draw_value(draw_value)
Como habrs imaginado, draw_value (representar valor) es o TRUE o FALSE, con consecuencias predecibles para
cualquiera de los dos.
El valor que muestra un control de escala se redondea a un valor decimal por defecto, tal y como se hace con el campo
valor en su Adjustment (Ajuste). Se puede cambiar esto con:
scale.set_digits(digits)
donde digits (dgitos) es el nmero de posiciones decimales que se representarn. Se puede poner el nmero que se
desee, pero no se representarn en pantalla ms de 13 dgitos decimales.
Finalmente, el valor se puede mostrar en diferentes posiciones relativas al canal:
scale.set_value_pos(pos)
El argumento pos (posicin) puede tomar uno de los siguientes valores:
POS_LEFT
POS_RIGHT
POS_TOP
POS_BOTTOM
Si pones el valor en un "lado" del canal (por ejemplo, en la parte de arriba o de abajo de un control de escala horizontal),
entonces seguir al deslizador en su desplazamiento hacia arriba y hacia abajo del canal.
8.3. Mtodos Comunes de los Rangos
La clase Range es bastante complicada internamente, pero, como todas las clases base de los controles, la mayora de
su complejidad solo resulta de inters si se quiere trastear con ellos. Adems, la mayora de los mtodos y seales que
define slo son tiles al escribir controles derivados. Hay, en cualquier caso, unos cuantos mtodos tiles que
funcionarn con todos los controles de rango.
8.3.1. Establecimiento de la Poltica de Actualizacin
La "poltica de actualizacin" de un control de rango define en qu puntos de la interaccin con el usuario se cambiar el
campo de valor de su Adjustment y emitir la seal "value_changed" en este Adjustment. Las polticas de
actualizacin son:
51
UPDATE_CONTINUOUS
UPDATE_DISCONTINUOUS
La seal "value_changed" slo se mite una vez que el deslizador ha parado de moverse
y el usuario ha soltado el botn del ratn.
UPDATE_DELAYED
52
ajustes, por lo que se puede ver cmo afectan a la manera en la que estos controles se comportan para el usuario. La
figura Figura 8.1, Ejemplo de Controles de Rango muestra el resultado de ejecutar el programa:
Figura 8.1. Ejemplo de Controles de Rango
#!/usr/bin/env python
# ejemplo rangewidgets.py
import pygtk
pygtk.require('2.0')
import gtk
# Funciones auxiliares
def make_menu_item(name, callback, data=None):
item = gtk.MenuItem(name)
item.connect("activate", callback, data)
item.show()
return item
def scale_set_default_values(scale):
scale.set_update_policy(gtk.UPDATE_CONTINUOUS)
scale.set_digits(1)
scale.set_value_pos(gtk.POS_TOP)
scale.set_draw_value(gtk.TRUE)
class RangeWidgets:
def cb_pos_menu_select(self, item, pos):
# Fijar la posicin del valor para ambos ajustes
self.hscale.set_value_pos(pos)
self.vscale.set_value_pos(pos)
53
28
29
def cb_update_menu_select(self, item, policy):
30
# Establecer la poltica de actualizacin para ambos controles
31
self.hscale.set_update_policy(policy)
32
self.vscale.set_update_policy(policy)
33
34
def cb_digits_scale(self, adj):
35
# Fijar el nmero de posiciones decimales a las que se ajusta el
valor
36
self.hscale.set_digits(adj.value)
37
self.vscale.set_digits(adj.value)
38
39
def cb_page_size(self, get, set):
40
# Fijar el valor del tamao de pgina y de su incremento
41
# para el ajuste al valor especificado por la escala "Page Size"
42
set.page_size = get.value
43
set.page_incr = get.value
44
# Ahora emitir la seal "changed" para reconfigurar todos los
controles
45
# que estn ligados a este ajuste
46
set.emit("changed")
47
48
def cb_draw_value(self, button):
49
# Activar o desactivar la representacin del valor en funcin del
50
# estado del botn de activacin
51
self.hscale.set_draw_value(button.get_active())
52
self.vscale.set_draw_value(button.get_active())
53
54
# crea la ventana de ejemplo
55
56
def __init__(self):
57
# Creacin de la ventana principal
58
self.window = gtk.Window (gtk.WINDOW_TOPLEVEL)
59
self.window.connect("destroy", lambda w: gtk.main_quit())
60
self.window.set_title("range controls")
61
62
box1 = gtk.VBox(gtk.FALSE, 0)
63
self.window.add(box1)
64
box1.show()
65
66
box2 = gtk.HBox(gtk.FALSE, 10)
67
box2.set_border_width(10)
68
box1.pack_start(box2, gtk.TRUE, gtk.TRUE, 0)
69
box2.show()
70
71
# value, lower, upper, step_increment, page_increment, page_size
72
# Obsrvese que el valor page_size solamente es significativo
73
# para controles de barra de desplazamiento y que el valor ms
alto posible es
74
# (upper - page_size).
75
adj1 = gtk.Adjustment(0.0, 0.0, 101.0, 0.1, 1.0, 1.0)
76
77
self.vscale = gtk.VScale(adj1)
78
scale_set_default_values(self.vscale)
79
box2.pack_start(self.vscale, gtk.TRUE, gtk.TRUE, 0)
80
self.vscale.show()
81
82
box3 = gtk.VBox(gtk.FALSE, 10)
83
box2.pack_start(box3, gtk.TRUE, gtk.TRUE, 0)
84
box3.show()
54
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
gtk.POS_TOP)
125
126
127
128
129
130
131
gtk.POS_LEFT)
132
133
134
gtk.POS_RIGHT)
135
136
137
138
139
140
141
55
142
box2.show()
143
144
box2 = gtk.HBox(gtk.FALSE, 10)
145
box2.set_border_width(10)
146
147
# Otro men de opciones ms, esta vez para la poltica de
actualizacin
148
# de los controles de escala
149
label = gtk.Label("Scale Update Policy:")
150
box2.pack_start(label, gtk.FALSE, gtk.FALSE, 0)
151
label.show()
152
153
opt = gtk.OptionMenu()
154
menu = gtk.Menu()
155
156
item = make_menu_item("Continuous", self.cb_update_menu_select,
157
gtk.UPDATE_CONTINUOUS)
158
menu.append(item)
159
160
item = make_menu_item ("Discontinuous",
self.cb_update_menu_select,
161
gtk.UPDATE_DISCONTINUOUS)
162
menu.append(item)
163
164
item = make_menu_item ("Delayed", self.cb_update_menu_select,
165
gtk.UPDATE_DELAYED)
166
menu.append(item)
167
168
opt.set_menu(menu)
169
box2.pack_start(opt, gtk.TRUE, gtk.TRUE, 0)
170
opt.show()
171
172
box1.pack_start(box2, gtk.TRUE, gtk.TRUE, 0)
173
box2.show()
174
175
box2 = gtk.HBox(gtk.FALSE, 10)
176
box2.set_border_width(10)
177
178
# Un control HScale para ajustar el nmero de dgitos en las
escalas
179
# de ejemplo
180
label = gtk.Label("Scale Digits:")
181
box2.pack_start(label, gtk.FALSE, gtk.FALSE, 0)
182
label.show()
183
184
adj2 = gtk.Adjustment(1.0, 0.0, 5.0, 1.0, 1.0, 0.0)
185
adj2.connect("value_changed", self.cb_digits_scale)
186
scale = gtk.HScale(adj2)
187
scale.set_digits(0)
188
box2.pack_start(scale, gtk.TRUE, gtk.TRUE, 0)
189
scale.show()
190
191
box1.pack_start(box2, gtk.TRUE, gtk.TRUE, 0)
192
box2.show()
193
194
box2 = gtk.HBox(gtk.FALSE, 10)
195
box2.set_border_width(10)
196
197
# Y un ltimo control HScale para ajustar el tamao de pgina
198
# de la barra de desplazamiento.
56
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
Se debe advertir que el programa no llama al mtodo connect() para el evento "delete_event", sino solamente a la
seal "destroy". Esto seguir realizando la accin deseada, puesto que un evento "delete_event" sin tratar resultar en
una seal "destroy" enviada a la ventana.
Captulo 9. Miscelnea de Controles
Tabla de contenidos
9.1. Etiquetas
9.2. Flechas (Arrow)
9.3. El Objeto Pistas (Tooltip)
9.4. Barras de Progreso (ProgressBar)
9.5. Dilogos
9.6. Imgenes
9.6.1. Pixmaps
9.7. Reglas
9.8. Barras de Estado
9.9. Entradas de Texto (Entry)
9.10. Botones Aumentar/Disminuir
9.11. Lista Desplegable (Combo)
9.12. Calendario
57
58
El programa de ejemplo label.py es un ejemplo corto para ilustrar estos mtodos. Este ejemplo hace uso del control
Frame (Marco) para demostrar mejor los estilos de etiqueta. Se puede ignorar esto por ahora ya que el control Frame
(Marco) se explica despus.
En GTK+ 2.0, el texto de la etiqueta puede contener marcas para el tipo de letra y otros atributos del texto, y las
etiquetas pueden ser seleccionables (para copiar y pegar). Estas caractersticas avanzadas no se explican aqui.
La figura Figura 9.1, Ejemplos de Etiquetas ilustra el resultado de ejecutar el programa de ejemplo:
Figura 9.1. Ejemplos de Etiquetas
#!/usr/bin/env python
# ejemplo label.py
import pygtk
pygtk.require('2.0')
import gtk
class Labels:
def __init__(self):
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.connect("destroy", lambda w: gtk.main_quit())
self.window.set_title("Label")
vbox = gtk.VBox(gtk.FALSE, 5)
hbox = gtk.HBox(gtk.FALSE, 5)
self.window.add(hbox)
hbox.pack_start(vbox, gtk.FALSE, gtk.FALSE, 0)
self.window.set_border_width(5)
frame = gtk.Frame("Normal Label")
label = gtk.Label("This is a Normal label")
frame.add(label)
59
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
label.\n"
41
42
43
44
45
46
47
48
49
It "
50
"
51
52
53
come to "
54
55
56
correctly, "
57
58
59
60
61
62
63
64
label. "
65
66
to it."
67
68
69
70
71
72
73
74
75
76
line")
width allocated
60
77
vbox.pack_start(frame, gtk.FALSE, gtk.FALSE, 0)
78
79
frame = gtk.Frame("Underlined label")
80
label = gtk.Label("This label is underlined!\n"
81
"This one is underlined in quite a funky
fashion")
82
label.set_justify(gtk.JUSTIFY_LEFT)
83
label.set_pattern(
84
"_________________________ _ _________ _ ______
__
_______ ___")
85
frame.add(label)
86
vbox.pack_start(frame, gtk.FALSE, gtk.FALSE, 0)
87
self.window.show_all ()
88
89 def main():
90
gtk.main()
91
return 0
92
93 if __name__ == "__main__":
94
Labels()
95
main()
Obsrvese que la etiqueta "Filled, wrapped label" no tiene justificacin completa (fill justified).
9.2. Flechas (Arrow)
El control Arrow (Flecha) dibuja la cabeza de una flecha, apuntando a un nmero de direcciones posibles y con un
nmero de estilos posibles. Puede ser muy til en un botn en muchas aplicaciones. Al igual que el control Label
(Etiqueta), tampoco emite ninguna seal.
Slo hay dos llamadas para manipular un control Arrow :
arrow = gtk.Arrow(arrow_type, shadow_type)
arrow.set(arrow_type, shadow_type)
La primera crea un control flecha con el tipo y apariencia indicados. La segunda permite cambiar cualquiera de estos
valores. El argumento arrow_type puede tomar uno de lo siguientes valores:
ARROW_UP
ARROW_DOWN
ARROW_LEFT
ARROW_RIGHT
#(Arriba)
#(Abajo)
#(Izquierda)
#(Derecha)
Estos valores obviamente indican la direccin hacia la que apunta la flecha. El argumento
<keyword>shadow_type</keyword> puede tomar uno de los siguientes valores:
SHADOW_IN
SHADOW_OUT
# valor predeterminado
SHADOW_ETCHED_IN
SHADOW_ETCHED_OUT
El programa de ejemplo arrow.py ilustra brevemente su uso. La figura Figura 9.2, Ejemplos de Botones con Flechas
muestra el resultado de ejecutar el programa:
Figura 9.2. Ejemplos de Botones con Flechas
61
62
51
52
53
54
55
56
57
58
59
60
window.show()
def main():
gtk.main()
return 0
if __name__ == "__main__":
Arrows()
main()
#!/usr/bin/env python
# ejemplo tooltip.py
import pygtk
pygtk.require('2.0')
import gtk
# Crear una Flecha con los parmetros especificados
# y empaquetarlo en un botn
def create_arrow_button(arrow_type, shadow_type):
63
12
button = gtk.Button()
13
arrow = gtk.Arrow(arrow_type, shadow_type)
14
button.add(arrow)
15
button.show()
16
arrow.show()
17
return button
18
19 class Tooltips:
20
def __init__(self):
21
# Creamos una ventana nueva
22
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
23
24
window.set_title("Tooltips")
25
26
# It's a good idea to do this for all windows.
27
window.connect("destroy", lambda w: gtk.main_quit())
28
29
# Establece el grosor del borde de la ventana
30
window.set_border_width(10)
31
32
# Creamos una caja para poner las flechas/botones
33
box = gtk.HBox(gtk.FALSE, 0)
34
box.set_border_width(2)
35
window.add(box)
36
37
# creamos un objeto de pista
38
self.tooltips = gtk.Tooltips()
39
40
# Empaquetamos y mostramos todos los controles
41
box.show()
42
43
button = create_arrow_button(gtk.ARROW_UP, gtk.SHADOW_IN)
44
box.pack_start(button, gtk.FALSE, gtk.FALSE, 3)
45
self.tooltips.set_tip(button, "SHADOW_IN")
46
47
button = create_arrow_button(gtk.ARROW_DOWN, gtk.SHADOW_OUT)
48
box.pack_start(button, gtk.FALSE, gtk.FALSE, 3)
49
self.tooltips.set_tip(button, "SHADOW_OUT")
50
51
button = create_arrow_button(gtk.ARROW_LEFT,
gtk.SHADOW_ETCHED_IN)
52
box.pack_start(button, gtk.FALSE, gtk.FALSE, 3)
53
self.tooltips.set_tip(button, "SHADOW_ETCHED_IN")
54
55
button = create_arrow_button(gtk.ARROW_RIGHT,
gtk.SHADOW_ETCHED_OUT)
56
box.pack_start(button, gtk.FALSE, gtk.FALSE, 3)
57
self.tooltips.set_tip(button, "SHADOW_ETCHED_OUT")
58
59
window.show()
60
61 def main():
62
gtk.main()
63
return 0
64
65 if __name__ == "__main__":
66
tt = Tooltips()
67
main()
64
Hay otros mtodos que se pueden usar con las pistas. Simplemente los listaremos, junto con una breve descripcin sobre
su funcin.
tooltips.enable()
Activa un conjunto de pistas desactivadas.
tooltips.disable()
Desactiva un conjunto de pistas activadas.
tooltips.set_delay(delay)
Fija los milisegundos que deben transcurrir con el puntero sobre el control antes de que la pista aparezca. El valor
predefinido es de 500 milisegundos (medio segundo).
Y esos son todos los mtodos asociados con las pistas. Ms de lo que siempre se querra saber :-)
9.4. Barras de Progreso (ProgressBar)
Las barras de progreso se usan para mostrar el estado de una operacin. Son bastante fciles de usar, como se ver en el
cdigo que sigue. Pero primero empecemos con una llamada para crear una nueva barra de progreso.
progressbar = gtk.ProgressBar(adjustment=None)
El argumento adjustment (ajuste) especifica un ajuste para usarlo con la barra de progreso progressbar. Si no se
especifica se crear un ajuste. Ahora que la barra de progreso est creada ya podemos usarla.
progressbar.set_fraction(fraction)
El objeto progressbar es la barra de progreso con la que queremos operar, y el argumento (fraction) es la
cantidad "completada", lo que significa la cantidad con la que se ha rellenado la barra de progreso desde 0 a 100%. Esto
se le pasa al mtodo como un nmero real entre 0 y 1.
Una barra de progreso puede orientarse de diversas formas usando el mtodo:
progressbar.set_orientation(orientation)
El argumento orientation puede tomar uno de los siguientes valores para indicar la direccin en la que la barra de
progreso se mueve:
PROGRESS_LEFT_TO_RIGHT
PROGRESS_RIGHT_TO_LEFT
PROGRESS_BOTTOM_TO_TOP
PROGRESS_TOP_TO_BOTTOM
#
#
#
#
izquierda a derecha
derecha a izquierda
abajo a arriba
arriba a abajo
Adems de indicar la cantidad de progreso que se ha completado, la barra de progreso tambin puede usarse
simplemente para indicar que ha habido alguna actividad. Esto puede ser til en situaciones donde el progreso no se
puede medir con un rango de valores. La siguiente funcin indica que se ha hecho algn progreso.
progressbar.pulse()
El tamao de paso de un indicador de actividad se establece usando la siguiente funcin, donde la fraccin es un nmero
entre 0.0 y 1.0.
progressbar.set_pulse_step(fraction)
65
Cuando no est en el modo actividad, la barra de progreso tambin puede mostrar una cadena de texto en su canal,
usando el siguiente mtodo:
progressbar.set_text(text)
Nota
Tngase en cuenta que set_text() no soporta el formateo de texto al estilo printf() como lo haca
la barra de progreso de GTK+ 1.2.
Se puede desactivar la visualizacin de la cadena llamando a set_text() de nuevo sin argumentos.
La cadena de texto actual de la barra de progreso se puede obtener con el siguiente mtodo:
text = progressbar.get_text()
Normalmente las Barras de Progreso usan cronmetros u otras funciones parecidas (mira la seccin sobre Cronmetros,
E/S y Funciones de Inactividad) para dar la ilusin de multitarea. Todas usarn los mtodos set_fraction() o
pulse() de la misma forma.
El programa progressbar.py proporciona un ejemplo de barra de progreso, actualizada usando cronmetros. Este
cdigo tambin muestra como reiniciar la Barra de Progreso. La figura Figura 9.4, Ejemplo de Barra de Progreso
muestra la ventana resultante:
Figura 9.4. Ejemplo de Barra de Progreso
#!/usr/bin/env python
# ejemplo progressbar.py
import pygtk
pygtk.require('2.0')
import gtk
# Actualizar el valor de la barra de progreso de manera
# que tengamos algo de movimiento
def progress_timeout(pbobj):
if pbobj.activity_check.get_active():
pbobj.pbar.pulse()
else:
# Calcular el valor de la barra de progreso usando el
66
16
# valor del rango establecido en el objeto ajuste
17
new_val = pbobj.pbar.get_fraction() + 0.01
18
if new_val > 1.0:
19
new_val = 0.0
20
# Fijar el nuevo valor
21
pbobj.pbar.set_fraction(new_val)
22
23
# Puesto que esta es una funcin de cronmetro, devolver TRUE de
manera
24
# que contine siendo llamada
25
return gtk.TRUE
26
27 class ProgressBar:
28
# Retrollamada que conmuta el dibujado del texto en el
29
# canal de la barra de progreso
30
def toggle_show_text(self, widget, data=None):
31
if widget.get_active():
32
self.pbar.set_text("some text")
33
else:
34
self.pbar.set_text("")
35
36
# Retrollamada que conmuta el modo de actividad de
37
# la barra de progreso
38
def toggle_activity_mode(self, widget, data=None):
39
if widget.get_active():
40
self.pbar.pulse()
41
else:
42
self.pbar.set_fraction(0.0)
43
44
# Retrollamada que conmuta la orientacin de la barra de progreso
45
def toggle_orientation(self, widget, data=None):
46
if self.pbar.get_orientation() == gtk.PROGRESS_LEFT_TO_RIGHT:
47
self.pbar.set_orientation(gtk.PROGRESS_RIGHT_TO_LEFT)
48
elif self.pbar.get_orientation() == gtk.PROGRESS_RIGHT_TO_LEFT:
49
self.pbar.set_orientation(gtk.PROGRESS_LEFT_TO_RIGHT)
50
51
# Limpiamos la memoria reservada y eliminamos el temporizador
52
def destroy_progress(self, widget, data=None):
53
gtk.timeout_remove(self.timer)
54
self.timer = 0
55
gtk.main_quit()
56
57
def __init__(self):
58
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
59
self.window.set_resizable(gtk.TRUE)
60
61
self.window.connect("destroy", self.destroy_progress)
62
self.window.set_title("ProgressBar")
63
self.window.set_border_width(0)
64
65
vbox = gtk.VBox(gtk.FALSE, 5)
66
vbox.set_border_width(10)
67
self.window.add(vbox)
68
vbox.show()
69
70
# Creamos un objeto de alineacin centrador
71
align = gtk.Alignment(0.5, 0.5, 0, 0)
72
vbox.pack_start(align, gtk.FALSE, gtk.FALSE, 5)
73
align.show()
74
67
75
76
77
78
79
80
81
de la barra
82
83
84
85
86
87
88
89
90
91
92
93
canal
94
95
96
97
98
99
100
101
actividad
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
simplemente
126
127
128
129
130
68
131
132
133
134
135
136
137
138
def main():
gtk.main()
return 0
if __name__ == "__main__":
ProgressBar()
main()
9.5. Dilogos
El control Dialog (Dilogo) es muy simple, y realmente es slo una ventana con unas cuantas cosas ya empaquetadas.
Simplemente crea una ventana, y luego empaqueta una VBox en ella, que contiene un separador y luego una HBox
llamada "action_area" ("rea de accin").
El control Dialog (Dilogo) se puede usar para mensajes emergentes para el usuario, y otras tareas similares. Es
realmente bsico, y slo hay una funcin para la caja de dilogo, que es:
dialog = gtk.Dialog(title=None, parent=None, flags=0, buttons=None)
donde title (ttulo) es el texto usado en la barra de ttulo, parent (padre) es la ventana principal de la aplicacin y
flags establece varios modos de operacin para el dilogo:
DIALOG_MODAL - hace el dilogo modal
DIALOG_DESTROY_WITH_PARENT - destruye el dilogo cuando su padre sea destruido
DIALOG_NO_SEPARATOR - omite el separador entre la vbox y el rea de accin
El argumento buttons (botones) es una tupla de pares texto de botn y respuesta. Todos los argumentos tienen valores
predeterminados y pueden especificarse usando palabras clave.
Esto crear la caja de dilogo, y ahora depende del desarrollador el usarla. Se podra empaquetar un botn en el rea de
accin:
button = ...
dialog.action_area.pack_start(button, TRUE, TRUE, 0)
button.show()
Y se podra aadir, por ejemplo, una etiqueta, al rea vbox usando el empaquetamiento, con algo as:
label = gtk.Label("Los dilogos molan")
dialog.vbox.pack_start(label, TRUE, TRUE, 0)
label.show()
Como ejemplo del uso de una caja de dilogo, se podran poner dos botones en el rea de accin, un botn de Cancelar,
un botn de Aceptar y una etiqueta en el rea vbox, haciendo una pregunta al usuario, informando de un error, etc.
Luego se podran conectar diferentes seales a cada botn y realizar la operacin que el usuario seleccione.
Si la funcionalidad bsica que proporcionan las cajas verticales y horizontales predeterminadas no dan el suficiente
control para la aplicacin, entonces se puede sencillamente empaquetar otro control dentro de las cajas proporcionadas.
Por ejemplo, se podra empaquetar una tabla en la caja vertical.
9.6. Imgenes
Las Images (Imgenes) son estructuras de datos que contienen dibujos. Estos dibujos se pueden usar en varios sitios.
Las Images (Imgenes) se pueden crear a partir de Pixbufs, Pixmaps, archivos que contengan informacin de
imagen (por ejemplo. XPM, PNG, JPEG, TIFF, etc.), e incluso ficheros de animacin.
69
70
12
gtk.main_quit()
13
return gtk.FALSE
14
15
# se invoca cuando el botn es pulsado. Simplemente imprime un
mensaje.
16
def button_clicked(self, widget, data=None):
17
print "button %s clicked" % data
18
19
def __init__(self):
20
# crea la ventana principal y conecta la seal delete_event
signal para finalizar
21
# la aplicacin
22
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
23
window.connect("delete_event", self.close_application)
24
window.set_border_width(10)
25
window.show()
26
27
# una caja horizontal que contenga los botones
28
hbox = gtk.HBox()
29
hbox.show()
30
window.add(hbox)
31
32
pixbufanim = gtk.gdk.PixbufAnimation("goalie.gif")
33
image = gtk.Image()
34
image.set_from_animation(pixbufanim)
35
image.show()
36
# un botn que contenga el control de imagen
37
button = gtk.Button()
38
button.add(image)
39
button.show()
40
hbox.pack_start(button)
41
button.connect("clicked", self.button_clicked, "1")
42
43
# crear varias imgenes con datos de archivos y cargarlos
44
# en botones
45
image = gtk.Image()
46
image.set_from_file("apple-red.png")
47
image.show()
48
# un botn que cotenga el control de imagen
49
button = gtk.Button()
50
button.add(image)
51
button.show()
52
hbox.pack_start(button)
53
button.connect("clicked", self.button_clicked, "2")
54
55
image = gtk.Image()
56
image.set_from_file("chaos.jpg")
57
image.show()
58
# un botn que cotenga el control de imagen
59
button = gtk.Button()
60
button.add(image)
61
button.show()
62
hbox.pack_start(button)
63
button.connect("clicked", self.button_clicked, "3")
64
65
image = gtk.Image()
66
image.set_from_file("important.tif")
67
image.show()
68
# un botn que cotenga el control de imagen
69
button = gtk.Button()
71
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
button.add(image)
button.show()
hbox.pack_start(button)
button.connect("clicked", self.button_clicked, "4")
image = gtk.Image()
image.set_from_file("soccerball.gif")
image.show()
# un botn que cotenga el control de imagen
button = gtk.Button()
button.add(image)
button.show()
hbox.pack_start(button)
button.connect("clicked", self.button_clicked, "5")
def main():
gtk.main()
return 0
if __name__ == "__main__":
ImagesExample()
main()
9.6.1. Pixmaps
Los Pixmaps son estructuras de datos que contienen dibujos. Estos dibujos se pueden usar en varios sitios, pero lo ms
comn es usarlos como iconos en un escritorio X, o como cursores.
Un pixmap con slo 2 colores se llama bitmap, y hay unas cuantas rutinas adicionales para trabajar con este caso
especial.
Para entender los pixmaps, es de ayuda entender cmo funciona el sistema X Window. En X, las aplicaciones no
necesitan ejecutarse en el mismo ordenador que interactua con el usuario. En cambio, estas aplicaciones, llamadas
"clientes", se comunican con un programa que muestra los grficos y maneja el teclado y el ratn. Este programa que
interactua directamente con el usuario se llama un "servidor de visualizacin" o "servidor X". Ya que la comunicacin
puede tener lugar sobre una red, es importante mantener alguna informacin en el servidor X. Los Pixmaps, por
ejemplo, se almacenan en la memoria del servidor X. Esto significa que, una vez que los valores de un pixmap se
establecen, no hay que seguir transmitiendolos por la red; en lugar de eso, se enva un comando para "mostrar el pixmap
nmero XYZ aqui." Incluso si no se est usando X con GTK simultneamente, usando construcciones como Pixmaps
har que los programas funcionen de forma aceptable en X.
Para usar pixmaps en PyGTK, primero debemos construir un gtk.gdk.Pixmap usando las funciones de gtk.gdk en
PyGTK. Los Pixmaps se pueden crear a partir de datos en memoria, o a partir de datos ledos desde un fichero.
Veamos cada una de las llamadas usadas para crear un pixmap.
pixmap = gtk.gdk.pixmap_create_from_data(window, data, width, height, depth,
fg, bg)
Esta rutina se usa para crear un pixmap con la profundidad de color dada por el argumento depth a partir de los datos
data en memoria. Si depth es -1 su valor se deduce de la de la ventana window. Cada pixel usa un nmero de bits de
datos para representar el color que es igual a la profundidad de color. El width(ancho) y el height (alto) son en
pixeles. El argumento window (ventana) debe referirse a una gtk.gdk.Window realizada, ya que los recursos de un
pixmap slo tienen sentido en el contexto de la pantalla donde se va a visualizar. fg y bg son los colores de frente y
fondo del pixmap.
Se pueden crear pixmaps desde ficheros XPM usando:
72
73
74
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
Una desventaja de usar pixmaps es que que el objeto mostrado siempre es rectangular, independientemente de la
imagen. Nos gustara crear escritorios y aplicaciones con iconos que tengan formas ms naturales. Por ejemplo, para la
interfaz de un juego, nos gustara tener botones redondos para pulsar. La forma de hacer esto es usar ventanas con
forma.
Una ventana con forma es simplemente un pixmap en el que los pixeles de fondo son transparentes. De esta forma,
cuando la imagen de fondo se colorea, no la sobreescribimos con un borde rectangular y que no encaja, de nuestro icono.
El programa de ejemplo wheelbarrow.p muestra una imagen completa en el escritorio. La figura Figura 9.7, Ejemplo
de Ventana con Forma muestra la imagen sobre una ventana de terminal:
Figura 9.7. Ejemplo de Ventana con Forma
#!/usr/bin/env python
# ejemplo wheelbarrow.py
import pygtk
pygtk.require('2.0')
import gtk
# XPM
75
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
WheelbarrowFull_xpm = [
"48 48 64 1",
"
c None",
".
c #DF7DCF3CC71B",
"X
c #965875D669A6",
"o
c #71C671C671C6",
"O
c #A699A289A699",
"+
c #965892489658",
"@
c #8E38410330C2",
"#
c #D75C7DF769A6",
"$
c #F7DECF3CC71B",
"%
c #96588A288E38",
"&
c #A69992489E79",
"*
c #8E3886178E38",
"=
c #104008200820",
"c #596510401040",
";
c #C71B30C230C2",
":
c #C71B9A699658",
">
c #618561856185",
",
c #20811C712081",
"<
c #104000000000",
"1
c #861720812081",
"2
c #DF7D4D344103",
"3
c #79E769A671C6",
"4
c #861782078617",
"5
c #41033CF34103",
"6
c #000000000000",
"7
c #49241C711040",
"8
c #492445144924",
"9
c #082008200820",
"0
c #69A618611861",
"q
c #B6DA71C65144",
"w
c #410330C238E3",
"e
c #CF3CBAEAB6DA",
"r
c #71C6451430C2",
"t
c #EFBEDB6CD75C",
"y
c #28A208200820",
"u
c #186110401040",
"i
c #596528A21861",
"p
c #71C661855965",
"a
c #A69996589658",
"s
c #30C228A230C2",
"d
c #BEFBA289AEBA",
"f
c #596545145144",
"g
c #30C230C230C2",
"h
c #8E3882078617",
"j
c #208118612081",
"k
c #38E30C300820",
"l
c #30C2208128A2",
"z
c #38E328A238E3",
"x
c #514438E34924",
"c
c #618555555965",
"v
c #30C2208130C2",
"b
c #38E328A230C2",
"n
c #28A228A228A2",
"m
c #41032CB228A2",
"M
c #104010401040",
"N
c #492438E34103",
"B
c #28A2208128A2",
"V
c #A699596538E3",
76
70 "C
c #30C21C711040",
71 "Z
c #30C218611040",
72 "A
c #965865955965",
73 "S
c #618534D32081",
74 "D
c #38E31C711040",
75 "F
c #082000000820",
76 "
",
77 "
.XoO
",
78 "
+@#$%o&
",
79 "
*=-;#::o+
",
80 "
>,<12#:34
",
81 "
45671#:X3
",
82 "
+89<02qwo
",
83 "e*
>,67;ro
",
84 "ty>
459@>+&&
",
85 "$2u+
><ipas8*
",
86 "%$;=*
*3:.Xa.dfg>
",
87 "Oh$;ya
*3d.a8j,Xe.d3g8+
",
88 " Oh$;ka
*3d$a8lz,,xxc:.e3g54
",
89 " Oh$;kO
*pd$%svbzz,sxxxxfX..&wn>
",
90 "
Oh$@mO
*3dthwlsslszjzxxxxxxx3:td8M4
",
91 "
Oh$@g& *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B*
",
92 "
Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5&
",
93 "
Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM* ",
94 "
OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ",
95 "
2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ",
96 "
:,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo",
97 "
+A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g",
98 "
*#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&en",
99 "
p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>",
100 "
OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ",
101 "
3206Bwxxszx%et.eaAp77m77mmmf3&eeeg* ",
102 "
@26MvzxNzvlbwfpdettttttttttt.c,n& ",
103 "
*;16=lsNwwNwgsvslbwwvccc3pcfu<o
",
104 "
p;<69BvwwsszslllbBlllllllu<5+
",
105 "
OS0y6FBlvvvzvzss,u=Blllj=54
",
106 "
c1-699Blvlllllu7k96MMMg4
",
107 "
*10y8n6FjvllllB<166668
",
108 "
S-kg+>666<M<996-y6n<8*
",
109 "
p71=4 m69996kD8Z-66698&&
",
110 "
&i0ycm6n4 ogk17,0<6666g
",
111 "
N-k-<>
>=01-kuu666>
",
112 "
,6ky&
&46-10ul,66,
",
113 "
Ou0<>
o66y<ulw<66&
",
114 "
*kk5
>66By7=xu664
",
115 "
<<M4
466lj<Mxu66o
",
116 "
*>>
+66uv,zN666*
",
117 "
566,xxj669
",
118 "
4666FF666>
",
119 "
>966666M
",
120 "
oM6668+
",
121 "
*4
",
122 "
",
123 "
"
124 ]
125
126 class WheelbarrowExample:
127
# Cuando se invoca (con la seal delete_event), finaliza la
aplicacin
128
def close_application(self, widget, event, data=None):
77
129
gtk.main_quit()
130
return gtk.FALSE
131
132
def __init__(self):
133
# Crea la ventana principal y conecta la seal delete_event para
finalizar
134
# la aplicacin. Obsrvese que la ventana principal no tendr
ttulo
135
# ya que vamos a hacer una ventana emergente (popup).
136
window = gtk.Window(gtk.WINDOW_POPUP)
137
window.connect("delete_event", self.close_application)
138
window.set_events(window.get_events() |
gtk.gdk.BUTTON_PRESS_MASK)
139
window.connect("button_press_event", self.close_application)
140
window.show()
141
142
# ahora para el pixmap y el control de imagen
143
pixmap, mask = gtk.gdk.pixmap_create_from_xpm_d(
144
window.window, None, WheelbarrowFull_xpm)
145
image = gtk.Image()
146
image.set_from_pixmap(pixmap, mask)
147
image.show()
148
149
# Para mostrar la imagen usamos un control fijo para situarla
150
fixed = gtk.Fixed()
151
fixed.set_size_request(200, 200)
152
fixed.put(image, 0, 0)
153
window.add(fixed)
154
fixed.show()
155
156
# Esto enmascara todo salvo la imagen misma
157
window.shape_combine_mask(mask, 0, 0)
158
159
# mostramos la ventana
160
window.set_position(gtk.WIN_POS_CENTER_ALWAYS)
161
window.show()
162
163 def main():
164
gtk.main()
165
return 0
166
167 if __name__ == "__main__":
168
WheelbarrowExample()
169
main()
Para hacer la imagen sensible, conectamos la seal "button_press_event" para que el programa finalice. Las lineas 138139 hacen el dibujo sensible a una pulsacin de un botn del ratn y lo conectan al mtodo close_application() .
9.7. Reglas
Los controles Ruler (Regla) se usan para indicar la posicin del puntero del ratn en una ventana determinada. Una
ventana puede tener una regla vertical a lo largo del ancho y una regla horizontal a lo largo del alto. Un pequeo
tringulo indicador en la regla muestra la posicin exacta del puntero respecto a la regla.
Antes de nada es necesario crear una regla. Las reglas horizontales y verticales se crean usando las siguientes funciones:
hruler = gtk.HRuler()
# regla horizontal
vruler = gtk.VRuler()
# regla vertical
78
Una vez que se crea una regla podemos definir la unidad de medida. Las unidades de medida para las reglas pueden ser
PIXELS (pxeles), INCHES (pulgadas) o CENTIMETERS (centmetros). Esto se fija con el mtodo:
ruler.set_metric(metric)
La medida predeterminada es PIXELS.
ruler.set_metric(gtk.PIXELS)
Otra caracterstica importante de una regla es cmo marca las unidades de escala y donde se coloca el indicador de
posicin inicialmente. Esto se fija usando el mtodo:
ruler.set_range(lower, upper, position, max_size)
Los argumentos lower (bajo) y upper (alto) definen la extensin de la regla, y max_size (tamao mximo) es el
mayor nmero posible que se visualizar. La Position (Posicin) define la posicin inicial del indicador del puntero
dentro de la regla.
Una regla vertical puede medir una ventana de 800 pxeles de ancho as:
vruler.set_range(0, 800, 0, 800)
Las marcas mostradas en la regla irn desde 0 a 800, con un nmero cada 100 pxeles. Si en lugar de eso quisieramos
una regla de 7 a 16, escribiramos:
vruler.set_range(7, 16, 0, 20)
El indicador de la regla es una pequea marca triangular que indica la posicin del puntero relativa a la regla. Si la regla
se usa para seguir el puntero del ratn, la seal "motion_notify_event" debe conectarse al mtodo "motion_notify_event"
de la regla. Hay que configurar una retrollamada para "motion_notify_event" para el rea y usar connect_object()
para que la regla emita una seal "motion_notify_signal":
def motion_notify(ruler, event):
return ruler.emit("motion_notify_event", event)
area.connect_object("motion_notify_event", motion_notify, ruler)
El programa de ejemplo rulers.py crea un rea de dibujo con una regla horizontal en la parte de arriba y una regla
vertical a su izquierda. El tamao del rea de dibujo es de 600 pxeles de ancho por 400 pxeles de alto. La regla
horizontal va desde 7 hasta 13 con una marca cada 100 pxeles, mientras que la regla vertical va de 0 a 400 con una
marca cada 100 pxeles. La colocacin del rea de dibujo y las reglas se hace con una tabla. La figura Figura 9.8,
Ejemplo de Reglas ilustra el resultado:
Figura 9.8. Ejemplo de Reglas
79
#!/usr/bin/env python
# ejemplo rulers.py
import pygtk
pygtk.require('2.0')
import gtk
class RulersExample:
XSIZE = 400
YSIZE = 400
# Esta rutina toma el control cuando se pulsa el botn de cerrar
def close_application(self, widget, event, data=None):
gtk.main_quit()
return gtk.FALSE
def __init__(self):
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.connect("delete_event", self.close_application)
window.set_border_width(10)
# Crea una tabla para colocar la regla y el rea de dibujo
table = gtk.Table(3, 2, gtk.FALSE)
window.add(table)
area = gtk.DrawingArea()
area.set_size_request(self.XSIZE, self.YSIZE)
80
29
30
31
32
33
34
table.attach(area, 1, 2, 1, 2,
gtk.EXPAND|gtk.FILL, gtk.FILL, 0, 0 )
area.set_events(gtk.gdk.POINTER_MOTION_MASK |
gtk.gdk.POINTER_MOTION_HINT_MASK )
# La regla horizontal est arriba. Cuando el ratn se mueve por
el
35
# rea de dibujo se pasa un evento motion_notify_event al
manejador
36
# adecuado para la regla
37
hrule = gtk.HRuler()
38
hrule.set_metric(gtk.PIXELS)
39
hrule.set_range(7, 13, 0, 20)
40
def motion_notify(ruler, event):
41
return ruler.emit("motion_notify_event", event)
42
area.connect_object("motion_notify_event", motion_notify, hrule)
43
table.attach(hrule, 1, 2, 0, 1,
44
gtk.EXPAND|gtk.SHRINK|gtk.FILL, gtk.FILL, 0, 0 )
45
46
# La regla vertical est a la izquierda. Cuando el ratn se mueve
por el
47
# rea de dibujo se pasa un evento motion_notify_event al
manejador
48
# adecuado para la regla
49
vrule = gtk.VRuler()
50
vrule.set_metric(gtk.PIXELS)
51
vrule.set_range(0, self.YSIZE, 10, self.YSIZE)
52
area.connect_object("motion_notify_event", motion_notify, vrule)
53
table.attach(vrule, 0, 1, 1, 2,
54
gtk.FILL, gtk.EXPAND|gtk.SHRINK|gtk.FILL, 0, 0 )
55
56
# Ahora mostramos todo
57
area.show()
58
hrule.show()
59
vrule.show()
60
table.show()
61
window.show()
62
63 def main():
64
gtk.main()
65
return 0
66
67 if __name__ == "__main__":
68
RulersExample()
69
main()
Las lineas 42 y 52 conectan la retrollamada motion_notify() al rea pasandole hrule en la linea 42 y vrule en la
lnea 52 como datos de usuario. La retrollamada motion_notify() se llamar dos veces cada vez que el ratn se
mueva - una vez con hrule y otra vez con vrule.
9.8. Barras de Estado
Las Statusbar (Barras de Estado) son unos controles simples que se usan para visualizar un mensaje de texto.
Mantienen una pila de los mensajes que se les han enviado, para que al quitar el mensaje actual se visualice el mensaje
anterior.
Para que distintas partes de la aplicacin puedan usar la misma barra de estado para visualizar mensajes, el control de
barra de estado mantiene Identificadores de Contexto que se usan para identificar diferentes "usuarios". El mensaje en el
81
tope de la pila es el que se visualiza, no importa el contexto al que pertenezca. Los mensajes se apilan en orden ltimo
en llegar primero en salir, no en orden de identificadores de contexto.
Una barra de estado se crea con una llamada a:
statusbar = gtk.Statusbar()
Se puede solicitar un nuevo Identificador de Contexto usando una llamada al siguiente mtodo con una pequea
descripcin textual del contexto:
context_id = statusbar.get_context_id(context_description)
Hay tres mtodos adicionales para utilizar las barras de estado:
message_id = statusbar.push(context_id, text)
statusbar.pop(context_id)
statusbar.remove(context_id, message_id)
El primero, push(), se usa para aadir un nuevo mensaje a la statusbar (barra de estado). Devuelve un
message_id (identificador de mensaje), que puede usarse con el mtodo remove() para borrar el mensaje que
cumpla la combinacin de message_id y context_id en la pila de la statusbar (barra de estado).
El mtodo pop() elimina el mensaje que est en la posicin ms alta de la pila con el identificador de contexto
context_id.
El programa de ejemplo statusbar.py crea una barra de estado y dos botones, uno para insertar elementos en la barra de
estado, y otro para sacar el ltimo elemento fuera. La figura Figura 9.9, Ejemplo de Barra de Estado muestra el
resultado:
Figura 9.9. Ejemplo de Barra de Estado
#!/usr/bin/env python
# ejemplo statusbar.py
import pygtk
pygtk.require('2.0')
import gtk
class StatusbarExample:
def push_item(self, widget, data):
buff = " Item %d" % self.count
self.count = self.count + 1
self.status_bar.push(data, buff)
82
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
return
def pop_item(self, widget, data):
self.status_bar.pop(data)
return
def __init__(self):
self.count = 1
# crea una ventana nueva
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_size_request(200, 100)
window.set_title("PyGTK Statusbar Example")
window.connect("delete_event", lambda w,e: gtk.main_quit())
vbox = gtk.VBox(gtk.FALSE, 1)
window.add(vbox)
vbox.show()
self.status_bar = gtk.Statusbar()
vbox.pack_start(self.status_bar, gtk.TRUE, gtk.TRUE, 0)
self.status_bar.show()
context_id = self.status_bar.get_context_id("Statusbar example")
button = gtk.Button("push item")
button.connect("clicked", self.push_item, context_id)
vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 2)
button.show()
button = gtk.Button("pop last item")
button.connect("clicked", self.pop_item, context_id)
vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 2)
button.show()
# siempre mostramos la ventana al final para que se muestre
# de una vez en pantalla.
window.show()
def main():
gtk.main()
return 0
if __name__ == "__main__":
StatusbarExample()
main()
83
84
#!/usr/bin/env python
# ejemplo entry.py
import pygtk
pygtk.require('2.0')
import gtk
class EntryExample:
def enter_callback(self, widget, entry):
entry_text = entry.get_text()
print "Entry contents: %s\n" % entry_text
def entry_toggle_editable(self, checkbutton, entry):
entry.set_editable(checkbutton.get_active())
def entry_toggle_visibility(self, checkbutton, entry):
entry.set_visibility(checkbutton.get_active())
def __init__(self):
# create a new window
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_size_request(200, 100)
window.set_title("GTK Entry")
window.connect("delete_event", lambda w,e: gtk.main_quit())
vbox = gtk.VBox(gtk.FALSE, 0)
window.add(vbox)
vbox.show()
entry = gtk.Entry()
entry.set_max_length(50)
entry.connect("activate", self.enter_callback, entry)
entry.set_text("hello")
entry.insert_text(" world", len(entry.get_text()))
entry.select_region(0, len(entry.get_text()))
vbox.pack_start(entry, gtk.TRUE, gtk.TRUE, 0)
entry.show()
hbox = gtk.HBox(gtk.FALSE, 0)
vbox.add(hbox)
hbox.show()
check = gtk.CheckButton("Editable")
hbox.pack_start(check, gtk.TRUE, gtk.TRUE, 0)
check.connect("toggled", self.entry_toggle_editable, entry)
check.set_active(gtk.TRUE)
check.show()
check = gtk.CheckButton("Visible")
hbox.pack_start(check, gtk.TRUE, gtk.TRUE, 0)
check.connect("toggled", self.entry_toggle_visibility, entry)
check.set_active(gtk.TRUE)
check.show()
button = gtk.Button(stock=gtk.STOCK_CLOSE)
button.connect("clicked", lambda w: gtk.main_quit())
vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 0)
85
59
60
61
62
63
64
65
66
67
68
69
70
button.set_flags(gtk.CAN_DEFAULT)
button.grab_default()
button.show()
window.show()
def main():
gtk.main()
return 0
if __name__ == "__main__":
EntryExample()
main()
lower
upper
step_increment
page_increment
page_size
no se usa
Adicionalmente, el botn del ratn botn-3 se puede usar para saltar directamente a los valores upper (superior) y
lower (inferior) cuando se usa para seleccionar uno de los botones. Veamos como crear un SpinButton (Botn
Aumentar/Disminuir):
spin_button = gtk.SpinButton(adjustment=None, climb_rate=0.0, digits=0)
El argumento climb_rate (razn de escalada) puede tomar un valor entre 0.0 y 1.0 e indica la cantidad de aceleracin
que el SpinButton tiene. El argumento digits especifica el nmero de cifras decimales que se mostrarn.
Un SpinButton se puede reconfigurar despus de su creacin usando el siguiente mtodo:
spin_button.configure(adjustment, climb_rate, digits)
86
La variable spin_button especifica el botn aumentar/disminuir que se va a reconfigurar. Los otros argumentos son
los mismos que antes.
El adjustment (ajuste) se puede fijar y recupar independientemente usando los siguientes dos mtodos:
spin_button.set_adjustment(adjustment)
adjustment = spin_button.get_adjustment()
El nmero de cifras decimales tambin se puede cambiar usando:
spin_button.set_digits(digits)
El valor que un SpinButton est mostrando actualmente se puede cambiar usando el siguiente mtodo:
spin_button.set_value(value)
El valor actual de un SpinButton se puede recuperar como un valor real o como un valor entero usando los siguientes
mtodos:
float_value = spin_button.get_value()
int_value = spin_button.get_value_as_int()
Si quieres alterar el valor de un SpinButton relativo a su valor actual, entonces usa el siguiente mtodo:
spin_button.spin(direction, increment)
El parmetro direction (direccin) puede tomar uno de los siguientes valores:
SPIN_STEP_FORWARD
SPIN_STEP_BACKWARD
SPIN_PAGE_FORWARD
SPIN_PAGE_BACKWARD
SPIN_HOME
SPIN_END
SPIN_USER_DEFINED
#
#
#
#
#
#
#
Este mtodo comprime bastante funcionalidad, que explicaremos ahora. Muchos de estos parmetros usan valores del
objeto Adjustment (Ajuste) que est asociado con un SpinButton (Botn Aumentar/Disminuir).
SPIN_STEP_FORWARD (paso adelante) y SPIN_STEP_BACKWARD (paso atrs) cambian el valor del SpinButton
con una cantidad especificada por el increment (incremento), a menos que increment (incremento) sea igual a 0,
en cuyo caso el valor se modifica con el step_increment (incremento de paso) del Adjustment.
SPIN_PAGE_FORWARD (pgina adelante) y SPIN_PAGE_BACKWARD (pgina atrs) simplemente alteran el valor del
SpinButton por increment (incremento).
SPIN_HOME (inicio) pone el valor del SpinButton a la parte de abajo del rango del Adjustment .
SPIN_END (fin) fija el valor del SpinButton a la parte de arriba del rango del Adjustment .
SPIN_USER_DEFINED (definido por el usuario) simplemente modifica el valor del SpinButton con la cantidad
especificada.
87
Ahora nos alejamos de los mtodos para fijar y recuperar los atributos del rango de un SpinButton, y nos centramos
en los mtodos que modifican la apariencia y comportamiento del propio control SpinButton.
El primero de estos mtodos se usa para limitar la caja de texto del SpinButton para que solo contenga un valor
numrico. Esto evita que el usuario escriba cualquier otra cosa que no sea un valor numrico dentro de la caja de texto
de un SpinButton:
spin_button.set_numeric(numeric)
El argumento numeric es TRUE para limitar la caja de texto a valores numricos o FALSE para quitar esta limitacin.
Puedes fijar si quieres que el valor del SpinButton se quede en los valores inferior y superior del rango con el
siguiente mtodo:
spin_button.set_wrap(wrap)
El SpinButton limitar los valores dentro del rango si wrap es TRUE.
Puedes hacer que el SpinButton redondee el valor al step_increment (incremento) ms cercano, lo cual se fija
dentro del objeto Adjustment usado en el SpinButton. Esto se consigue con el siguiente mtodo cuando el
argumento snap_to_ticks es TRUE :
spin_button.set_snap_to_ticks(snap_to_ticks)
La poltica de actualizacin de un SpinButton se cambia con el siguiente mtodo:
spin_button.set_update_policy(policy)
Los valores posibles de esta poltica son:
UPDATE_ALWAYS
# actualizar siempre
88
#!/usr/bin/env python
# ejemplo spinbutton.py
import pygtk
pygtk.require('2.0')
import gtk
class SpinButtonExample:
def toggle_snap(self, widget, spin):
spin.set_snap_to_ticks(widget.get_active())
def toggle_numeric(self, widget, spin):
spin.set_numeric(widget.get_active())
def change_digits(self, widget, spin, spin1):
spin1.set_digits(spin.get_value_as_int())
def get_value(self, widget, data, spin, spin2, label):
if data == 1:
buf = "%d" % spin.get_value_as_int()
else:
buf = "%0.*f" % (spin2.get_value_as_int(),
spin.get_value())
label.set_text(buf)
def __init__(self):
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.connect("destroy", lambda w: gtk.main_quit())
window.set_title("Spin Button")
main_vbox = gtk.VBox(gtk.FALSE, 5)
main_vbox.set_border_width(10)
window.add(main_vbox)
frame = gtk.Frame("Not accelerated")
main_vbox.pack_start(frame, gtk.TRUE, gtk.TRUE, 0)
89
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
vbox = gtk.VBox(gtk.FALSE, 0)
vbox.set_border_width(5)
frame.add(vbox)
# Botones de aumentar/disminuir da, mes y ao
hbox = gtk.HBox(gtk.FALSE, 0)
vbox.pack_start(hbox, gtk.TRUE, gtk.TRUE, 5)
vbox2 = gtk.VBox(gtk.FALSE, 0)
hbox.pack_start(vbox2, gtk.TRUE, gtk.TRUE, 5)
label = gtk.Label("Day :")
label.set_alignment(0, 0.5)
vbox2.pack_start(label, gtk.FALSE, gtk.TRUE, 0)
adj = gtk.Adjustment(1.0, 1.0, 31.0, 1.0, 5.0, 0.0)
spinner = gtk.SpinButton(adj, 0, 0)
spinner.set_wrap(gtk.TRUE)
vbox2.pack_start(spinner, gtk.FALSE, gtk.TRUE, 0)
vbox2 = gtk.VBox(gtk.FALSE, 0)
hbox.pack_start(vbox2, gtk.TRUE, gtk.TRUE, 5)
label = gtk.Label("Month :")
label.set_alignment(0, 0.5)
vbox2.pack_start(label, gtk.FALSE, gtk.TRUE, 0)
adj = gtk.Adjustment(1.0, 1.0, 12.0, 1.0, 5.0, 0.0)
spinner = gtk.SpinButton(adj, 0, 0)
spinner.set_wrap(gtk.TRUE)
vbox2.pack_start(spinner, gtk.FALSE, gtk.TRUE, 0)
vbox2 = gtk.VBox(gtk.FALSE, 0)
hbox.pack_start(vbox2, gtk.TRUE, gtk.TRUE, 5)
label = gtk.Label("Year :")
label.set_alignment(0, 0.5)
vbox2.pack_start(label, gtk.FALSE, gtk.TRUE, 0)
adj = gtk.Adjustment(1998.0, 0.0, 2100.0, 1.0, 100.0, 0.0)
spinner = gtk.SpinButton(adj, 0, 0)
spinner.set_wrap(gtk.FALSE)
spinner.set_size_request(55, -1)
vbox2.pack_start(spinner, gtk.FALSE, gtk.TRUE, 0)
frame = gtk.Frame("Accelerated")
main_vbox.pack_start(frame, gtk.TRUE, gtk.TRUE, 0)
vbox = gtk.VBox(gtk.FALSE, 0)
vbox.set_border_width(5)
frame.add(vbox)
hbox = gtk.HBox(gtk.FALSE, 0)
vbox.pack_start(hbox, gtk.FALSE, gtk.TRUE, 5)
vbox2 = gtk.VBox(gtk.FALSE, 0)
hbox.pack_start(vbox2, gtk.TRUE, gtk.TRUE, 5)
label = gtk.Label("Value :")
label.set_alignment(0, 0.5)
90
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
spinner1)
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
91
158
159
160
161
162
163
164
def main():
gtk.main()
return 0
if __name__ == "__main__":
SpinButtonExample()
main()
92
la siguiente entrada de la lista (arriba o abajo, segn la combinacin de tecla indique). Esto se hace buscando en la lista
el elemento correspondiente al valor actual de la entrada y seleccionando el elemento anterior/siguiente correspondiente.
Normalmente en una entrada las teclas de flecha se usan para cambiar el foco (tambin puedes hacer esto usando el
tabulador). Ten en cuenta que cuando el elemento actual es el ltimo de la lista y pulsas la tecla flecha abajo se cambia
el foco (lo mismo ocurre cuando estas en el primer elemento y pulsas la tecla flecha arriba).
Si el valor actual de la entrada no est en la lista, el mtodo set_use_arrows() se desactiva.
El mtodo set_use_arrows_always() , cuando val es TRUE, tambin permite al usuario el uso de las teclas de
flecha arriba/abajo para ciclar por las opciones de la lista desplegable, excepto que da la vuelta a los valores de la lista,
desactivando por completo el uso de las flechas arriba y abajo para cambiar el foco.
El mtodo set_case_sensitive() dice si GTK busca o no las entradas de una forma sensible a maysculas. Esto se
usa cuando se le pide al control Combo que busque un valor de la lista usando la entrada actual de la caja de texto. Este
completado puede producirse de forma sensible o insensible a maysculas, dependiendo de lo que le pasemos a este
mtodo. El control Combo tambin puede simplemente completar la entrada actual si el usuario pulsa la combinacin de
teclas MOD-1+Tab. MOD-1 normalmente corresponde a la tecla Alt, gracias a la utilidad xmodmap. Ten en cuenta, sin
embargo, que algunos manejadores de ventana tambin usan esta combinacin de teclas, lo que inutilizar su uso en
GTK.
Ahora que tenemos una lista desplegable, y que tiene la apariencia y el comportamiento que queremos, lo nico que nos
falta es la capacidad de obtener los datos de la lista desplegable. Esto es relativamente directo. La mayora del tiempo,
de lo nico que en necesario preocuparse es de obtener los datos de la entrada. La entrada es accesible simplemente
como combo.entry. Las dos cosas fundamentales que se querrn hacer con ella es conectarle la seal "activate", que
indica que el usuario ha pulsado la tecla Return o la tecla Enter, y leer el texto. Lo primero se consigue usando algo
como:
combo.entry.connect("activate", my_callback, my_data)
Obtener el texto en cualquier momento se consigue simplemente usando el siguiente mtodo:
string = combo.entry.get_text()
Eso es todo lo importante. Hay un mtodo:
combo.disable_activate()
que desactivar la seal "activate" en el control de entrada de la lista desplegable. Personalmente, no se me ocurre
ninguna situacin en la que se quiera usar, pero existe.
9.12. Calendario
El control Calendar (Calendario) es una forma efectiva para visualizar y obtener informacin relativa a fechas
mensuales. Es un control muy fcil de usar y trabajar con l.
Crear un control GtkCalendar es tan simple como:
calendar = gtk.Calendar()
El calendario mostrar el mes y el ao actual de manera predeterminada.
Puede haber ocasiones en las que se necesite cambiar mucha informacin dentro de este control y los siguientes mtodos
permiten realizar mltiples cambios al control Calendar sin que el usuario vea muchos cambios en pantalla.
calendar.freeze() # congelar
calendar.thaw()
# reanudar
93
Funcionan exactamente igual que los mtodos freeze/thaw de cualquier otro control (desactivando los cambios y
reanudndolos).
El control Calendar tiene unas cuantas opciones que permiten cambiar la manera en la que el control se visualiza y se
comporta usando el mtodo:
calendar.display_options(flags)
El argumento flags (banderas) se puede formar combinando cualquiera de las siguientes cinco opciones usando el
operador lgico (|):
CALENDAR_SHOW_HEADING
CALENDAR_SHOW_DAY_NAMES
esta opcin especifica que la descripcin de tres letras para cada da (Lun,
Mar, etc.) debe mostrarse.
CALENDAR_NO_MONTH_CHANGE
esta opcin dice que el usuario no podr cambiar el mes que se muestra.
Esto puede ser bueno si slo se necesita mostrar un mes en particular como
cuando se muestran 12 controles de calendario uno para cada mes dentro de
un mes en particular.
94
# da seleccionado
# mes anterior
next_month
# mes siguiente
prev_year
# ao anterior
next_year
# ao siguiente
Esto nos deja con la necesidad de poner todo ello junto en el programa de ejemplo calendar.py. La figura Figura 9.12,
Ejemplo de Calendario muestra el resultado del programa:
Figura 9.12. Ejemplo de Calendario
#!/usr/bin/env python
#
#
#
#
#
#
#
#
#
#
#
#
ejemplo calendar.py
Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson, Mattias Gronlund
Copyright (C) 2000 Tony Gale
Copyright (C) 2001-2004 John Finlay
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
95
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
#
#
#
#
#
#
#
import pygtk
pygtk.require('2.0')
import gtk, pango
import time
class CalendarExample:
DEF_PAD = 10
DEF_PAD_SMALL = 5
TM_YEAR_BASE = 1900
calendar_show_header = 0
calendar_show_days = 1
calendar_month_change = 2
calendar_show_week = 3
def calendar_date_to_string(self):
year, month, day = self.window.get_date()
mytime = time.mktime((year, month+1, day, 0, 0, 0, 0, 0, -1))
return time.strftime("%x", time.localtime(mytime))
def calendar_set_signal_strings(self, sig_str):
prev_sig = self.prev_sig.get()
self.prev2_sig.set_text(prev_sig)
prev_sig = self.last_sig.get()
self.prev_sig.set_text(prev_sig)
self.last_sig.set_text(sig_str)
def calendar_month_changed(self, widget):
buffer = "month_changed: %s" % self.calendar_date_to_string()
self.calendar_set_signal_strings(buffer)
def calendar_day_selected(self, widget):
buffer = "day_selected: %s" % self.calendar_date_to_string()
self.calendar_set_signal_strings(buffer)
def calendar_day_selected_double_click(self, widget):
buffer = "day_selected_double_click: %s"
buffer = buffer % self.calendar_date_to_string()
self.calendar_set_signal_strings(buffer)
year, month, day = self.window.get_date()
if self.marked_date[day-1] == 0:
self.window.mark_day(day)
self.marked_date[day-1] = 1
else:
self.window.unmark_day(day)
self.marked_date[day-1] = 0
def calendar_prev_month(self, widget):
buffer = "prev_month: %s" % self.calendar_date_to_string()
96
75
76
77
def
78
79
80
81
def
82
83
84
85
def
86
87
88
89
def
90
91
92
93
94
95
96
97
def
98
99
100
101
102
103
104
105
106
def
107
108
109
110
111
112
113
def
114
115
116
117
118
119
120
121
122
123
124
125
wid.destroy(),
126
127
128
129
130
131
132
133
self.calendar_set_signal_strings(buffer)
calendar_next_month(self, widget):
buffer = "next_month: %s" % self.calendar_date_to_string()
self.calendar_set_signal_strings(buffer)
calendar_prev_year(self, widget):
buffer = "prev_year: %s" % self.calendar_date_to_string()
self.calendar_set_signal_strings(buffer)
calendar_next_year(self, widget):
buffer = "next_year: %s" % self.calendar_date_to_string()
self.calendar_set_signal_strings(buffer)
calendar_set_flags(self):
options = 0
for i in range(5):
if self.settings[i]:
options = options + (1<<i)
if self.window:
self.window.display_options(options)
calendar_toggle_flag(self, toggle):
j = 0
for i in range(5):
if self.flag_checkboxes[i] == toggle:
j = i
self.settings[j] = not self.settings[j]
self.calendar_set_flags()
calendar_font_selection_ok(self, button):
self.font = self.font_dialog.get_font_name()
if self.window:
font_desc = pango.FontDescription(self.font)
if font_desc:
self.window.modify_font(font_desc)
calendar_select_font(self, button):
if not self.font_dialog:
window = gtk.FontSelectionDialog("Font Selection Dialog")
self.font_dialog = window
window.set_position(gtk.WIN_POS_MOUSE)
window.connect("destroy", self.font_dialog_destroyed)
window.ok_button.connect("clicked",
self.calendar_font_selection_ok)
window.cancel_button.connect_object("clicked",
lambda wid:
self.font_dialog)
window = self.font_dialog
if not (window.flags() & gtk.VISIBLE):
window.show()
else:
window.destroy()
self.font_dialog = None
97
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
fuente.
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
98
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
99
253
254
255
256
if __name__ == "__main__":
CalendarExample()
main()
100
El programa de ejemplo colorsel.py demuestra el uso del ColorSelectionDialog. Este programa muestra una
ventana que contiene un rea de dibujo. Al hacer clic en ella se abre un dilogo de seleccin de color, y cambiando el
color en dicho dilogo se cambia el color de fondo. La figura Figura 9.13, Ejemplo de Dilogo de Seleccin de Color
muestra el programa en accin:
Figura 9.13. Ejemplo de Dilogo de Seleccin de Color
#!/usr/bin/env python
# ejemplo colorsel.py
import pygtk
pygtk.require('2.0')
import gtk
class ColorSelectionExample:
# manejador de cambio de Color
def color_changed_cb(self, widget):
# Obtenemos el mapa de color del rea de dibujo
colormap = self.drawingarea.get_colormap()
# Obtenemos el color actual
color = self.colorseldlg.colorsel.get_current_color()
101
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
salir
68
69
70
eventos de
71
72
73
65535, 0)
102
74
75
self.drawingarea.set_size_request(200, 200)
76
self.drawingarea.set_events(gtk.gdk.BUTTON_PRESS_MASK)
77
self.drawingarea.connect("event", self.area_event)
78
79
# Aadimos el rea de dibujo a la ventana y mostramos ambos
controles
80
window.add(self.drawingarea)
81
self.drawingarea.show()
82
window.show()
83
84 def main():
85
gtk.main()
86
return 0
87
88 if __name__ == "__main__":
89
ColorSelectionExample()
90
main()
9.14. Selectores de Fichero
El control de seleccin de fichero es una forma rpida y fcil de mostrar una caja de dilogo de Fichero. Viene con
botones Ok, Cancelar y Ayuda, por lo que es estupendo para ahorrase tiempo de programacin.
Para crear una nueva caja de seleccin de fichero se usa:
filesel = gtk.FileSelection(title=None)
Para fijar el nombre de fichero, por ejemplo para mostrar un directorio especfico, o establecer un fichero
predeterminado, usa este mtodo:
filesel.set_filename(filename)
Para obtener el nombre de fichero que el usuario ha escrito o seleccionado, se usa este mtodo:
filename = filesel.get_filename()
Tambin hay referencias a los controles contenidos en el control de seleccin de ficheros. Estos son los atributos:
filesel.dir_list
# lista de directorios
filesel.file_list
# lista de ficheros
filesel.selection_entry # entrada de seleccin
filesel.selection_text
# texto de seleccin
filesel.main_vbox
# caja vertical principal
filesel.ok_button
# botn ok
filesel.cancel_button
# botn cancelar
filesel.help_button
# botn ayuda
filesel.history_pulldown # lista de historia
filesel.history_menu
# men de historia
filesel.fileop_dialog # dilogo
filesel.fileop_entry # entrada
filesel.fileop_file # fichero
filesel.fileop_c_dir # cambio de directorio
filesel.fileop_del_file # borrar fichero
filesel.fileop_ren_file # renombrar fichero
filesel.button_area
# rea de botones
filesel.action_area
# rea de accin
103
Lo ms probable es que se quieran usar los atributos ok_button, cancel_button, y help_button para conectar
sus seales a las retrollamadas.
El programa de ejemplo filesel.py ilustra el uso del control de seleccin de ficheros. Como se puede ver, no hay mucho
ms que decir para crear un control de seleccin de ficheros. Aunque en este ejemplo el botn Ayuda aparece en
pantalla, no hace nada porque no hay ninguna seal conectada a l. La figura Figura 9.14, Ejemplo de Seleccin de
Ficheros muestra la pantalla resultante:
Figura 9.14. Ejemplo de Seleccin de Ficheros
104
23
self.filew.ok_button.connect("clicked", self.file_ok_sel)
24
25
# Conectar cancel_button para destruir el control
26
self.filew.cancel_button.connect("clicked",
27
lambda w: self.filew.destroy())
28
29
# Fijamos el nombre de fichero, como si fuese un dilogo de
guardado,
30
# y damos un nombre por defecto
31
self.filew.set_filename("penguin.png")
32
33
self.filew.show()
34
35 def main():
36
gtk.main()
37
return 0
38
39 if __name__ == "__main__":
40
FileSelectionExample()
41
main()
9.15. Dilogo de Seleccin de Fuentes
El Dilogo de Seleccin de Fuentes permite al usuario seleccionar una fuente de forma interactiva. El dilogo contiene
un control FontSelection y botones de OK y Cancelar. Un botn de Aplicar tambin est disponible en el dilogo,
pero inicialmente est oculto. El Dilogo de Seleccin de Fuentes permite al usuario seleccionar una fuente de las
fuentes de sistema disponibles (las mismas que se obtienen al usar xlsfonts).
La figura Figura 9.15, Dilogo de Seleccin de Fuentes ilustra un FontSelectionDialog :
Figura 9.15. Dilogo de Seleccin de Fuentes
105
106
#!/usr/bin/env python
# ejemplo eventbox.py
import pygtk
pygtk.require('2.0')
import gtk
107
9 class EventBoxExample:
10
def __init__(self):
11
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
12
window.set_title("Event Box")
13
window.connect("destroy", lambda w: gtk.main_quit())
14
window.set_border_width(10)
15
16
# Creamos una EventBox y la aadimos a la ventana principal
17
event_box = gtk.EventBox()
18
window.add(event_box)
19
event_box.show()
20
21
# Creamos una etiqueta larga
22
label = gtk.Label("Click here to quit, quit, quit, quit, quit")
23
event_box.add(label)
24
label.show()
25
26
# La recortamos
27
label.set_size_request(110, 20)
28
29
# Y conectamos una accin a la misma
30
event_box.set_events(gtk.gdk.BUTTON_PRESS_MASK)
31
event_box.connect("button_press_event", lambda w,e:
gtk.main_quit())
32
33
# Ms cosas para las que se necesita una ventana de X ...
34
event_box.realize()
35
event_box.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND1))
36
37
# Poner el fondo en verde
38
event_box.modify_bg(gtk.STATE_NORMAL,
39
event_box.get_colormap().alloc_color("green"))
40
41
window.show()
42
43 def main():
44
gtk.main()
45
return 0
46
47 if __name__ == "__main__":
48
EventBoxExample()
49
main()
10.2. El control Alineador
El control Alignment (Alineador) permite colocar un control dentro de su ventana con una posicin y un tamao
relativos al tamao del propio control Alignment. Por ejemplo, puede ser til para centrar un control dentro de la
ventana.
Slo hay dos llamadas asociadas al control Alignment:
alignment = gtk.Alignment(xalign=0.0, yalign=0.0, xscale=0.0, yscale=0.0)
alignment.set(xalign, yalign, xscale, yscale)
La funcin gtk.Alignment() crea un nuevo control Alignment con los parmetros especificados. El mtodo
set() permite alterar los parmetros de alineacin de un control Alignment existente.
108
Los cuatro parmetros son nmeros en coma flotante que pueden estar entre 0.0 y 1.0. Los argumentos xalign y
yalign afectan a la posicin del control dentro del Alignment. Las propiedades de alineacin especifican la fraccin
de espacio libre que se colocar por encima o a la izquierda del control hijo. Sus valoren van de 0.0 (sin espacio libre
por encima o a la izquierda del hijo) a 1.0 (todo espacio libre o a la izquierda del hijo). Naturalmente, si las dos
propiedades de escala estn puestas a 1.0, entonces las propiedades de alineacin no tienen efecto, puesto que el control
hijo se expandir para llenar el espacio disponible.
Los argumentos xscale e yscale especifican la fraccin de espacio libre absorbido por el control hijo. Los valores
pueden variar desde 0.0 (el hijo no absorbe nada) hasta 1.0 (el hijo toma todo el espacio libre.
Un control hijo puede aadirse a este Alignment usando:
alignment.add(widget)
Para un ejemplo del uso de un control Alignment, consulte el ejemplo del control de Barra de Progreso
progressbar.py
10.3. Contenedor Fijo (Fixed)
El contenedor Fixed (Fijo) permite situar controles en una posicin fija dentro de su ventana, relativa a su esquina
superior izquierda. La posicin de los controles se puede cambiar dinmicamente.
Slo hay dos llamadas asociadas al control fijo:
fixed = gtk.Fixed()
fixed.put(widget, x, y)
fixed.move(widget, x, y)
La funcin gtk.Fixed() permite crear un nuevo contenedor Fixed.
El mtodo put() coloca al control en el contenedor fijo en la posicin especificada por x e y.
El mtodo move() te permite mover el control especificado a una nueva posicin.
El ejemplo fixed.py ilustra cmo usar el contenedor Fixed . La figura Figura 10.2, Ejemplo de Fijo muestra el
resultado:
Figura 10.2. Ejemplo de Fijo
109
#!/usr/bin/env python
# ejemplo fixed.py
import pygtk
pygtk.require('2.0')
import gtk
class FixedExample:
# Esta retrollamada mueve el botn a una nueva posicin
# en el contenedor Fixed.
def move_button(self, widget):
self.x = (self.x+30)%300
self.y = (self.y+50)%300
self.fixed.move(widget, self.x, self.y)
def __init__(self):
self.x = 50
self.y = 50
# Creamos una nueva ventana
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_title("Fixed Container")
# Conectamos el evento "destroy" a un manejador de seales
window.connect("destroy", lambda w: gtk.main_quit())
# Fija el grosor del borde de ventana
window.set_border_width(10)
# Creamos un contenedor Fixed
self.fixed = gtk.Fixed()
window.add(self.fixed)
self.fixed.show()
for i in range(1, 4):
# Crea un nuevo ton con la etiqueta "Press me"
110
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
111
layout.set_hadjustment(adjustment)
layout.set_vadjustment(adjustment)
El programa de ejemplo layout.py crea tres botones y los pone en un control disposicin. Cuando se hace clic en un
botn, se mueve a una posicin aleatoria en el control disposicin. La figura Figura 10.3, Ejemplo de Disposicin
ilustra la ventana inicial del programa:
Figura 10.3. Ejemplo de Disposicin
#!/usr/bin/env python
# ejemplo layout.py
import pygtk
pygtk.require('2.0')
import gtk
import random
class LayoutExample:
def WindowDeleteEvent(self, widget, event):
# devolvemos false para que se destruya la ventana
return gtk.FALSE
def WindowDestroy(self, widget, *data):
# salimos del bucle principal
gtk.main_quit()
def ButtonClicked(self, button):
# movemos el botn
self.layout.move(button, random.randint(0,500),
random.randint(0,500))
def __init__(self):
# creamos la ventana principal
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
112
27
window.set_title("Layout Example")
28
window.set_default_size(300, 300)
29
window.connect("delete-event", self.WindowDeleteEvent)
30
window.connect("destroy", self.WindowDestroy)
31
# creamos la tabla y la empaquetamos en la ventana
32
table = gtk.Table(2, 2, gtk.FALSE)
33
window.add(table)
34
# creamos el control disposicin y lo empaquetamos en la tabla
35
self.layout = gtk.Layout(None, None)
36
self.layout.set_size(600, 600)
37
table.attach(self.layout, 0, 1, 0, 1, gtk.FILL|gtk.EXPAND,
38
gtk.FILL|gtk.EXPAND, 0, 0)
39
# creamos las barras de desplazamiento y las empaquetamos tambin
40
vScrollbar = gtk.VScrollbar(None)
41
table.attach(vScrollbar, 1, 2, 0, 1, gtk.FILL|gtk.SHRINK,
42
gtk.FILL|gtk.SHRINK, 0, 0)
43
hScrollbar = gtk.HScrollbar(None)
44
table.attach(hScrollbar, 0, 1, 1, 2, gtk.FILL|gtk.SHRINK,
45
gtk.FILL|gtk.SHRINK, 0, 0)
46
# indicamos a las barras de desp. que usen el ajuste del control
disposicin
47
vAdjust = self.layout.get_vadjustment()
48
vScrollbar.set_adjustment(vAdjust)
49
hAdjust = self.layout.get_hadjustment()
50
hScrollbar.set_adjustment(hAdjust)
51
# creamos 3 botones ylos ponemos en el control disposicin
52
button = gtk.Button("Press Me")
53
button.connect("clicked", self.ButtonClicked)
54
self.layout.put(button, 0, 0)
55
button = gtk.Button("Press Me")
56
button.connect("clicked", self.ButtonClicked)
57
self.layout.put(button, 100, 0)
58
button = gtk.Button("Press Me")
59
button.connect("clicked", self.ButtonClicked)
60
self.layout.put(button, 200, 0)
61
# mostramos todos los controles
62
window.show_all()
63
64 def main():
65
# entramos en el bucle principal
66
gtk.main()
67
return 0
68
69 if __name__ == "__main__":
70
LayoutExample()
71
main()
10.5. Marcos (Frame)
Los Marcos se pueden usar para encerrar un widget o un grupo de ellos dentro de una caja que, opcionalmente, puede
llevar un ttulo. La posicin del ttulo y el estilo de la caja se puede alterar a gusto.
Un Frame (Marco) se puede crear con la siguiente funcin
frame = gtk.Frame(label=None)
El label (ttulo) se coloca en la esquina superior izquierda del marco de manera predeterminada. Especificando un
valor de None para el argumento label o sin especificar el argumento label har que no se visualice ningn ttulo.
El texto del ttulo se puede cambiar usando el mtodo:
113
frame.set_label(label)
La posicin del ttulo se puede cambiar usando el mtodo:
frame.set_label_align(xalign, yalign)
xalign y yalign toman valores entre 0.0 y 1.0. xalign indica la posicin del ttulo en la horizontal superior del
marco. yalign no se usa por ahora. El valor por defecto de xalign es 0.0 lo que coloca al ttulo en la esquina
izquierda del marco.
El siguiente mtodo modifica el estilo de la caja que se usa para rodear el marco.
frame.set_shadow_type(type)
El argumento type puede tomar uno de los siguientes valores:
SHADOW_NONE
SHADOW_IN
SHADOW_OUT
SHADOW_ETCHED_IN
SHADOW_ETCHED_OUT
#
#
#
#
#
sin sombra
sombra hacia dentro
sombra hacia fuera
sombra marcada hacia dentro (valor predeterminado)
sombra marcada hacia fuera
El ejemplo frame.py muestra el uso del control Marco. La figura Figura 10.4, Ejemplo de Marco muestra la ventana
resultante:
Figura 10.4. Ejemplo de Marco
#!/usr/bin/env python
# ejemplo frame.py
import pygtk
pygtk.require('2.0')
import gtk
114
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
class FrameExample:
def __init__(self):
# Creamos una ventana nueva
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_title("Frame Example")
# Conectamos el evento "destroy" al manejador de seal
window.connect("destroy", lambda w: gtk.main_quit())
window.set_size_request(300, 300)
# Fijamos el grosor del borde de ventana
window.set_border_width(10)
# Creamos un Marco
frame = gtk.Frame()
window.add(frame)
# Fijamos la etiqueta del marco
frame.set_label("GTK Frame Widget")
# Alineamos la etiqueta a la derecha del marco
frame.set_label_align(1.0, 0.0)
# Fijamos el estilo del marco
frame.set_shadow_type(gtk.SHADOW_ETCHED_OUT)
frame.show()
# Mostramos la ventana
window.show()
def main():
# Entramos en el bucle de eventos
gtk.main()
return 0
if __name__ == "__main__":
FrameExample()
main()
115
116
29
30
31
32
33
34
35
36
37
38
39
40
41
117
118
30
31
return scrolled_window
32
33
# Aadimos texto a nuestro control de texto - esta retrollamada se
invoca
34
# cuando se realiza nuestra ventana. Podramos forzar tambin la
realizacin
35
# mediante gtk.Widget.realize(), pero primero tendra que ser parte
36
# de una jerarqua
37
def insert_text(self, buffer):
38
iter = buffer.get_iter_at_offset(0)
39
buffer.insert(iter,
40
"From: [email protected]\n"
41
"To: [email protected]\n"
42
"Subject: Made it!\n"
43
"\n"
44
"We just got in this morning. The weather has
been\n"
45
"great - clear but cold, and there are lots of fun
sights.\n"
46
"Sojourner says hi. See you soon.\n"
47
" -Path\n")
48
49
# Creamos un rea de texto desplazable que muestra un "mensaje"
50
def create_text(self):
51
view = gtk.TextView()
52
buffer = view.get_buffer()
53
scrolled_window = gtk.ScrolledWindow()
54
scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,
gtk.POLICY_AUTOMATIC)
55
scrolled_window.add(view)
56
self.insert_text(buffer)
57
scrolled_window.show_all()
58
return scrolled_window
59
60
def __init__(self):
61
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
62
window.set_title("Paned Windows")
63
window.connect("destroy", lambda w: gtk.main_quit())
64
window.set_border_width(10)
65
window.set_size_request(450, 400)
66
67
# creamos un control vpaned y lo aadimos a la ventana principal
68
vpaned = gtk.VPaned()
69
window.add(vpaned)
70
vpaned.show()
71
72
# Ahora creamos los contenidos de las dos partes de la ventana
73
list = self.create_list()
74
vpaned.add1(list)
75
list.show()
76
77
text = self.create_text()
78
vpaned.add2(text)
79
text.show()
80
window.show()
81
82 def main():
83
gtk.main()
84
return 0
119
85
86
87
88
if __name__ == "__main__":
PanedExample()
main()
#
#
#
#
#
sin sombra
sombra hacia adentro
sombra hacia afuera
sombra marcada hacia adentro
sombra marcada hacia fuera
120
Este mtodo especifica la poltica a usar con respecto a las barras de desplazamiento. El primer argumento le fija la
poltica a la barra de desplazamiento horizontal, y el segundo, la poltica de la barra de desplazamiento vertical.
La poltica puede tomar los valores POLICY_AUTOMATIC o POLICY_ALWAYS. POLICY_AUTOMATIC decidir
automticamente si son necesarias las barras de desplazamiento, mientras que POLICY_ALWAYS siempre dejar
visibles las barras de desplazamiento.
Tras esto se puede colocar el objeto dentro de la ventana de desplazamiento usando el siguiente mtodo.
scrolled_window.add_with_viewport(child)
El programa de ejemplo scrolledwin.py coloca una tabla con 100 botones biestado dentro de una ventana de
desplazamiento. Slo se comentan las partes que pueden ser nuevas. La figura Figura 10.7, Ejemplo de Ventana de
Desplazamiento muestra la ventana del programa:
Figura 10.7. Ejemplo de Ventana de Desplazamiento
#!/usr/bin/env python
# ejemplo scrolledwin.py
import pygtk
pygtk.require('2.0')
import gtk
class ScrolledWindowExample:
def destroy(self, widget):
gtk.main_quit()
def __init__(self):
# Creamos una nueva ventana de dilogo en la que empaquetar
# la ventana de desplazamiento
window = gtk.Dialog()
window.connect("destroy", self.destroy)
window.set_title("ScrolledWindow example")
121
19
window.set_border_width(0)
20
window.set_size_request(300, 300)
21
22
# creamos una nueva ventana de desplazamiento
23
scrolled_window = gtk.ScrolledWindow()
24
scrolled_window.set_border_width(10)
25
26
# la poltica es bien POLICY AUTOMATIC, o bien POLICY_ALWAYS.
27
# POLICY_AUTOMATIC decidir automticamente la necesidad de
barras de
28
# desplazamiento, mientras que POLICY_ALWAYS dejar
permanentemente las
29
# barras. La primera es la barra de desplazamiento horizontal, la
segunda la
30
# vertical.
31
scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,
gtk.POLICY_ALWAYS)
32
33
# Se crea la ventana de dilogo con una vbox en ella
34
window.vbox.pack_start(scrolled_window, gtk.TRUE, gtk.TRUE, 0)
35
scrolled_window.show()
36
37
# creamos una tabla de 10 por 10 casillas
38
table = gtk.Table(10, 10, gtk.FALSE)
39
40
# fijamos el espaciado a 10 en x y 10 en y
41
table.set_row_spacings(10)
42
table.set_col_spacings(10)
43
44
# empaquetamos la tabla en la ventana de desplazamiento
45
scrolled_window.add_with_viewport(table)
46
table.show()
47
48
# esto simplemente crea una trama de botones biestado en la tabla
49
# para demostrar el uso de la ventana de desplazamiento
50
for i in range(10):
51
for j in range(10):
52
buffer = "button (%d,%d)" % (i, j)
53
button = gtk.ToggleButton(buffer)
54
table.attach(button, i, i+1, j, j+1)
55
button.show()
56
57
# Aadimos un botn "close" en la parte inferior del dilogo
58
button = gtk.Button("close")
59
button.connect_object("clicked", self.destroy, window)
60
61
# ponemos el botn como posible predeterminado
62
button.set_flags(gtk.CAN_DEFAULT)
63
window.action_area.pack_start( button, gtk.TRUE, gtk.TRUE, 0)
64
65
# Esto lo establece como predeterminado. Pulsando
66
# "Enter" har que se active este botn
67
button.grab_default()
68
button.show()
69
window.show()
70
71 def main():
72
gtk.main()
73
return 0
74
122
75
76
77
if __name__ == "__main__":
ScrolledWindowExample()
main()
Si se prueba a cambiar el tamao a la ventana se ver cmo reaccionan las barras de desplazamiento. Puede que tambin
se quiera usar el mtodo set_size_request() para fijar el tamao por defecto de la ventana o de otros controles.
10.10. Cajas de Botones (ButtonBoxes)
Las ButtonBoxes (Cajas de Botones) proporcionan un sistema fcil de agrupar botones rpidamente. Vienen en
variedades horizontales y verticales. Se puede crear una nueva ButtonBox con una de las siguiente llamadas, que
crean una caja horizontal o vertical, respectivamente:
hbutton_box = gtk.HButtonBox()
vbutton_box = gtk.VButtonBox()
Los nicos mtodos de las cajas de botones afectan a la disposicin de los botones.
La disposicin de los botones dentro de la caja se establece usando:
button_box.set_layout(layout_style)
El argumento layout_style puede tomar uno de los siguientes valores:
BUTTONBOX_DEFAULT_STYLE
BUTTONBOX_SPREAD
BUTTONBOX_EDGE
BUTTONBOX_START
BUTTONBOX_END
#
#
#
#
#
estilo predeterminado
esparcidos
al borde
al principio
al final
123
#!/usr/bin/env python
# ejemplo buttonbox.py
import pygtk
pygtk.require('2.0')
import gtk
class ButtonBoxExample:
# Creamos una Button Box con los parmetros especificados
def create_bbox(self, horizontal, title, spacing, layout):
frame = gtk.Frame(title)
if horizontal:
bbox = gtk.HButtonBox()
else:
bbox = gtk.VButtonBox()
bbox.set_border_width(5)
frame.add(bbox)
# Fijamos el aspecto de Button Box
124
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
79
80
81
82
bbox.set_layout(layout)
bbox.set_spacing(spacing)
button = gtk.Button(stock=gtk.STOCK_OK)
bbox.add(button)
button = gtk.Button(stock=gtk.STOCK_CANCEL)
bbox.add(button)
button = gtk.Button(stock=gtk.STOCK_HELP)
bbox.add(button)
return frame
def __init__(self):
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_title("Button Boxes")
window.connect("destroy", lambda x: gtk.main_quit())
window.set_border_width(10)
main_vbox = gtk.VBox(gtk.FALSE, 0)
window.add(main_vbox)
frame_horz = gtk.Frame("Horizontal Button Boxes")
main_vbox.pack_start(frame_horz, gtk.TRUE, gtk.TRUE, 10)
vbox = gtk.VBox(gtk.FALSE, 0)
vbox.set_border_width(10)
frame_horz.add(vbox)
vbox.pack_start(self.create_bbox(gtk.TRUE, "Spread (spacing 40)",
40, gtk.BUTTONBOX_SPREAD),
gtk.TRUE, gtk.TRUE, 0)
vbox.pack_start(self.create_bbox(gtk.TRUE, "Edge (spacing 30)",
30, gtk.BUTTONBOX_EDGE),
gtk.TRUE, gtk.TRUE, 5)
vbox.pack_start(self.create_bbox(gtk.TRUE, "Start (spacing 20)",
20, gtk.BUTTONBOX_START),
gtk.TRUE, gtk.TRUE, 5)
vbox.pack_start(self.create_bbox(gtk.TRUE, "End (spacing 10)",
10, gtk.BUTTONBOX_END),
gtk.TRUE, gtk.TRUE, 5)
frame_vert = gtk.Frame("Vertical Button Boxes")
main_vbox.pack_start(frame_vert, gtk.TRUE, gtk.TRUE, 10)
hbox = gtk.HBox(gtk.FALSE, 0)
hbox.set_border_width(10)
frame_vert.add(hbox)
hbox.pack_start(self.create_bbox(gtk.FALSE, "Spread (spacing 5)",
5, gtk.BUTTONBOX_SPREAD),
gtk.TRUE, gtk.TRUE, 0)
hbox.pack_start(self.create_bbox(gtk.FALSE, "Edge (spacing 30)",
125
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
30, gtk.BUTTONBOX_EDGE),
gtk.TRUE, gtk.TRUE, 5)
hbox.pack_start(self.create_bbox(gtk.FALSE, "Start (spacing 20)",
20, gtk.BUTTONBOX_START),
gtk.TRUE, gtk.TRUE, 5)
hbox.pack_start(self.create_bbox(gtk.FALSE, "End (spacing 20)",
20, gtk.BUTTONBOX_END),
gtk.TRUE, gtk.TRUE, 5)
window.show_all()
def main():
# Entramos en el bucle de eventos
gtk.main()
return 0
if __name__ == "__main__":
ButtonBoxExample()
main()
126
Si es necesario, la orientacin de una barra de herramientas, su estilo y el hecho de que las pistas estn disponibles, se
puede cambiar sobre la marcha usando los siguientes mtodos:
toolbar.set_orientation(orientation)
toolbar.set_style(style)
toolbar.set_tooltips(enable)
Donde la orientation puede ser ORIENTATION_HORIZONTAL u ORIENTATION_VERTICAL. El style se usa
para especificar la apariencia de la barra de herramientas y puede ser TOOLBAR_ICONS (iconos), TOOLBAR_TEXT
(texto), o TOOLBAR_BOTH (ambos). El argumento enable puede ser o TRUE o FALSE.
Para mostrar algunas otras cosas que se pueden hacer con una barra de herramientas veamos el siguiente programa de
ejemplo toolbar.py (interrumpiremos el listado con explicaciones adicionales):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/env python
# ejemplo toolbar.py
import pygtk
pygtk.require('2.0')
import gtk
class ToolbarExample:
# Este mtodo se conecta al botn Close o
# al cierre de la ventana desde el Gestor de Ventanas
def delete_event(self, widget, event=None):
gtk.main_quit()
return gtk.FALSE
Este principio debera resultar familiar si no es el primer programa PyGTK. No obstante hay una cosa ms, vamos a
importar un bonito dibujo XPM (gtk.xpm) para que nos sirva de icono para todos los botones. La linea 10 empieza la
clase de ejemplo ToolbarExample y las lineas 12-14 definen el mtodo de retrollamada que finalizar el programa.
16
# es fcil... cuando se conmuta uno de los botones, simplemente
17
# comprobamos cul est activo y cambiamos el estilo de la barra de
herramientas
18
# de acuerdo con ello
19
def radio_event(self, widget, toolbar):
20
if self.text_button.get_active():
21
toolbar.set_style(gtk.TOOLBAR_TEXT)
22
elif self.icon_button.get_active():
23
toolbar.set_style(gtk.TOOLBAR_ICONS)
24
elif self.both_button.get_active():
25
toolbar.set_style(gtk.TOOLBAR_BOTH)
26
27
# incluso ms fcil, comprobamos la conmutacin del botn
28
# y activamos/desactivamos las pistas
29
def toggle_event(self, widget, toolbar):
30
toolbar.set_tooltips(widget.get_active())
31
Las lineas 19-30 son dos mtodos de retrollamada que se llamarn cuando uno de los botones de la barra de
herramientas se active. Estas cosas resultan familiares si ya se han usado botones biestado (y botones de exclusin
mtua).
32
def __init__(self):
127
33
# Aqu est la ventana principal (un dilogo) y el asa
34
# necesitamos una barra de herramientas, un icono con mscara
(uno para todos
35
# los botones) y un control de icono en el que ponerlo (aunque
crearemos
36
# un control aparte para cada botn)
37
# creamos una nueva ventana con su ttulo y un tamao adecuado
38
dialog = gtk.Dialog()
39
dialog.set_title("GTKToolbar Tutorial")
40
dialog.set_size_request(450, 250)
41
dialog.set_resizable(gtk.TRUE)
42
43
# generalmente querremos salir si cierran la ventana
44
dialog.connect("delete_event", self.delete_event)
45
46
# para que tenga mejor aspecto ponemos la barra en la caja con
asa
47
# de manera que se pueda separar de la ventana principal
48
handlebox = gtk.HandleBox()
49
dialog.vbox.pack_start(handlebox, gtk.FALSE, gtk.FALSE, 5)
50
Lo anterior debera ser comn a cualquier otra aplicacin PyGTK. Inicializacin de una instancia de
ToolbarExample, creacin de la ventana, etc. Slo hay una cosa que probablemente necesite ms explicacin: una
caja con mango (asa) (HandleBox). Una caja con mango es simplemente otra caja cualquiera que puede usarse para
meter controles dentro. La diferencia entre ella y las cajas tpicas es que puede despegarse de la ventana padre (o, de
hecho, la caja con mango permanece en el padre, pero se reduce a un rectngulo muy pequeo, mientras que todos sus
contenidos cambian de padre a una nueva ventana flotante). Normalmente es bueno tener una barra de herramientas
separable, por lo que estos dos controles suelen ir juntos normalmente.
51
52
53
54
55
56
57
58
59
Bien, lo que hacemos arriba es una simple inicializacin del control de la barra de herramientas.
60
61
62
63
64
65
66
67
68
69
70
En el cdigo anterior se puede ver el caso ms simple: aadir un botn a la barra de herramientas. Justo antes de aadir
el nuevo elemento, tenemos que construir un control de imgen que servir como icono para este elemento; este paso
tiene que repetirse para cada nuevo elemento. Justo despus del elemento aadimos tambin espacio, para que los
128
elementos siguientes no se toquen unos a otros. Como se puede ver, el mtodo append_item() devuelve una
referencia al control de botn recin creado, para que se pueda trabajar con l de la forma habitual.
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
Aqui empezamos a crear un grupo de botones de exclusin mtua. Para hacer esto usamos el mtodo
append_element() . De hecho, usando este mtodo, uno puede tambin puede aadir items normales o incluso
espacios (type = gtk.TOOLBAR_CHILD_SPACE (espacio) o gtk.TOOLBAR_CHILD_BUTTON (botn)). En el
ejemplo anterior hemos empezado a crear un grupo de botones de exclusin mtua. Cuando se crean otros botones de
exclusin mtua se necesita una referencia al botn anterior en el grupo, para que se pueda construir una lista de botones
fcilmente (mira la seccin RadioButtons de este tutorial). Tambin tenemos una referencia al botn en la instancia
de ToolbarExample para acceder a l ms tare.
86
anteriores
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
129
Creamos otros botones de exclusin mtua de la misma forma excepto que le pasamos uno de los botones creados al
mtodo append_element() para especificar el grupo.
Al final tenemos que establecer el estado de uno de los botones manualmente (si no todos tienen el estado activo,
impidindonos que los vayamos intercambiando).
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
Un botn biestado se puede crear de la forma obvia (si ya se han creado botones de exclusin mtua alguna vez).
131
132
133
134
"Private")
135
136
mostrarla
137
138
Como se puede ver, aadir cualquier tipo de control a una barra de herramientas es fcil. La nica cosa que se tiene que
recordar es que este control debe mostrarse manualmente (al contrario que los elementos, que se mostrarn de golpe con
la barra de herramientas).
139
140
141
142
143
144
145
146
147
148
149
150
151
La linea 142 termina la definicin de la clase ToolbarExample. Las lineas 144-147 definen la funcin main() que
simplemente llama a la funcin gtk.main() para iniciar el bucle de procesamiento de eventos. Las lineas 149-151
crean una instancia de ToolbarExample y luego entran en el bucle de procesamiento de eventos. As termina el
tutorial de la barra de herramientas. Por supuesto, para apreciarla en su mximo esplendor se necesita tambin el icono
XPM, gtk.xpm. La figura Figura 10.8, Ejemplo de Barra de Herramientas muestar la ventana resultante:
130
#
#
#
#
izquierda
derecha
arriba
abajo
131
El ltimo mtodo para aadir una pgina a las fichas contiene todas las propiedades de los otros dos, y adems permite
especificar en qu posicin se insertar la pgina en las fichas.
notebook.insert_page(child, tab_label, position)
Los parmetros son los mismos que en append() y prepend() excepto en que contiene un parmetro extra,
position. Este parmetro se usa para especificar en qu lugar la pgina se insertar; la primera pgina est en la
posicin cero.
Ahora que sabemos cmo aadir una pgina, veamos cmo podemos borrar una pgina de las fichas.
notebook.remove_page(page_num)
Este mtodo borra la pgina especificada por page_num de las fichas contenidas en la variable notebook.
Para saber cul es la pgina actual de las fichas utiliza el mtodo:
page = notebook.get_current_page()
Los prximos dos mtodos son simples llamadas para mover la pgina hacia adelante o hacia atrs. Simplemente se
utilizan en el control de fichas sobre el que se quiera operar.
notebook.next_page()
notebook.prev_page()
Nota
Cuando el notebook tiene como pgina actual la ltima pgina, y se llama a next_page() , no
ocurre nada. De forma anloga, si el notebook est en la primera pgina, y se llama a prev_page(),
no pasa nada.
Este mtodo fija la pgina "activa". Si se quiere que las fichas comiencen con la pgina 5 activa, se usar este mtodo. Si
no se usa, el comportamiento predeterminado de las fichas es mostrar la primera pgina.
notebook.set_current_page(page_num)
Los siguientes dos mtodos aaden o borran las pestaas y el borde respectivamente.
notebook.set_show_tabs(show_tabs)
notebook.set_show_border(show_border)
El siguiente mtodo es til cuando se tiene un gran nmero de pginas, y las pestaas no caben en la pgina. Permite
desplazarse por las pestaas usando dos botones de flechas.
notebook.set_scrollable(scrollable)
show_tabs (mostrar pestaas), show_border (mostrar border) y scrollable (desplazable) pueden ser TRUE o
FALSE.
Ahora veamos un ejemplo. El programa notebook.py crea una ventana con unas fichas y seis botones. Las fichas
contienen 11 pginas, aadidas de tres formas diferentes, al final, en medio y al principio. Los botones permiten rotar la
posicin de las pestaas, aadir o borrar las pestaas y el borde, borrar una pgina, cambiar las pginas hacia delante y
hacia atrs, y salir del programa. La figura Figura 10.9, Ejemplo de Fichas muestra la ventana del programa:
Figura 10.9. Ejemplo de Fichas
132
#!/usr/bin/env python
# ejemplo notebook.py
import pygtk
pygtk.require('2.0')
import gtk
class NotebookExample:
# Este mtodo rota la posicin de las pestaas
def rotate_book(self, button, notebook):
notebook.set_tab_pos((notebook.get_tab_pos()+1) %4)
# Aadir/Eliminar las pestaas de pgina y los bordes
def tabsborder_book(self, button, notebook):
tval = gtk.FALSE
bval = gtk.FALSE
if self.show_tabs == gtk.FALSE:
tval = gtk.TRUE
if self.show_border == gtk.FALSE:
bval = gtk.TRUE
notebook.set_show_tabs(tval)
self.show_tabs = tval
notebook.set_show_border(bval)
self.show_border = bval
# Eliminar una pgina de las fichas
def remove_book(self, button, notebook):
page = notebook.get_current_page()
notebook.remove_page(page)
# Need to refresh the widget -# This forces the widget to redraw itself.
notebook.queue_draw_area(0,0,-1,-1)
def delete(self, widget, event=None):
gtk.main_quit()
133
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
return gtk.FALSE
def __init__(self):
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.connect("delete_event", self.delete)
window.set_border_width(10)
table = gtk.Table(3,6,gtk.FALSE)
window.add(table)
# Crear unas nuevas fichas, definir la posicin de las pestaas
notebook = gtk.Notebook()
notebook.set_tab_pos(gtk.POS_TOP)
table.attach(notebook, 0,6,0,1)
notebook.show()
self.show_tabs = gtk.TRUE
self.show_border = gtk.TRUE
# Aadimos unas cuantas pginas a las fichas
for i in range(5):
bufferf = "Append Frame %d" % (i+1)
bufferl = "Page %d" % (i+1)
frame = gtk.Frame(bufferf)
frame.set_border_width(10)
frame.set_size_request(100, 75)
frame.show()
label = gtk.Label(bufferf)
frame.add(label)
label.show()
label = gtk.Label(bufferl)
notebook.append_page(frame, label)
# Ahora aadimos una pgina en un punto determinado
checkbutton = gtk.CheckButton("Check me please!")
checkbutton.set_size_request(100, 75)
checkbutton.show ()
label = gtk.Label("Add page")
notebook.insert_page(checkbutton, label, 2)
# Finalmente aadimos una pgina delante
for i in range(5):
bufferf = "Prepend Frame %d" % (i+1)
bufferl = "PPage %d" % (i+1)
frame = gtk.Frame(bufferf)
frame.set_border_width(10)
frame.set_size_request(100, 75)
frame.show()
label = gtk.Label(bufferf)
frame.add(label)
label.show()
label = gtk.Label(bufferl)
notebook.prepend_page(frame, label)
134
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
Espero que esto ayude en el camino para crear fichas en los programas PyGTK.
10.13. Elementos incrustables y puntos de conexin (Plugs y Sockets)
Los Plugs y los Sockets cooperan para embeber la interfaz de un proceso en otro proceso. Esto tambin se puede
lograr con el uso de Bonobo.
10.13.1. Elementos incrustables (Plugs)
Un Plug encapsula una interfaz de usuario aportada por una aplicacin de forma que se puede embeber en la interfaz de
otra aplicacin. La seal "embedded" alerta a la aplicacin embebida que ha sido embebida en la interfaz de usuario de
la otra aplicacin.
135
#!/usr/bin/python
import pygtk
pygtk.require('2.0')
import gtk,sys
Wid = 0L
if len(sys.argv) == 2:
Wid = long(sys.argv[1])
plug = gtk.Plug(Wid)
print "Plug ID=", plug.get_id()
def embed_event(widget):
print "I (",widget,") have just been embedded!"
plug.connect("embedded", embed_event)
entry = gtk.Entry()
entry.set_text("hello")
def entry_point(widget):
print "You've changed my text to '%s'" % widget.get_text()
entry.connect("changed", entry_point)
plug.connect("destroy", gtk.mainquit)
plug.add(entry)
plug.show_all()
gtk.mainloop()
136
Un Socket proporciona el control que permite embeber un control incrustable Plug desde otra aplicacin a la propia
interfaz de forma transparente. Una aplicacin crea un control Socket, pasa el ID de ventana de ese control a otra
aplicacin, que luego crea un Plug utilizando ese ID de ventana como parmetro. Cualquier control contenido en el
elemento Plug aparece dentro de la ventana de la primera aplicacin.
El identificador (ID) de ventana del Socket se obtiene utilizando el mtodo de Socket get_id(). Antes de usar este
mtodo el Socket debe estar realizado y aadido a su control padre.
Nota
Si se le pasa el ID de ventana del Socket a otro proceso que va a crear un elemento Plug en el
Socket, es preciso asegurarse de que el control Socket no se va a destruir hasta que se crea el
elemento Plug.
Cuando se notifica a GTK+ que la ventana embebida ha sido destruda, entonces tambin se encarga de destruir el
Socket. Por tanto, se debe prever la posibilidad de que los sockets se destruyan en cualquier momento mientras el
bucle de eventos principal est en ejecucin. La destruccin de un Socket provocar tambin la destruccin de un
Plug embebido en l.
La comunicacin entre un Socket y un Plug se hace segn el protocolo XEmbed. Este protocolo tambin se ha
implementado en otros toolkits como Qt, lo que permite el mismo nivel de integracin al integrar un control de Qt en
GTK+ o viceversa.
Creacin de un nuevo Socket vaco:
socket = gtk.Socket()
El Socket debe estar contenido en una ventana raz antes de la invocacin del mtodo add_id():
socket.add_id(window_id)
que aade un cliente XEMBED, tal como un Plug, al Socket. El cliente puede estar en el mismo proceso o en uno
diferente.
Para embeber un Plug en un Socket se puede, bien crear el Plug con:
plug = gtk.Plug(0L)
y luego pasar el nmero devuelto por el mtodo de Plug get_id() al mtodo de Socket add_id():
socket.add_id(plug)
, o bien se puede invocar el mtodo de Socket get_id():
window_id = socket.get_id()
para obtener el identificador de ventana (ID) del socket, y luego crear el plug con:
plug = gtk.Plug(window_id)
El Socket debe haber sido aadido ya a una ventana raz antes de que se pueda hacer esta llamada.
El programa de ejemplo socket.py ilustra el uso de un Socket:
1
2
#!/usr/bin/python
137
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
import string
import pygtk
pygtk.require('2.0')
import gtk,sys
window = gtk.Window()
window.show()
socket = gtk.Socket()
socket.show()
window.add(socket)
print "Socket ID=", socket.get_id()
window.connect("destroy", gtk.mainquit)
def plugged_event(widget):
print "I (",widget,") have just had a plug inserted!"
socket.connect("plug-added", plugged_event)
if len(sys.argv) == 2:
socket.add_id(long(sys.argv[1]))
gtk.mainloop()
138
Esto es algo complicado por el hecho de que los controles de elementos de men se usan para dos cosas distintas. Son
tanto los controles que se colocan en el men, y el control que se coloca en la barra de men, que, cuando se selecciona,
activa el men.
Veamos las funciones que se usan para crear mens y barras de mens. La primera funcin se usa para crear una nueva
barra de men:
menu_bar = gtk.MenuBar()
Esta funcin bastante autoexplicativa crea una nueva barra de mens. Se puede usar el mtodo gtk.Container
add() para meter la barra de mens en una ventana, o los mtodos pack de gtk.Box para meterlo en una caja - igual
que los botones.
menu = gtk.Menu()
Esta funcin devuelve una referencia a un nuevo men; nunca se mostrar (con el mtodo show() ), es slo un
contenedor para elementos de mens. Esto se aclarar ms cuando se vea el ejemplo que aparece ms abajo.
La siguiente funcin se usa para crear elementos de mens que se colocan en el men (y la barra de mens):
menu_item = gtk.MenuItem(label=None)
El parmetro label, si existe, se analizar buscando caracteres mnemnicos. Esta llamada se usa para crear los
elementos de men que se van a visualizar. Debe recordarse la diferenciar entre un "men" como el que se crea con la
funcin gtk.Menu() y un "elemento de men" como el que se crea con gtk.MenuItem() . El elemento de men ser
en realidad un botn con una accin asociada, mientras que un men ser un contenedor que contiene elementos de
men.
Una vez se ha creado un elemento de men es preciso meterlo en un men. Esto se consigue con el mtodo append() .
Para poder saber cundo el usuario selecciona un elemento, se necesita conectarlo a la seal "activate" de la forma
habitual. Por tanto, si se quiere crear un men estndar Archivo, con las opciones Abrir, Guardar, y Salir, el cdigo sera
algo asi como:
file_menu = gtk.Menu()
139
open_item = gtk.MenuItem("Abrir")
save_item = gtk.MenuItem("Guardar")
quit_item = gtk.MenuItem("Salir")
# Los aadimos al men
file_menu.append(open_item)
file_menu.append(save_item)
file_menu.append(quit_item)
# Se conectan las funciones de retrollamada a la seal "activate"
open_item.connect_object("activate", menuitem_response, "file.open")
save_item.connect_object("activate", menuitem_response, "file.save")
# se conecta el elemento quit a nuestra funcin de salida
quit_item.connect_object ("activate", destroy, "file.quit")
# Mostramos los elementos de men
open_item.show()
save_item.show()
quit_item.show()
En este punto tenemos nuestro men. Ahora es necesario crear una barra de men y un elemento de men para la entrada
Archivo, al que aadiremos nuestro men. El cdigo es el siguiente:
menu_bar = gtk.MenuBar()
window.add(menu_bar)
menu_bar.show()
file_item = gtk.MenuItem("Archivo")
file_item.show()
Ahora necesitamos asociar el men con file_item. Esto se hace con el mtodo:
menu_item.set_submenu(submenu)
Por tanto, nuestro ejemplo continuara as:
menu_item.set_submenu(file_menu)
Lo nico que nos queda es aadir el men a la barra de mens, lo cual se consigue con el mtodo:
menu_bar.append(child)
que en nuestro caso es as:
menu_bar.append(file_item)
Si queremos el men justificado a la derecha en la barra de mens, como suelen ser los mens de ayuda, podemos usar
el siguiente mtodo (de nuevo en file_item en nuestro ejemplo) antes de aadirlo a la barra de mens.
menu_item.set_right_justified(right_justified)
Aqui tenemos un resumen de los pasos necesarios para crear una barra de mens con mens en ella:
140
Se crea un elemento de men usando gtk.MenuItem(). Este ser la raiz del men, el texto que aparezca en l
estar en la propia barra de mens.
Se usa el mtodo set_submenu() para aadir el men al elemento raiz del men (el creado en el paso
anterior).
Se crea una nueva barra de mens con gtk.MenuBar(). Este paso se hace nicamente una vez al crear una
serie de mens en una barra de mens.
Se usa el mtodo append() para poner el men raiz en la barra de mens.
#!/usr/bin/env python
# ejemplo menu.py
import pygtk
pygtk.require('2.0')
import gtk
class MenuExample:
def __init__(self):
# create a new window
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_size_request(200, 100)
window.set_title("GTK Menu Test")
window.connect("delete_event", lambda w,e: gtk.main_quit())
# Se inicia el control men, y se ha de recordar que NUNCA
141
18
# se debe mostrar con show() este control!!
19
# Este es el men que contiene los elementos de men, el que
20
# se desplegar al pulsar en "Root Menu" en la aplicacin
21
menu = gtk.Menu()
22
23
# Despus un pequeo bucle construye tres entradas de men para
24
# "test-menu". Obsrvese la llamada a gtk_menu_append. Aqu
vamos
25
# aadiendo una lista de elementos de men a nuestro men.
Normalmente tambin
26
# interceptaramos la seal "clicked" para cada uno de los
elementos de men y
27
# se establecera una retrollamada para ellas, pero se omiten
aqu para ahorrar espacio
28
for i in range(3):
29
# Copiar los nombres a buf
30
buf = "Test-undermenu - %d" % i
31
32
# Crear un nuevo elemento de men con nombre...
33
menu_items = gtk.MenuItem(buf)
34
35
# ...y lo aadimos al men
36
menu.append(menu_items)
37
38
# Hacemos algo interesante cuando se selecciona el elemento de
men
39
menu_items.connect("activate", self.menuitem_response, buf)
40
41
# Mostramos el control
42
menu_items.show()
43
44
# Este es el men raz, y ser la etiqueta mostrada en la barra
de men
45
# No tendr un manejador de seal asociado, puesto que nicamente
46
# muestra el resto del men al pulsarlo.
47
root_menu = gtk.MenuItem("Root Menu")
48
49
root_menu.show()
50
51
# Ahora especificamos que queremos que nuestro recien creado
"menu" sea el
52
# men del men raz "root menu"
53
root_menu.set_submenu(menu)
54
55
# Una vbox para poner un men y un botn en l:
56
vbox = gtk.VBox(gtk.FALSE, 0)
57
window.add(vbox)
58
vbox.show()
59
60
# Creamos una barra de men para los mens, que aadimos a la
ventana principal
61
menu_bar = gtk.MenuBar()
62
vbox.pack_start(menu_bar, gtk.FALSE, gtk.FALSE, 2)
63
menu_bar.show()
64
65
# Creamos un botn al que aadir el men emergente
66
button = gtk.Button("press me")
67
button.connect_object("event", self.button_press, menu)
68
vbox.pack_end(button, gtk.TRUE, gtk.TRUE, 2)
69
button.show()
142
70
71
# Y finalmente aadimos el elemento de men a la barra de men.
Este es el
72
# elemento de men raz del que hemos estado hablando =)
73
menu_bar.append (root_menu)
74
75
# siempres mostramos la ventana como ltimo paso, de manera que
se dibuja todo
76
# de una vez.
77
window.show()
78
79
# Respondemos a una pulsacin de botn con un men pasado como
control "widget"
80
#
81
# Ntese que el argumento "widget" es el men pasado, NO
82
# el botn que fue pulsado.
83
def button_press(self, widget, event):
84
if event.type == gtk.gdk.BUTTON_PRESS:
85
widget.popup(None, None, None, event.button, event.time)
86
# Notificamos al cdigo de llamada que hemos manejado este
evento.
87
# La cola acaba aqu.
88
return gtk.TRUE
89
# Notificamos al cdigo de llamada que no manejamos este evento.
La cola contina.
90
return gtk.FALSE
91
92
# Imprimimos una cadena cuando se selecciona un elemento de men
93
def menuitem_response(self, widget, string):
94
print "%s" % string
95
96 def main():
97
gtk.main()
98
return 0
99
100 if __name__ == "__main__":
101
MenuExample()
102
main()
Tambin se puede hacer un elemento de men insensible y, usando una tabla de atajos (aceleradores), conectar teclas a
retrollamadas de mens.
11.3. Uso de la Factoria de Elementos
Ahora que conocemos la forma difcil, as es como se hara usando las llamadas a gtk.ItemFactory.
11.4. Ejemplo de Factoria de Elementos - ItemFactory
El programa de ejemplo itemfactory.py usa la gtk.ItemFactory. La figura Figura 11.2, Ejemplo de Factoria de
Elementos muestra la ventana del programa:
Figura 11.2. Ejemplo de Factoria de Elementos
143
144
40
# Param 1: El tipo del men - puede ser MenuBar, Menu,
41
#
u OptionMenu.
42
# Param 2: La ruta o camino del men.
43
# Param 3: Una referencia a un AccelGroup. La factoria de
elementos establece la
44
#
tabla de aceleradores (atajos) al generar los mens.
45
item_factory = gtk.ItemFactory(gtk.MenuBar, "<main>",
accel_group)
46
47
# Este mtodo genera los elementos de men. Pasa a la factora de
elementos la lista
48
# de los elementos de men
49
item_factory.create_items(self.menu_items)
50
51
# Aadir el nuevo grupo de aceleradores a la ventana.
52
window.add_accel_group(accel_group)
53
54
# es necesario mantener una referencia a item_factory para evitar
su destruccin
55
self.item_factory = item_factory
56
# Finalmente, se devuelve la barra de men creada por la factora
de elementos.
57
return item_factory.get_widget("<main>")
58
59
def __init__(self):
60
self.menu_items = (
61
( "/_File",
None,
None, 0, "<Branch>" ),
62
( "/File/_New",
"<control>N", self.print_hello, 0, None
),
63
( "/File/_Open",
"<control>O", self.print_hello, 0, None
),
64
( "/File/_Save",
"<control>S", self.print_hello, 0, None
),
65
( "/File/Save _As", None,
None, 0, None ),
66
( "/File/sep1",
None,
None, 0, "<Separator>" ),
67
( "/File/Quit",
"<control>Q", gtk.mainquit, 0, None ),
68
( "/_Options",
None,
None, 0, "<Branch>" ),
69
( "/Options/Test", None,
None, 0, None ),
70
( "/_Help",
None,
None, 0, "<LastBranch>" ),
71
( "/_Help/About",
None,
None, 0, None ),
72
)
73
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
74
window.connect("destroy", gtk.mainquit, "WM destroy")
75
window.set_title("Item Factory")
76
window.set_size_request(300, 200)
77
78
main_vbox = gtk.VBox(gtk.FALSE, 1)
79
main_vbox.set_border_width(1)
80
window.add(main_vbox)
81
main_vbox.show()
82
83
menubar = self.get_main_menu(window)
84
85
main_vbox.pack_start(menubar, gtk.FALSE, gtk.TRUE, 0)
86
menubar.show()
87
window.show()
88
89 def main():
90
gtk.main()
91
return 0
145
92
93
94
95
if __name__ == "__main__":
ItemFactoryExample()
main()
Por ahora esto es slo un ejemplo. Ms adelante aparecer una explicacin y muchos comentarios.
Captulo 12. rea de Dibujo
Tabla de contenidos
12.1. Contexto Grfico
12.2. Mtodos de Dibujo
El control DrawingArea (rea de Dibujo) proporciona un lienzo en el que poder dibujar de forma sencilla haciendo
uso de los mtodos de la clase gtk.gdk.Drawable ("dibujable"). Un control DrawingArea en realidad encapsula
un elemento del tipo gtk.gdk.Window (ventana), que es una subclase de gtk.gdk.Drawable (al igual que
gtk.gdk.Pixmap).
Para crear un rea de Dibujo se utiliza la funcin:
drawing_area = gtk.DrawingArea()
Los elementos DrawingArea (rea de Dibujo) se crean inicialmente con un tamao de (0,0), por lo que es necesario
usar el siguiente mtodo para poder hacer visible el rea de Dibujo (drawing_area) asignndole un ancho y una
altura distintas de cero:
drawing_area.set_size_request(width, height)
width y height especifican respectivamente el ancho y alto del rea de dibujo (DrawingArea).
Para dibujar en este control es necesario acceder a su dibujable (gtk.gdk.Drawable) asociado, en este caso una
ventana (gtk.gdk.Window), utilizando su atributo window:
drawable = drawing_area.window
Nota
Para poder acceder a la ventana (gtk.gdk.Window) asociada al rea de dibujo (DrawingArea) y
poder dibujar en ella el rea de Dibujo debe haber sido instanciada (lo que da lugar a que genere una
seal "realize").
12.1. Contexto Grfico
Hay disponibles varios mtodos que nos facilitan el dibujo en un rea de dibujo (en su gtk.gdk.Drawable). Todos
ellos precisan un contexto grfico (gtk.gdk.GC) que alberga la informacin necesaria para llevar a cabo el dibujado.
Para ello, un contexto grfico (gtk.gdk.GC) dispone de los siguientes atributos::
background
cap_style
clip_mask
clip_x_origin
clip_y_origin
fill
font
foreground
function
#
#
#
#
#
#
#
#
#
fondo
estilo de fin de linea
mscara de recorte
origen x del rectngulo de recorte
origen y del rectngulo de recorte
relleno
fuente
color de frente (primer plano)
funcin
146
igual que CAP_BUTT para lineas con ancho distinto de cero. En el caso de lneas de ancho cero no
se dibuja el punto final de la linea.
CAP_BUTT
los extremos de las lineas se dibujan como cuadrados que se extienden hasta las coordenadas del
punto final.
CAP_ROUND
los extremos de las lineas se dibujan como semicrculos de dimetro igual al grosor de la linea y
cuyo centro se sita en el punto final.
CAP_PROJECTING
los extremos de las lineas son dibujados como cuadrados que se prolongan medio grosor ms all
del punto final.
147
clip_mask especifica un mapa de pxeles (gtk.gdk.Pixmap) que se usar para hacer el recorte del dibujo
contenido en el rea de dibujo (drawing_area).
clip_x_origin y clip_y_origin especifican los valores de las coordenadas x e y correspondientes al origen del
rectngulo de recorte tomando como referencia la esquina superior izquierda del rea de dibujo (drawing_area).
fill especifica el estilo de relleno usado en el dibujo. Los estilos de relleno que se encuentran disponibles son:
SOLID
TILED
STIPPLED
dibuja usando un patrn de mapa de bits. Los pxeles correspondientes a los bits activados (a
uno) del mapa de bits se dibujan usando el color de primer plano (foregraund) y aquellos
correspondientes a los bits que no estn activados se dejan intactos.
dibuja usando un patrn de mapa de bits. Los pxeles correspondientes a los bits del mapa de bits
OPAQUE_STIPPLED que estn activados (a uno) se dibujan con el color de primer plano; aquellos correspondientes a
los bits que no estn activados se dibujan con el color de fondo.
font es la gtk.gdk.Font (Fuente) que se usa cmo fuente predeterminada para dibujar texto.
Nota
El uso del atributo font est obsoleto.
function especifica una funcin usada para combinar los valores de los bits de los pxeles de origen con los valores
de los bits de los pxeles de destino y producir un pixel transformado. Los 16 posibles valores de este parmetro
corresponden a las tablas de verdad posibles de tamao 2x2. Sin embargo, solamente algunos de estos valores tienen
utilidad en la prctica. As, en el caso de imgenes a color es frecuentel el uso de COPY, XOR e INVERT y en el de
mapas de bits tambin son habituales AND y OR. Los valores posibles de function son:
COPY
INVERT
XOR
CLEAR
AND
AND_REVERSE
AND_INVERT
NOOP
OR
EQUIV
OR_REVERSE
COPY_INVERT
OR_INVERT
NAND
SET
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
Copiar
Invertir
O exclusivo
Limpiar
Y
Y al revs
Y invertida
Nada
O
equivalencia
O al revs
Copiar invertido
O invertido
No-Y
Fijar
graphics_exposures especifica si las exposiciones grficas estn activadas (TRUE) o desactivadas (FALSE).
Cuando graphics_exposures tiene el valor TRUE un fallo al copiar un pxel en una operacin de dibujo genera un
evento expose y en el caso de que la copia tenga xito se generar un evento noexpose.
join_style especifica el estilo de unin que se usa en los encuentros de lineas en ngulo. Los estilos disponibles son:
JOIN_MITER los lados de cada linea se extienden para unirse en ngulo.
JOIN_ROUND los lados de las dos lineas se unen con un arco de crculo.
JOIN_BEVEL
los lados de las dos lineas se unen en chafln, una linea recta que forma el mismo ngulo con cada una
de las lneas.
148
line_style especifica el estilo con el que se dibuja una linea. Los estilos disponibles son:
LINE_SOLID
LINE_ON_OFF_DASH se dibujan los segmentos impares; los segmentos pares no se dibujan (lnea discontnua).
los segmentos impares son normales. Los segmentos pares se dibujan con el color de fondo si el
LINE_DOUBLE_DASH estilo de relleno es SOLID, o con el color de fondo aplicado a la mscara del patrn si el estilo
de relleno es STIPPLED.
line_width especifica el ancho con el que se dibujan las lneas.
stipple especifica el gtk.gdk.Pixmap que se usar como patrn cuando el relleno est puesto a STIPPLED o a
OPAQUE_STIPPLED.
sub_window especifica el modo de dibujo de una ventana (gtk.gdk.Window) que tiene ventanas hijas
(gtk.gdk.Windows). Los valores posibles de este parmetro son:
CLIP_BY_CHILDREN
INCLUDE_INFERIORS
tile especifica el gtk.gdk.Pixmap que se usar para dibujo cuadriculado cuando el relleno (fill) est puesto a
TILED.
ts_x_origin y ts_y_origin especifican las posiciones iniciales (el origen) de los mapas de bits de patrn y de
dibujo cuadriculado.
Un Contexto Grfico nuevo se crea mediante una llamada al mtodo gtk.gdk.Drawable new_gc() :
gc = drawable.new_gc(foreground=None, background=None, font=None,
function=-1, fill=-1, tile=None,
stipple=None, clip_mask=None, subwindow_mode=-1,
ts_x_origin=-1, ts_y_origin=-1, clip_x_origin=-1,
clip_y_origin=-1, graphics_exposures=-1,
line_width=-1, line_style=-1, cap_style=-1
join_style=-1)
Para poder crear un nuevo Contexto Grfico usando este mtodo es preciso que el control (correspondiente a drawable)
sea:
Los diversos atributos del Contexto Grfico tienen los valores por defecto si no se fijan en el mtodo new_gc(). Si se
desea establecer el valor de los atributos de los contextos grficos mediante el mtodo new_gc() resulta muy prctico el
uso de argumentos con nombre de Python.
Tambin se pueden establecer los atributos individuales de un gtk.gdk.GC asisgnando valores a los atributos
correspondientes. Estos son algunos ejemplos de ello:
gc.cap_style = CAP_BUTT
gc.line_width = 10
gc.fill = SOLD
gc.foreground = micolor
o usando los siguientes mtodos:
149
gc.set_foreground(color)
gc.set_background(color)
gc.set_function(function)
gc.set_fill(fill)
gc.set_tile(tile)
gc.set_stipple(stipple)
gc.set_ts_origin(x, y)
gc.set_clip_origin(x, y)
gc.set_clip_mask(mask)
gc.set_clip_rectangle(rectangle)
gc.set_subwindow(mode)
gc.set_exposures(exposures)
gc.set_line_attributes(line_width, line_style, cap_style, join_style)
El patrn de trazos que se usa cuando el parmetro de estilo de linea (line_style) es LINE_ON_OFF_DASH o
LINE_DOUBLE_DASH se puede fijar haciendo uso del siguiente mtodo:
gc.set_dashes(offset, dash_list)
donde offset (desplazamiento) es el ndice del valor del trazo inicial en dash_list y dash_list (lista de trazos)
es una lista o tupla que contiene los nmeros de pxeles que dibujar o saltar para formar los trazos. stos se dibujan
empezando con el nmero de pxeles en la posicin de desplazamiento(offset); despus, el siguiente nmero de
pxeles no se dibuja, y, luego, se dibuja el siguiente nmero de pxeles;; y as se contina recorriendo todos los nmeros
de la lista de trazos y empezando otra vez cuando se llega a su final. Por ejemplo, si la lista de trazos es (2, 4, 8, 16) y el
desplazamiento es 1, los trazos se dibujarn as: dibuja 4 pxeles, salta 8 pxeles, dibuja 16 pxeles, salta 2 pxeles, dibuja
4 pxeles, y as sucesivamente.
Es posible hacer una copia de un gtk.gdk.GC existente utilizando el mtodo:
gc.copy(src_gc)
Los atributos de gc sern los mismos que los de src_gc.
12.2. Mtodos de Dibujo
Hay un conjunto general de mtodos que se pueden usar para dibujar en el rea de dibujo. Estos mtodos de dibujo
funcionan en cualquier gtk.gdk.Drawable (Dibujable), que es una gtk.gdk.Window (Ventana) o un
gtk.gdk.Pixmap (Mapa de Pxeles). Los mtodos de dibujo son:
drawable.draw_point(gc, x, y) # dibuja_punto
gc es el Contexto Grfico que se usar para hacer el dibujo.
x e y son las coordenadas del punto.
drawable.draw_line(gc, x1, y1, x2, y2) # dibuja linea
gc es el Contexto Grfico.
x1 e y1 especifican el punto de inicio de la linea. x2 e y2 especifican el punto final de la linea.
drawable.draw_rectangle(gc, filled, x, y, width, height) # dibuja rectngulo
gc es el Contexto Grfico.
filled es un valor booleano que indica si el rectngulo debe ser rellenado con el color de primer plano (valor TRUE) o
no (valor FALSE).
150
151
152
RGB_DITHER_NORMAL
menos)
slamente.
RGB_DITHER_MAX
menos.
El programa de ejemplo drawingarea.py muestra el uso de la mayora de los mtodos de un rea de dibujo
(DrawingArea). Tambin inserta el rea de dibujo (DrawingArea) en el interior de una ventana con barras de
desplazamiento (ScrolledWindow) y aade los controles de desplazamiento horizontal y vertical (Ruler). La figura
Figura 12.1, Ejemplo de rea de Dibujo muestra la ventana resultante:
Figura 12.1. Ejemplo de rea de Dibujo
153
#!/usr/bin/env python
# example drawingarea.py
import pygtk
pygtk.require('2.0')
import gtk
import operator
import time
import string
class DrawingAreaExample:
def __init__(self):
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_title("Ejemplo de rea de Dibujo")
window.connect("destroy", lambda w: gtk.main_quit())
self.area = gtk.DrawingArea()
self.area.set_size_request(400, 300)
self.pangolayout = self.area.create_pango_layout("")
self.sw = gtk.ScrolledWindow()
self.sw.add_with_viewport(self.area)
self.table = gtk.Table(2,2)
self.hruler = gtk.HRuler()
self.vruler = gtk.VRuler()
self.hruler.set_range(0, 400, 0, 400)
self.vruler.set_range(0, 300, 0, 300)
self.table.attach(self.hruler, 1, 2, 0, 1, yoptions=0)
self.table.attach(self.vruler, 0, 1, 1, 2, xoptions=0)
self.table.attach(self.sw, 1, 2, 1, 2)
window.add(self.table)
self.area.set_events(gtk.gdk.POINTER_MOTION_MASK |
gtk.gdk.POINTER_MOTION_HINT_MASK )
self.area.connect("expose-event", self.area_expose_cb)
def motion_notify(ruler, event):
return ruler.emit("motion_notify_event", event)
self.area.connect_object("motion_notify_event", motion_notify,
self.hruler)
154
38
self.area.connect_object("motion_notify_event", motion_notify,
39
self.vruler)
40
self.hadj = self.sw.get_hadjustment()
41
self.vadj = self.sw.get_vadjustment()
42
def val_cb(adj, ruler, horiz):
43
if horiz:
44
span = self.sw.get_allocation()[3]
45
else:
46
span = self.sw.get_allocation()[2]
47
l,u,p,m = ruler.get_range()
48
v = adj.value
49
ruler.set_range(v, v+span, p, m)
50
while gtk.events_pending():
51
gtk.main_iteration()
52
self.hadj.connect('value-changed', val_cb, self.hruler, True)
53
self.vadj.connect('value-changed', val_cb, self.vruler, False)
54
def size_allocate_cb(wid, allocation):
55
x, y, w, h = allocation
56
l,u,p,m = self.hruler.get_range()
57
m = max(m, w)
58
self.hruler.set_range(l, l+w, p, m)
59
l,u,p,m = self.vruler.get_range()
60
m = max(m, h)
61
self.vruler.set_range(l, l+h, p, m)
62
self.sw.connect('size-allocate', size_allocate_cb)
63
self.area.show()
64
self.hruler.show()
65
self.vruler.show()
66
self.sw.show()
67
self.table.show()
68
window.show()
69
70
def area_expose_cb(self, area, event):
71
self.style = self.area.get_style()
72
self.gc = self.style.fg_gc[gtk.STATE_NORMAL]
73
self.draw_point(10,10)
74
self.draw_points(110, 10)
75
self.draw_line(210, 10)
76
self.draw_lines(310, 10)
77
self.draw_segments(10, 100)
78
self.draw_rectangles(110, 100)
79
self.draw_arcs(210, 100)
80
self.draw_pixmap(310, 100)
81
self.draw_polygon(10, 200)
82
self.draw_rgb_image(110, 200)
83
return gtk.TRUE
84
85
def draw_point(self, x, y):
86
self.area.window.draw_point(self.gc, x+30, y+30)
87
self.pangolayout.set_text("Punto")
88
self.area.window.draw_layout(self.gc, x+5, y+50,
self.pangolayout)
89
return
90
91
def draw_points(self, x, y):
92
points = [(x+10,y+10), (x+10,y), (x+40,y+30),
93
(x+30,y+10), (x+50,y+10)]
94
self.area.window.draw_points(self.gc, points)
95
self.pangolayout.set_text("Puntos")
155
96
self.area.window.draw_layout(self.gc, x+5, y+50,
self.pangolayout)
97
return
98
99
def draw_line(self, x, y):
100
self.area.window.draw_line(self.gc, x+10, y+10, x+20, y+30)
101
self.pangolayout.set_text("Lnea")
102
self.area.window.draw_layout(self.gc, x+5, y+50,
self.pangolayout)
103
return
104
105
def draw_lines(self, x, y):
106
points = [(x+10,y+10), (x+10,y), (x+40,y+30),
107
(x+30,y+10), (x+50,y+10)]
108
self.area.window.draw_lines(self.gc, points)
109
self.pangolayout.set_text("Lneas")
110
self.area.window.draw_layout(self.gc, x+5, y+50,
self.pangolayout)
111
return
112
113
def draw_segments(self, x, y):
114
segments = ((x+20,y+10, x+20,y+70), (x+60,y+10, x+60,y+70),
115
(x+10,y+30 , x+70,y+30), (x+10, y+50 , x+70, y+50))
116
self.area.window.draw_segments(self.gc, segments)
117
self.pangolayout.set_text("Segmentos")
118
self.area.window.draw_layout(self.gc, x+5, y+80,
self.pangolayout)
119
return
120
121
def draw_rectangles(self, x, y):
122
self.area.window.draw_rectangle(self.gc, gtk.FALSE, x, y, 80,
70)
123
self.area.window.draw_rectangle(self.gc, gtk.TRUE, x+10, y+10,
20, 20)
124
self.area.window.draw_rectangle(self.gc, gtk.TRUE, x+50, y+10,
20, 20)
125
self.area.window.draw_rectangle(self.gc, gtk.TRUE, x+20, y+50,
40, 10)
126
self.pangolayout.set_text("Rectngulos")
127
self.area.window.draw_layout(self.gc, x+5, y+80,
self.pangolayout)
128
return
129
130
def draw_arcs(self, x, y):
131
self.area.window.draw_arc(self.gc, gtk.FALSE, x+10, y, 70, 70,
132
0, 360*64)
133
self.area.window.draw_arc(self.gc, gtk.TRUE, x+30, y+20, 10, 10,
134
0, 360*64)
135
self.area.window.draw_arc(self.gc, gtk.TRUE, x+50, y+20, 10, 10,
136
0, 360*64)
137
self.area.window.draw_arc(self.gc, gtk.TRUE, x+30, y+10, 30, 50,
138
210*64, 120*64)
139
self.pangolayout.set_text("Arcos")
140
self.area.window.draw_layout(self.gc, x+5, y+80,
self.pangolayout)
141
return
142
143
def draw_pixmap(self, x, y):
144
pixmap, mask = gtk.gdk.pixmap_create_from_xpm(
156
145
self.area.window, self.style.bg[gtk.STATE_NORMAL],
"gtk.xpm")
146
147
self.area.window.draw_drawable(self.gc, pixmap, 0, 0, x+15,
y+25,
148
-1, -1)
149
self.pangolayout.set_text("Mapa de Pxeles")
150
self.area.window.draw_layout(self.gc, x+5, y+80,
self.pangolayout)
151
return
152
153
def draw_polygon(self, x, y):
154
points = [(x+10,y+60), (x+10,y+20), (x+40,y+70),
155
(x+30,y+30), (x+50,y+40)]
156
self.area.window.draw_polygon(self.gc, gtk.TRUE, points)
157
self.pangolayout.set_text("Polgono")
158
self.area.window.draw_layout(self.gc, x+5, y+80,
self.pangolayout)
159
return
160
161
def draw_rgb_image(self, x, y):
162
b = 80*3*80*['\0']
163
for i in range(80):
164
for j in range(80):
165
b[3*80*i+3*j] = chr(255-3*i)
166
b[3*80*i+3*j+1] = chr(255-3*abs(i-j))
167
b[3*80*i+3*j+2] = chr(255-3*j)
168
buff = string.join(b, '')
169
self.area.window.draw_rgb_image(self.gc, x, y, 80, 80,
170
gtk.gdk.RGB_DITHER_NONE, buff, 80*3)
171
self.pangolayout.set_text("Imagen RGB")
172
self.area.window.draw_layout(self.gc, x+5, y+80,
self.pangolayout)
173
return
174
175
def main():
176
gtk.main()
177
return 0
178
179
if __name__ == "__main__":
180
DrawingAreaExample()
181
main()
Captulo 13. Control de Vista de Texto
13.1. Perspectiva general de la Vista de Texto
El control TextView y sus objetos asociados (TextBuffers, TextMarks, TextIters, TextTags y
TextTagTables) proporcionan un potente marco para la edicin de textos multilnea.
Un TextBuffer (Buffer de Texto) contiene el texto que se visualizar en uno o ms controles TextView (Vista de
Texto)
En GTK+ 2.0 el texto se codifica en UTF-8 de modo que la codificacin de un caracter puede estar compuesta por varios
bytes. Dentro de un TextBuffer es necesario diferenciar entre contadores de carcteres (llamados desplazamientos) y
contadores de bytes (llamados ndices).
Los TextIters (Iteradores de Texto) proporcionan una representacin efmera de la posicin entre dos carcteres
dentro de un TextBuffer . Los TextIters son vlidos hasta que el nmero de caracteres en el TextBuffer
157
cambia; Por ejemplo, siempre que se inserten o se borren carcteres en el TextBuffer todos los TextIters se
invalidan. Los TextIters son la principal forma de especificar localizaciones en un TextBuffer para manipular
texto.
Los TextMarks (Marcas de Texto) se proporcionan para permitir almacenar posiciones en un TextBuffer que se
mantienen entre modificaciones del buffer. Una marca es cmo un TextIter (representa una posicin entre dos
carcteres en un TextBuffer) pero si el texto alrededor de la marca se borra, la marca permanece donde estaba el
texto borrado. De la misma forma, si se inseta texto en la marca, la marca acaba bien a la izquierda o bien a la derecha
del texto insertado, dependiendo de la gravedad de la marca - gravedad a la derecha deja la marca a la derecha del texto
insertado mientras que gravedad a la izquierda deja la marca a la izquierda. Las TextMarks se pueden asociar a un
nombre o dejarlas annimas si no se les da un nombre. Cada TextBuffer tiene dos marcas predefinidas llamadas
insert (insertar) y selection_bound (lmite de seleccin). Estas marcas se refieren al punto de insercin y al
lmite de la seleccin (la seleccin est entre las marcas insert y selection_bound).
Las TextTags (Etiquetas de Texto) son objetos que especifican un conjunto de atributos que se pueden aplicar a un
rango de texto en un TextBuffer. Cada TextBuffer tiene una TextTagTable (Tabla de Etiquetas de Texto) que
contiene las etiquetas disponibles en ese buffer. Las TextTagTables se pueden compartir entre TextBuffers para
ofrecer consistencia. Los TextTags normalmente se usan para cambiar la apariencia de un rango de texto pero tambin
pueden usarse para evitar que un rango de texto sea editado.
13.2. Vistas de Texto
Slo hay una funcin para crear un control TextView (Vista de Texto).
textview = gtk.TextView(buffer=None)
Cuando se crea una TextView tambin se crear un TextBuffer (Buffer de Texto) asociado y una
TextTagTable (Tabla de Etiquetas de Texto) de forma predeterminada. Si quieres usar un TextBuffer ya
existente en una vista de texto TextView puedes especificarlo en el mtodo anterior. Para cambiar el TextBuffer
que usa una TextView usa el siguiente mtodo:
textview.set_buffer(buffer)
Usa el siguiente mtodo para obtener una referencia al TextBuffer a partir de una TextView:
buffer = textview.get_buffer()
Un control de TextView no tiene barras de desplazamiento para ajustar la vista en caso de que el texto sea ms grande
que la ventana. Para incluir barras de desplazamiento, es necesario incluir la TextView en una ScrolledWindow
(Ventana de Desplazamiento).
Una TextView se puede usar para permitir la edicin de un cuerpo de texto, o para mostrar varias lneas de un texto de
slo lectura al usuario o usuaria. Para cambiar entre estos modos de operacin se utiliza el mtodo:
textview.set_editable(setting)
El argumento setting puede ser TRUE o FALSE y especifica si se permite la edicin del contenido del control
TextView. El modo de edicin de la TextView se puede cambiar por zonas de texto dentro del TextBuffer
usando TextTags.
Puedes obtener el modo actual de edicin usando el mtodo:
setting = textview.get_editable()
Cuando la TextView no es editable probablemente se debera ocultar el cursor usando el mtodo:
158
textview.set_cursor_visible(setting)
El argumento setting puede ser TRUE o FALSE y especifica si el cursor debe ser visible. La TextView puede
ajustar las lneas de texto que son demasiado largas para que quepan en una nica lnea de la ventana. El
comportamiento predeterminado es no ajustar las lneas. Es posible cambiarlo usando el mtodo:
textview.set_wrap_mode(wrap_mode)
Este mtodo te permite especificar que el texto debe ajustarse en los lmites de palabras o caracteres. El argumento
word_wrap puede ser:
gtk.WRAP_NONE # sin ajuste
gtk.WRAP_CHAR # ajuste por caracteres
gtk.WRAP_WORD # ajuste por palabras
La justificacin predetermianda del texto en una TextView se puede establecer y obtener usando los mtodos:
textview.set_justification(justification)
justification = textview.get_justification()
donde justification puede ser:
gtk.JUSTIFY_LEFT
# justificacin a la izquierda
gtk.JUSTIFY_RIGHT # justificacin a la derecha
gtk.JUSTIFY_CENTER # justificacin al centro
Nota
La justificacin ser JUSTIFY_LEFT si el wrap_mode (modo de ajuste) es WRAP_NONE. Las
etiquetas asociadas con un TextBuffer pueden cambiar la justificacin predeterminada.
Se pueden modificar y ver otros atributos predeterminados en una TextView, tales como el margen izquierdo, el
margen derecho, las tabulaciones y la indentacin de prrafos, usando los siguientes mtodos:
# margen izquierdo
textview.set_left_margin(left_margin)
left_margin = textview.get_left_margin()
# margen derecho
textview.set_right_margin(right_margin)
right_margin = textview.get_right_margin()
# indentacin
textview.set_indent(indent)
indent = textview.get_indent()
#espacio anterior de lnea (en pxeles)
textview.set_pixels_above_lines(pixels_above_line)
pixels_above_line = textview.get_pixels_above_lines()
#espacio posterior de lnea (en pxeles)
textview.set_pixels_below_lines(pixels_below_line)
pixels_below_line = textview.get_pixels_below_lines()
# pxeles en ajuste
textview.set_pixels_inside_wrap(pixels_inside_wrap)
pixels_inside_wrap = textview.get_pixels_inside_wrap()
159
# tabulaciones
textview.set_tabs(tabs)
tabs = textview.get_tabs()
left_margin, right_margin, indent, pixels_above_lines, pixels_below_lines y
pixels_inside_wrap se especifican en pxeles. Los valores predeterminados de estos parmetros se pueden
modificar con etiquetas asociadas a un TextBuffer. tabs es un pango.TabArray.
El programa de ejemplo textview-basic.py ilustra el uso bsico del control TextView:
Figura 13.1. Ejemplo bsico de Vista de Texto
#!/usr/bin/env python
# example textview-basic.py
import pygtk
pygtk.require('2.0')
import gtk
class TextViewExample:
def toggle_editable(self, checkbutton, textview):
textview.set_editable(checkbutton.get_active())
def toggle_cursor_visible(self, checkbutton, textview):
textview.set_cursor_visible(checkbutton.get_active())
def toggle_left_margin(self, checkbutton, textview):
if checkbutton.get_active():
160
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
textview.set_left_margin(50)
else:
textview.set_left_margin(0)
def toggle_right_margin(self, checkbutton, textview):
if checkbutton.get_active():
textview.set_right_margin(50)
else:
textview.set_right_margin(0)
def new_wrap_mode(self, radiobutton, textview, val):
if radiobutton.get_active():
textview.set_wrap_mode(val)
def new_justification(self, radiobutton, textview, val):
if radiobutton.get_active():
textview.set_justification(val)
def close_application(self, widget):
gtk.main_quit()
def __init__(self):
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_resizable(gtk.TRUE)
window.connect("destroy", self.close_application)
window.set_title("TextView Widget Basic Example")
window.set_border_width(0)
box1 = gtk.VBox(gtk.FALSE, 0)
window.add(box1)
box1.show()
box2 = gtk.VBox(gtk.FALSE, 10)
box2.set_border_width(10)
box1.pack_start(box2, gtk.TRUE, gtk.TRUE, 0)
box2.show()
sw = gtk.ScrolledWindow()
sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
textview = gtk.TextView()
textbuffer = textview.get_buffer()
sw.add(textview)
sw.show()
textview.show()
box2.pack_start(sw)
# Load the file textview-basic.py into the text window
infile = open("textview-basic.py", "r")
if infile:
string = infile.read()
infile.close()
textbuffer.set_text(string)
hbox = gtk.HButtonBox()
box2.pack_start(hbox, gtk.FALSE, gtk.FALSE, 0)
hbox.show()
vbox = gtk.VBox()
vbox.show()
161
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
gtk.WRAP_NONE)
110
111
112
113
114
gtk.WRAP_CHAR)
115
116
117
118
gtk.WRAP_WORD)
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
162
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
radio.show()
radio = gtk.RadioButton(radio, "JUSTIFY__CENTER")
vbox.pack_start(radio, gtk.FALSE, gtk.TRUE, 0)
radio.connect("toggled", self.new_justification, textview,
gtk.JUSTIFY_CENTER)
radio.show()
separator = gtk.HSeparator()
box1.pack_start(separator, gtk.FALSE, gtk.TRUE, 0)
separator.show()
box2 = gtk.VBox(gtk.FALSE, 10)
box2.set_border_width(10)
box1.pack_start(box2, gtk.FALSE, gtk.TRUE, 0)
box2.show()
button = gtk.Button("close")
button.connect("clicked", self.close_application)
box2.pack_start(button, gtk.TRUE, gtk.TRUE, 0)
button.set_flags(gtk.CAN_DEFAULT)
button.grab_default()
button.show()
window.show()
def main():
gtk.main()
return 0
if __name__ == "__main__":
TextViewExample()
main()
Las lneas 10-34 definen las retrollamadas para los botones de exclusin mtua y los botones de activacin que se usan
para cambiar los atributos predeterminados de la TextView. Las lneas 55-63 crean una ScrolledWindow que
contenga la TextView. La ScrolledWindow se empaqueta en una VBox con los botones que se crean en las lneas
72-140. El TextBuffer asociado con la TextView se rellena con el contenido del archivo fuente en las lneas 64-70.
13.3. Buffers de Texto
El TextBuffer (Buffer de Texto) es el componente principal del sistema de edicin de texto de PyGTK. Contiene el
texto, las TextTag (Etiquetas de Texto) en una TextTagTable (Tabla de Etiquetas de Texto) y las TextMark
(Marcas de Texto) que juntos describen cmo debe visualizarse el texto y permiten la modificacin del texto de forma
interactiva. Como ya se dijo en la seccin anterior, un TextBuffer est asociado con una o ms TextView (Vistas
de Texto), que muestran el contenido del TextBuffer.
Un TextBuffer se puede crear automticamente cuando se crea una TextView o se puede crear con la funcin:
textbuffer = TextBuffer(tabla=None)
donde tabla es una TextTagTable. Si no se especifica tabla (o si es None) se crear una TextTagTable para
el Buffer de Texto.
Existen numerosos mtodos que se pueden usar para:
163
164
enditer = textbuffer_get_end_iter()
startiter, enditer = textbuffer.get_bounds()
start, end = textbuffer.get_selection_bounds()
get_start_iter() crea un iterador que est justo antes del primer caracter en el buffer de texto.
get_end_iter() crea un iterador que est justo despus del ltimo caracter en el buffer de texto.
get_bounds() crea una tupla de dos iteradores que estn justo antes del primer caracter y justo detrs del ltimo
caracter del buffer de texto respectivamente.
get_selection_bounds() crea una tupla de dos iteradores que estn en las mismas posiciones que las marcas
insert y selection_bound en el buffer de texto.
13.3.3. Insercin, Obtencin y Eliminacin de Texto
El texto de un TextBuffer se puede fijar con el mtodo:
textbuffer.set_text(text)
Este mtodo reemplaza el contenido actual del buffer de texto con el texto text que se le pasa como parmetro.
El mtodo ms general para insertar caracteres en un buffer de texto es:
textbuffer.insert(iter, text)
el cual inserta el texto text en la posicin del buffer de texto especificada por el iterador iter.
Si quieres simular la insercin de texto que producira un usuario interactivo usa este mtodo:
result = textbuffer.insert_interactive(iter, text, default_editable)
el cual inserta el texto text en la posicin del buffer de texto especificada por el iterador iter pero slo si la posicin
es editable (es decir, no tiene una etiqueta que especifique que el texto no es editable) y el argumento
default_editable es TRUE. El resultado indica si el texto fue insertado o no.
El argumento default_editable indica si es editable o no cuando el texto no tiene una etiqueta que lo especifique;
default_editable normalmente se determina llamando al mtodo get_editable() de TextView.
Otros mtodos que insertan texto son:
textbuffer.insert_at_cursor(text)
result = textbuffer.insert_at_cursor_interactive(text, default_editable)
textbuffer.insert_range(iter, start, end)
result = textbuffer.insert_range_interactive(iter, start, end,
default_editable)
insert_at_cursor() es una funcin auxiliar que inserta el texto en la posicin actual del cursor (sealada por
insert).
165
insert_range() copia el texto, pixbuffers y etiquetas entre start y end de un TextBuffer (si las etiquetas
pertenecen a otro buffer de texto la tabla de etiquetas debe ser la misma) y los inserta en el buffer de texto en la posicin
especificada por iter.
Las versiones interactivas de estos mtodos funcionan de la misma forma excepto que slo insertarn el texto si las
posiciones son editables.
Finalmente, el texto se puede insertar y asociar a etiquetas al mismo tiempo usando los mtodos:
textbuffer.insert_with_tags(iter, text, tag1, tag2, ...)
textbuffer.insert_with_tags_by_name(iter, text, tagname1, tagname2, ...)
insert_with_tags() inserta el texto text en el buffer de texto en la posicin especificada por iter y le aplica las
etiquetas especificadas.
insert_with_tags_by_name() hace lo mismo pero te permite especificar las etiquetas por su nombre.
El texto de un buffer de texto se puede borrar usando los mtodos:
textbuffer.delete(start, end)
result = textbuffer.delete_interactive(start, end, default_editable)
delete() borra el texto entre los iteradores TextIter start y end en el buffer de texto.
delete_interactive() borra todo el texto editable (determinado por las etiquetas de texto aplicables y el
argumento default_editable) entre start y end.
Puedes obtener una copia del texto de un buffer de texto usando los mtodos:
text = textbuffer.get_text(start, end, include_hidden_chars=TRUE)
text = textbuffer.get_slice(start, end, include_hidden_chars=TRUE)
get_text() devuelve una copia del texto en el buffer de texto entre start y end; si el argumento
include_hidden_chars es FALSE el texto que no se visualice no se devuelve. Los caracteres que representan
imgenes incrustadas o controles, son excluidos.
get_slice() es igual que get_text() excepto que el texto que devuelve incluye un carcter 0xFFFC por cada
imgene incrustada o control.
13.3.4. Marcas de Texto (TextMark)
Las TextMarks (Marcas de Texto) son similares a los TextIter ya que tambin especifican posiciones en un
TextBuffer entre dos caracteres. Sin embargo, las Marcas de Texto TextMark mantienen su posicin aunque el
buffer cambie. Los mtodos de las Marcas de Texto TextMark se describirn en la seccin TextMark.
Un buffer de texto contiene de serie dos marcas predeterminadas: la marca insert (insertar) y la marca
selection_bound (lmite de la seleccin). La marca insert es la posicin predeterminada de insercin del texto y
la marca selection_bound combinada con la marca insert define un rango de seleccin.
Las marcas predeterminadas de serie se pueden obtener con los mtodos:
insertmark = textbuffer.get_insert()
selection_boundmark = textbuffer.get_selection_bound()
166
Las marcas insert y selection_bound se pueden colocar simultneamente en una posicin usando el mtodo:
textbuffer.place_cursor(where)
donde where es un iterador de texto que especifica la posicin. El mtodo place_cursor() es necesario para evitar
crear una seleccin temporal si las marcas se movieran individualmente.
Las TextMarks se crean usando el mtodo:
mark = textbuffer.create_mark(mark_name, where, left_gravity=FALSE)
donde mark_name es el nombre que se le asigna a la marca (puede ser None para crear una marca annima), where
es el iterador de texto que especifica la posicin de la marca en el buffer de texto y left_gravity indica dnde se
pondr la marca cuando se inserte texto en la marca (a la izquierda si es TRUE o a la derecha si es FALSE).
Se puede mover una marca en el buffer de texto usando los mtodos:
textbuffer.move_mark(mark, where)
textbuffer.move_mark_by_name(name, where)
mark especifica la marca que se va a mover. name especifica el nombre de la marca que se va a mover. where es un
iterador de texto qeu especifica la nueva posicin.
Una marca se puede borrar de un buffer de texto usando los mtodos:
textbuffer.delete_mark(mark)
textbuffer.delete_mark_by_name(name)
Se puede recuperar una marca a partir de su nombre con el mtodo:
mark = textbuffer.get_mark(name)
13.3.5. Creacin y Uso de Etiquetas de Texto
Las TextTags (Etiquetas de Texto) contienen uno o ms atributos (por ejemplo, colores de frente y de fondo, fuentes
de texto, editabilidad) que se pueden aplicar a uno o ms rangos de texto en un buffer de texto. Los atributos que se
pueden especificar mediante propiedades de una TextTag se describirn en la seccin TextTag.
Una TextTag se puede crear con atributos e instalada en la TextTagTable (Tabla de Etiquetas de Texto) de un
TextBuffer (Buffer de Texto) usando el siguiente mtodo:
tag = textbuffer.create_tag(name=None, attr1=val1, attr2=val2, ...)
donde name es una cadena de texto que especifica el nombre de la etiqueta o None si la etiqueta es una etiqueta
annima y los pares de clave-valor especifican los atributos que tendr la etiqueta. Mira la seccin TextTag para
obtener ms informacin acerca de qu atributos se pueden establecer mediante las propiedades de una TextTag.
Una etiqueta se puede aplicar a un rango de texto en un buffer de texto usando los mtodos:
textbuffer.apply_tag(tag, start, end)
textbuffer.apply_tag_by_name(name, start, end)
167
tag es la etiqueta que se va a aplicar al texto. name es el nombre de la etiqueta a aplicar. start (comienzo) y end
(final) son los iteradores de texto que especifican el rango de texto que ser afectado por la etiqueta.
Se puede borrar una etiqueta de un rango de texto usando los mtodos:
textbuffer.remove_tag(tag, start, end)
textbuffer.remove_tag_by_name(name, start, end)
Se pueden borrar todas las etiquetas de un rango de texto usando el mtodo:
textbuffer.remove_all_tags(start, end)
13.3.6. Insercin de Imgenes y Controles
Adems de texto, un TextBuffer puede contener imgenes y un punto de anclaje para controles. Se puede aadir un
control a una TextView en un punto de anclaje. Se puede aadir un control diferente en cada TextView que tenga un
buffer con un punto de anclaje.
Se puede insertar un pixbuf con el siguiente mtodo:
textbuffer.insert_pixbuf(iter, pixbuf)
donde iter especifica la posicin en el textbuffer donde insertar el pixbuf. La imagen contar como un caracter
y ser representada en lo que devuelve get_slice() (pero no en lo que devuelve get_text() ) como el caracter
Unicode "0xFFFC".
Se puede insertar un control GTK+ en una TextView en una posicin del buffer especificada por un
TextChildAnchor (Anclaje de Hijo del Texto). El TextChildAnchor contar como un carcter representado por
"0xFFFC" de manera similar a un pixbuf.
El TextChildAnchor se puede crear e insertar en el buffer usando este mtodo:
anchor = text_buffer.create_child_anchor(iter)
donde iter es la posicin del anclaje.
Tambin se puede crear e insertar un TextChildAnchor con dos operaciones por separado:
anchor = gtk.TextChildAnchor()
text_buffer.insert_child_anchor(iter, anchor)
Despus se puede aadir el control al TextView en la posicin del anclaje usando el mtodo:
text_view.add_child_at_anchor(child, anchor)
Se puede obtener la lista de controles en una posicin especifica del buffer usando el mtodo:
widget_list = anchor.get_widgets()
Tambin se puede aadir un control al TextView usando el mtodo:
text_view.add_child_in_window(child, which_window, xpos, ypos)
168
donde el control child se coloca en la ventana which_window en la posicin especificada por xpos e ypos. El
parmetro which_window indica en cul de las ventanas que componen el control TextView se colocar el control:
gtk.TEXT_WINDOW_TOP
gtk.TEXT_WINDOW_BOTTOM
gtk.TEXT_WINDOW_LEFT
gtk.TEXT_WINDOW_RIGHT
gtk.TEXT_WINDOW_TEXT
gtk.TEXT_WINDOW_WIDGET
#
#
#
#
#
#
ventana
ventana
ventana
ventana
ventana
ventana
# devuelve el desplazamiento en el
line_number = iter.get_line()
iterador
line_offset = iter.get_line_offset()
lnea
# devuelve el desplazamiento en la
numchars = iter.get_chars_in_line()
la lnea
169
Los siguientes atributos se pueden obtener a partir de un objeto de la clase TextAttributes (no est implementado
en PyGTK <=1.99.15):
bg_color
color de fondo
fg_color
color de frente
bg_stipple
fg_stipple
rise
underline
estilo de subrayado
strikethrough
draw_bg
justification
estilo de la justificacin
direction
font
PangoFontDescription en uso
font_scale
left_margin
right_margin
pixels_above_lines
pixels_below_lines
pixels_inside_wrap
tabs
PangoTabArray en uso
wrap_mode
language
PangoLanguage en uso
invisible
bg_full_height
editable
si el texto es editable
realized
pad1
pad2
pad3
pad4
13.4.3. Copiar un Iterador de Texto
Se puede duplicar un TextIter usando el mtodo:
iter_copy = iter.copy()
13.4.4. Recuperar Texto y Objetos
Se pueden obtener varias cantidades de texto y objetos de un TextBuffer usando los siguientes mtodos
TextBuffer :
char = iter.get_char()
final del buffer
text = start.get_slice(end)
iteradores de principio y fin
170
text = start.get_text(end)
iteradores de principio y fin
pixbuf = iter.get_pixbuf()
None)
tag_list = iter.get_toggled_tags()
estn activadas o desactivadas
tag_list = iter.get_tags()
prioridades
result = iter.has_tag(tag)
iterador
Estos mtodos devuelven TRUE si la marca que representa el parmetro tag satisface la condicin en las posicin del
iterador iter. En los tres primeros mtodos, si la marca representada por el parmetro tag es None entonces el
resultado es TRUE si cualquier etiqueta satisface la condicin dada en la posicin del iterador iter.
Los siguientes mtodos indican si el texto en la posicin del iterador TextIter es editable o permite insercin de
texto:
result = iter.editable()
result = iter.can_insert(default_editability)
El mtodo editable() indica si el iter est en un rango editable de texto mientras que el mtodo can_insert()
indica si el texto se puede insertar en el iterador iter considerando la editabilidad predeterminada de la TextView, el
TextBuffer y las etiquetas aplicables. La editabilidad predeterminada default_editability se obtiene con el
mtodo:
default_editability = textview.get_editable()
La equivalencia de dos iteradores TextIter se puede determinar con el mtodo:
are_equal = lhs.equal(rhs)
Dos iteradores TextIter se pueden comparar con el mtodo:
171
result = lhs.compare(rhs)
result ser: -1 si lhs es menor que rhs; 0 si lhs es igual que rhs; y, 1 si lhs es mayor que rhs.
Para determinar si un iterador TextIter est situado entre otros dos iteradores TextIter se usa el mtodo:
result = iter.in_range(start, end)
El result ser TRUE si iter est entre start y end. Atencin: start y end deben estar en orden ascendente.
Esto se puede garantizar con el mtodo:
first.order(second)
que reordenar los desplazamientos de los iteradores TextIter para que first est antes que second.
13.4.6. Comprobar la posicin en un Texto
La posicin de un TextIter con respecto al texto en un TextBuffer se puede determinar con los siguientes
mtodos:
result = iter.starts_word()
# empieza palabra
result = iter.ends_word()
# termina palabra
result = iter.inside_word()
# dentro de palabra
# termina frase
# empieza lnea
result = iter.ends_line()
# termina lnea
result ser TRUE si el TextIter est en la posicin dada. Estos mtodos son autoexplicativos. La definicin de los
elementos de texto y sus lmites se determina por el lenguaje usado en el iterador TextIter. Obsrvese que una lnea
es una coleccin de frases similar a un prrafo.
Los siguientes mtodos se pueden usar para determinar si un TextIter est al principio o al final de un
TextBuffer:
result = iter.is_start()
result = iter.is_end()
result es TRUE si el iterador TextIter est al principio o al final del TextBuffer.
Ya que un TextBuffer puede contener mltiples caracteres que se visualizan en la prctica como una nica posicin
del cursor (por ejemplo la combinacin de retorno de carro y nueva lnea o una letra con un smbolo de acento) es
posible que un TextIter pueda estar en una posicin que no corresponde con una posicin de cursor. El siguiente
mtodo indica si un iterador TextIter est en una posicin del cursor:
result = iter.is_cursor_position()
13.4.7. Movimiento a travs del Texto
172
Los TextIters se pueden mover por un TextBuffer en saltos de varias unidades de texto. La definicin de las
unidades de texto se establece en el objeto PangoLanguage que se use en la posicin del TextIter . Los mtodos
bsicos son:
result = iter.forward_char()
# avanzar un caracter
result = iter.backward_char()
# retroceder un caracter
result = iter.forward_word_end()
result = iter.backward_word_start()
palabra
result = iter.forward_sentence_end()
# retroceder al principio de la
# avanzar al principio de la
result = iter.backward_line()
lnea
# retroceder al principio de la
result = iter.forward_to_line_end()
result es TRUE si el TextIter se movi y FALSE si el TextIter est al principio o al final del TextBuffer.
Todos estos mtodos (excepto forward_to_line_end()) tienen mtodos equivalentes que reciben una cantidad
(que puede ser positiva o negativa) para mover el TextIter en un salto de mltiples unidades de texto:
result = iter.forward_chars(count)
result = iter.backward_chars(count)
result = iter.forward_word_ends(count)
result = iter.backward_word_starts(count)
result = iter.forward_sentence_ends(count)
result = iter.backward_sentence_starts(count)
result = iter.forward_lines(count)
result = iter.backward_lines(count)
result = iter.forward_cursor_positions(count)
result = iter.backward_cursor_positions(count)
13.4.8. Moverse a una Posicin Determinada
Un iterador TextIter se puede mover a una posicin especfica en el TextBuffer haciendo uso de los siguientes
mtodos:
173
iter.set_offset(char_offset)
caracteres especfico
# moverse al desplazamiento de
iter.set_line(line_number)
como parmetro
iter.set_line_offset(char_on_line)
la lnea actual
iter.forward_to_end()
Adems, un iterador TextIter se puede mover a una posicin donde una etiqueta est activada o desactivada usando
los mtodos:
result = iter.forward_to_tag_toggle(tag)
result = iter.backward_to_tag_taoggle(tag)
result es TRUE si el TextIter se movi a la nueva posicin donde exista la etiqueta tag. Si la etiqueta tag es
None entonces el iterador TextIter se mover a la siguiente posicin donde exista una etiqueta.
13.4.9. Bsqueda en el Texto
Una bsqueda de una cadena de texto en un TextBuffer se hace con los siguientes mtodos:
match_start, match_end = iter.forward_search(str, flags, limit=None)
bsqueda hacia adelante
174
setting = textmark.get_visible()
textmark.set_visible(setting)
donde setting es TRUE si la marca es visible.
El TextBuffer que contiene una TextMark se puede recuperar usando el mtodo:
buffer = textmark.get_buffer()
Puedes determinar si una TextMark ha sido borrada usando el mtodo:
setting = textmark.get_deleted()
La gravedad izquierda de una TextMark se puede recuperar usando el mtodo:
setting = textmark.get_left_gravity()
La gravedad izquierda de una TextMark indica donde acabar la marca despus de una insercin. Si la gravedad
izquierda es TRUE la marca se pondr a la izquierda de la insercin; si es FALSE, a la derecha de la insercin.
13.6. Etiquetas de Texto y Tablas de Etiquetas
Las etiquetas de texto TextTags especifican atributos que se pueden aplicar a un rango de texto en un buffer de texto.
Cada buffer de texto TextBuffer tiene una tabla de etiquetas de texto TextTagTable que contiene las etiquetas de
texto TextTags que se pueden aplicar dentro del buffer TextBuffer. Las tablas de etiquetas de texto
TextTagTable se pueden usar en ms de un buffer de texto para ofrecer estilos de texto consistentes.
13.6.1. Etiquetas de Texto
Las TextTags (Etiquetas de Texto) pueden tener nombre o ser annimas. Una TextTag se crea usando la funcin:
tag = gtk.TextTag(name=None)
Si el name (nombre) no se especifica o si es None la tag (etiqueta) ser annima. Las TextTags tambin se pueden
crear usando el mtodo de TextBuffer create_tag() que tambin te permite especificar los atributos y aade la
etiqueta a la tabla de etiquetas del buffer (vese la subseccin TextBuffer).
Los atributos que pueden aparecer en una TextTag son:
name
Lectura /
Escritura
background
Escritura
foreground
Escritura
backgroundgdk
Lectura /
Escritura
foreground-gdk
Lectura /
Escritura
backgroundstipple
Lectura /
Escritura
foregroundstipple
Lectura /
Escritura
font
Lectura /
Escritura
Descripcin de la fuente como una cadena de texto, por ejemplo, "Sans Italic 12"
175
font-desc
Lectura /
Escritura
family
Lectura /
Escritura
style
Lectura /
Escritura
variant
Lectura /
Escritura
weight
Lectura /
Escritura
Peso de la fuente como un entero, mira los valores predefinidos en PangoWeight; por
ejemplo, pango.WEIGHT_BOLD.
stretch
Lectura /
Escritura
size
Lectura /
Escritura
size-points
Lectura /
Escritura
scale
Lectura /
Escritura
pixels-abovelines
Lectura /
Escritura
pixels-belowlines
Lectura /
Escritura
pixels-insidewrap
Lectura /
Escritura
editable
Lectura /
Escritura
wrap-mode
Lectura /
Escritura
justification
Lectura /
Escritura
direction
Lectura /
Escritura
left-margin
Lectura /
Escritura
indent
Lectura /
Escritura
strikethrough
Lectura /
Escritura
right-margin
Lectura /
Escritura
underline
Lectura /
Escritura
rise
Lectura /
Escritura
Desplazamiento del texto por encima de la lnea base (por debajo de la lnea base si es
negativo) en pxeles
backgroundfull-height
Lectura /
Escritura
Si el color de fondo rellena la altura completa de la lnea o slo la altura de los caracteres
marcados
language
Lectura /
Escritura
El idioma en el que est el texto, como un cdigo ISO. Pango puede usar esto como una
ayuda para visualizar el texto. Si no entiendes este parmetro, probablemente no lo
necesitas.
176
tabs
Lectura /
Escritura
invisible
Lectura /
Escritura
Lectura / Escritura
foreground-set
Lectura / Escritura
background-stipple-set
Lectura / Escritura
foreground-stipple-set
Lectura / Escritura
family-set
Lectura / Escritura
style-set
Lectura / Escritura
variant-set
Lectura / Escritura
weight-set
Lectura / Escritura
stretch-set
Lectura / Escritura
size-set
Lectura / Escritura
scale-set
Lectura / Escritura
pixels-above-lines-set
Lectura / Escritura
pixels-below-lines-set
Lectura / Escritura
pixels-inside-wrap-set
Lectura / Escritura
editable-set
Lectura / Escritura
wrap-mode-set
Lectura / Escritura
justification-set
Lectura / Escritura
direction-set
Lectura / Escritura
left-margin-set
Lectura / Escritura
indent-set
Lectura / Escritura
strikethrough-set
Lectura / Escritura
right-margin-set
Lectura / Escritura
underline-set
Lectura / Escritura
rise-set
Lectura / Escritura
background-full-height-set
Lectura / Escritura
language-set
Lectura / Escritura
tabs-set
Lectura / Escritura
invisible-set
Lectura / Escritura
177
Por tanto, para obtener el atributo de una etiqueta, primero tienes que comprobar si el atributo ha sido establecido en la
etiqueta. Por ejemplo, para obtener un valor correcto de justificacin tienes que hacer algo as como:
if tag.get_property("justification-set"):
justification = tag.get_property("justification")
La prioridad predeterminada de una etiqueta es el orden en el que se aade a la TextTagTable. Las etiquetas con
prioridad ms alta tienen preferencia si hay mltiples etiquetas para establecer el mismo atributo para un rango de texto.
La prioridad se puede obtener y fijar con los mtodos:
priority = tag.get_priority()
tag.set_priority(priority)
La prioridad de una etiqueta debe estar entre 0 y uno menos del tamao de la TextTagTable.
13.6.2. Tablas de Etiquetas de Texto
Una TextTagTable (Tabla de Etiquetas de Texto) se crea al crear un TextBuffer . Tambin se puede crear una
TextTagTable con la funcin:
table = TextTagTable()
Se puede aadir una TextTag (Etiqueta de Texto) a una TextTagTable usando el mtodo:
table.add(tag)
La etiqueta tag no puede estar ya en la tabla y no puede tener el mismo nombre que otra etiqueta en la tabla.
Es posible buscar una etiqueta en una tabla con el mtodo:
tag = table.lookup(name)
Este mtodo devuelve la etiqueta tag en la tabla que tenga el nombre name o None si no hay ninguna etiqueta con ese
nombre.
Se puede borrar una TextTag de una TextTagTable con el mtodo:
table.remove(tag)
El tamao de la TextTagTable se puede consultar con el mtodo:
size = table.get_size()
13.7. Un ejemplo de Vista de Texto
El programa de ejemplo testtext.py (que se deriva del programa testtext.c incluido en la distribucin GTK+ 2.0.x)
demuestra el uso del control TextView y sus objetos asociados: TextBuffers, TextIters, TextMarks,
TextTags, TextTagTables. La figura Figura 13.2, Ejemplo de Vista de Texto ilustra su funcionamiento:
Figura 13.2. Ejemplo de Vista de Texto
178
El programa testtext.py define unas cuantas clases adems de la clase principal TestText:
La clase Buffer , en las lneas 99-496, es una subclase de la clase gtk.TextBuffer. Proporciona las
capacidades de edicin del buffer que usan los objetos View.
La clase View , lneas 498-1126, es una subclase de la clase gtk.Window y contiene un objeto
gtk.TextView que usa un objeto Buffer en lugar de un objeto gtk.TextBuffer. Proporciona una
ventana y visualiza los contenidos de un objeto Buffer adems de una barra de men.
La clase FileSel , lneas 73-97, es una subclase de la clase gtk.FileSelection que proporciona una
seleccin de ficheros para los contenidos de la clase Buffer .
La clase Stack proporciona objetos de pilas simples.
La ventana de ciclo de color se implementa usando etiquetas de texto que se aplican a una seccin del texto en un buffer.
Las lneas 109-115 (en el mtodo __init__() ) crean estas etiquetas y las lneas 763-784 (en el mtodo
do_apply_colors() ) aplican las etiquetas de colores a una seccin del texto de dos en dos caracteres. Las lienas
202-239 proporcionan los mtodos (color_cycle_timeout(), set_colors() y cycle_colors()) que
producen el ciclo de color cuando est activado. El ciclo de color se activa estableciendo (lnea 220) la propiedad
foreground_gdk de las etiquetas individuales color_tags (que tambin establecen la propiedad
foreground_set ). El ciclo de color se desactiva poniendo la propiedad foreground_set a FALSE (lnea 222).
Los colores se cambian periodicamente al desplazar el tono del color ( start_hue (line 237))
Un nuevo Buffer se rellena con un contenido de ejemplo cuando se selecciona el men Test Example (el mtodo
fill_example_buffer() en las lneas 302-372). El buffer de ejemplo contiene texto de varios colores, estilos,
idiomas y pixbuffers. El mtodo init_tags() (lneas 260-300) establece una variedad de TextTags para usarlas con
179
el texto de ejemplo. La seal de evento de estas etiquetas se conecta al mtodo tag_event_handler() (lneas 241256) para ilustrar la captura de los eventos de botn y movimiento.
El modo de ajuste del control TextView est puesto a WRAP_WORD (lnea 580) y las ventanas de borde del
TextView se visualizan al establecer su tamao en las lneas 587-588 y las lneas 596-597. Las ventanas del borde
derecho e izquierdo se usan para mostrar los nmeros de lnea y las ventanas de borde superior e inferior muestran las
posiciones de tabulacin cuando se utilizan tabulaciones personalizadas. Las ventanas de borde se actualizan cuando se
recibe la seal "expose-event" en el TextView (lneas 590 y 599) El mtodo line_numbers_expose() (lneas
1079-1116) determina si las ventanas de borde izquierdo o derecho tienen un evento de exposicin y, si es as, calculan
el tamao del rea de exposicin. Entonces, la localizacin del principio de la lnea y el nmero de lnea para cada lnea
en el rea de exposicin se calcula en el mtodo get_lines() (lneas 1057-1077). Los nmeros de lnea se dibujan en
la ventana de borde en la posicin (transformada por la lnea 1109).
Las posiciones personalizadas de tabulacin se visualizan en las ventanas de borde superior e inferior de una forma
similar (lneas 1013-1055). Slo se visualizan cuando el cursor se mueve dentro de un rango de texto que tiene el
atributo de tabulaciones propias. Esto se detecta manejando la seal "mark-set" en el mtodo
cursor_set_handler() (lneas 999-1011) e invalidando las ventanas de borde superior e inferior si la marca a
activar es la marca insert.
Los controles mviles se aaden al objeto View con el mtodo do_add_children() (lneas 892-899) el cual llama al
mtodo add_movable_children() (lneas 874-890). Los hijos son gtk.Labels (Etiquetas) que pueden
arrastrarse por las ventanas que forman parte de un control TextView .
De la misma forma, los controles se aaden a las ventanas del TextView de una View y el Buffer usando el mtodo
do_add_focus_children() (lneas 901-949).
Captulo 14. Control de Vista de rbol (TreeView)
El control TreeView (control de vista de rbol) muestra listas y rboles de mltiples columnas. Sustituye los antiguos
controles List, CList, Tree y CTree por un conjunto de objetos mucho ms potente y flexible que utilizan el patrn
Modelo-Vista-Controlador (MVC) para proporcionar las siguientes funcionalidades:
Obviamente, todas estas posibilidades conllevan el coste de un conjunto de objetos e interfaces significativamente ms
complejo y que puede llegar a parecer intimidatorio inicialmente. En el resto del captulo exploraremos los objetos e
interfaces de la vista de rbol (TreeView) para comprender sus usos habituales. Los usos ms esotricos tendrn que ser
investigados por el lector.
Comenzaremos con una rpida visin general de objetos e interfaces para posteriormente bucear en la interfaz de
TreeModel y en las clases predefinidas ListStore y TreeStore.
14.1. Introduccin
El control TreeView (vista de rbol) es el objeto de interfaz de usuario que muestra los datos almacenados en un
objeto que implemente la interfaz TreeModel. En PyGTK 2.0 se proporcionan dos clases base para modelos de rbol.:
180
TreeStore (almacn de datos en rbol), que proporcionan almacenamiento para datos jerrquicos,
organizndolos como filas de un rbol y con los datos en columnas. Cada fila del rbol puede tener cero o ms
filas hijas, aunque todas las filas deben tener el mismo nmero de columnas.
ListStore (almacn de datos en lista), que proporciona almacenamiento para datos tabulares, con una
organizacin en filas y columnas, de una manera similar a la tabla de una base de datos relacional. Un
ListStore (almacn de datos en lista) realmente consiste en una versin simplificada de un TreeStore
(almacn de datos en rbol), cuyas filas no tienen descendientes. Fue creada para proporcionar una interfaz ms
sencilla (y presuntamente ms eficiente) a un modelo de datos tan comn. Y,
TreeModelSort, que proporciona un modelo en el que los datos del modelo de rbol subyacente se
mantienen ordenados. Y,
TreeModelFilter, que proporciona un modelo que contiene un subconjunto de los datos del modelo
subyacente. Este modelo nicamente est disponible desde la versin de PyGTK 2.4 y posteriores.
Un TreeView (vista de rbol) muestra todas las filas que contiene un TreeModel, pero permite mostrar
selectivamente algunas de las columnas. Tambin es posible presentar las columnas en un orden diferente al de
almacenamiento en el TreeModel.
Un TreeView usa objetos del tipo TreeViewColumn (columnas de vista de rbol) para organizar la visualizacin de
los datos en columnas. Cada TreeViewColumn muestra una columna con un encabezado opcional que puede contener
los datos de diversas columnas de un TreeModel. Los objetos TreeViewColumn se empaquetan (de forma
semejante a los contenedores HBox) con objetos CellRenderer (intrpretes de celda), para generar la visualizacin
de los datos asociados situados en una fila y columna de un TreeModel. Existen tres clases de CellRenderer
predefinidas:
CellRendererPixbuf, que muestra una imagen del tipo pixbuf en las celdas de un TreeViewColumn.
CellRendererText, que muestra una cadena en las celdas de un TreeViewColumn. Si es necesario
convierte los datos de la columna a cadenas de texto. Por ejemplo, si se mostrase una columna de datos
consistente en nmeros en coma flotante, el CellRendererText los convertira en cadenas de texto antes de
mostrarlos.
CellRendererToggle, que muestra un valor booleano como un botn conmutable en las celdas de un
TreeViewColumn.
Una TreeViewColumn puede contener varios objetos CellRenderer para proporcionar una columna que, por
ejemplo, pueda contener una imagen y un texto juntos.
Finalmente, los objetos TreeIter (iterador de rbol), TreeRowReference (referencia de fila de rbol) y
TreeSelection (seleccin de rbol) proporcionan un puntero transitorio a la fila de un TreeModel, un puntero
persistente a la fila de un TreeModel y un objeto que gestiona una seleccin en una TreeView.
La visualizacin de un TreeView se compone de las siguientes operaciones generales (aunque no necesariamente en
este orden):
Se crea un objeto de modelo de rbol (TreeModel), generalmente un ListStore o un TreeStore con una
o ms columnas de un tipo de datos determinado.
El modelo de rbol puede entonces llenarse con una o ms filas de datos.
Se crea un control TreeView y se asocia al modelo de rbol.
Se crean una o ms columnas TreeViewColumn y se insertan en el TreeView. Cada una de ellas resultar
en la visualizacin de una columna.
Para cada TreeViewColumn se crea uno o ms intrpretes de celda CellRenderer, que son aadidos a
cada TreeViewColumn.
Se fijan los atributos de cada CellRenderer para indicar de qu columnas del modelo de rbol han de tomar
los datos del atributo, como por ejemplo, el texto que se ha de mostrar. Esto permite que los CellRenderer
muestren las columnas de cada fila de forma distinta.
181
El TreeView se inserta y es mostrado en una ventana (Window) o una ventana con barras de desplazamiento
(ScrolledWindow).
Los datos del modelo de rbol se manipulan programticamente en respuesta a las acciones de los usuarios y
usuarias. El TreeView rastrear de forma automtica los cambios.
#!/usr/bin/env python
# example basictreeview.py
import pygtk
pygtk.require('2.0')
import gtk
class BasicTreeViewExample:
# close the window and quit
def delete_event(self, widget, event, data=None):
gtk.main_quit()
return gtk.FALSE
def __init__(self):
# Create a new window
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.set_title("Basic TreeView Example")
self.window.set_size_request(200, 200)
self.window.connect("delete_event", self.delete_event)
# create a TreeStore with one string column to use as the model
self.treestore = gtk.TreeStore(str)
# we'll add some data now - 4 rows with 3 child rows each
for parent in range(4):
piter = self.treestore.append(None, ['parent %i' % parent])
for child in range(3):
self.treestore.append(piter, ['child %i of parent %i' %
(child, parent)])
# create the TreeView using treestore
self.treeview = gtk.TreeView(self.treestore)
# create the TreeViewColumn to display the data
self.tvcolumn = gtk.TreeViewColumn('Column 0')
# add tvcolumn to treeview
self.treeview.append_column(self.tvcolumn)
# create a CellRendererText to render the data
self.cell = gtk.CellRendererText()
# add the cell to the tvcolumn and allow it to expand
self.tvcolumn.pack_start(self.cell, True)
# set the cell "text" attribute to column 0 - retrieve text
# from that column in treestore
self.tvcolumn.add_attribute(self.cell, 'text', 0)
182
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# make it searchable
self.treeview.set_search_column(0)
# Allow sorting on the column
self.tvcolumn.set_sort_column_id(0)
# Allow drag and drop reordering of rows
self.treeview.set_reorderable(True)
self.window.add(self.treeview)
self.window.show_all()
def main():
gtk.main()
if __name__ == "__main__":
tvexample = BasicTreeViewExample()
main()
En programas reales el TreeStore probablemente se llenara de datos despus de que la visualizacin del TreeView
se produjese por una accin del usuario. Nos detendremos en los detalles de las interfaces de TreeView en secciones
posteriores. Figura 14.1, Programa elemental de ejemplo de TreeView muestra la ventana creada por el programa
basictreeview.py tras la expanxin de un par de filas madre.
Figura 14.1. Programa elemental de ejemplo de TreeView
la obtencin de las caractersticas del conjunto de datos que almacena, tales como el nmero de columnas y el
tipo de datos de stas.
la obtencin de un iterador (TreeIter), una referencia temporal que apunta a una fila del modelo.
183
la obtencin de informacin sobre un nodo (o fila), tal como el nmero de nodos que descienden de l, una lista
de stos nodos hijos, el contenido de sus columnas y un puntero al nodo padre.
informar de los cambios que se produzcan elos datos del modelo (TreeModel).
tipos bsicos de Python, tal como: int, str, long, float y object.
tipos de PyGTK tales como Button, VBox, gdk.Rectangle, gdk.Pixbuf, etc.
tipos GObject (GTypes de GTK+) especificados bien como constantes GObject Type o como cadenas. La
mayora de GTypes son traducidos a un tipo de Python:
o gobject.TYPE_CHAR o 'gchar'
o gobject.TYPE_UCHAR o 'guchar'
o gobject.TYPE_BOOLEAN o 'gboolean'
o gobject.TYPE_INT o 'gint'
o gobject.TYPE_UINT o 'guint'
o gobject.TYPE_LONG o 'glong
o gobject.TYPE_ULONG o 'gulong
o gobject.TYPE_INT64 o 'gint64'
o gobject.TYPE_UINT64 o 'guint64'
o gobject.TYPE_FLOAT o 'gfloat'
o gobject.TYPE_DOUBLE o 'gdouble'
o gobject.TYPE_STRING o 'gchararray'
o gobject.TYPE_OBJECT o 'GObject
Por ejemplo, para crear un ListStore o TreeStore cuyas columnas contuviesen un gdk.Pixbuf (un tipo de
imagen), un entero, una cadena y un tipo booleano habra que hacier algo as:
liststore = ListStore(gtk.gdk.Pixbuf, int, str, 'gboolean')
treestore = TreeStore(gtk.gdk.Pixbuf, int, str, 'gboolean')
Una vez que se crea un almacn de datos en Lista (ListStore) o un almacn de datos en rbol (TreeStore) y sus
columnas han sido definidas, ya no es posible hacer en ellos cambios o modificaciones. As mismo es importante tener
en cuenta que no existe una relacin predefinida entre las columnas de una vista de rbol (TreeView) y las columnas
de su correspondiente modelo (TreeModel). Es decir, la quinta columna de datos de un modelo (TreeModel) puede
mostrarse en la primera columna de una determinada vista de rbol (TreeView) y en la tercera columna en otra vista
diferente. Por tanto, no es necesario preocuparse de cmo se mostrarn los datos a la hora de crear los almacenes de
datos (Stores).
Si estos dos tipos de almacenes de datos no se ajustan a las necesidades de una aplicacin es posible definir otros
almacenes de datos personalizados en Python siempre que stos implementen la interfaz TreeModel. Retomaremos esta
cuestin ms adelante en la seccin dedicada a GenericTreeModel .
14.2.3. Cmo referirse a las filas de un modelo TreeModel
Antes de que podamos hablar de la gestion de las filas de datos de un almacn TreeStore o ListStore necesitamos
una forma de especificar a qu columna nos queremos referir. PyGTK dispone de tres formas de indicar columnas de un
modelo TreeModel rows: un camino de rbol, un iterador TreeIter y una referencia TreeRowReference.
14.2.3.1. Caminos de rbol (Tree Paths)
Un camino de rbol es una representacin mediante enteros, una cadena o una tupla de la localizacin de una fila en un
almacn de datos. Un valor entero especifica la fila del nivel superior del almacn, empezando por 0. Por ejemplo, un
184
valor de camino de 4 especificara la quinta fila del almacn de datos. De la misma manera, una representacin mediante
una cadena resultara "4" y la representacin como tupla (4,). Esto es suficiente para llegar a determinar cualquier fila en
un almacn de datos en lista (ListStore), pero en el caso de un rbol (TreeStore) necesitamos poder indicar las
filas hijas. Para este caso debemos usar la representacin mediante una cadena o una tupla.
Dado que un almacn en rbol (TreeStore) puede tener una jerarqua de una profudidad arbitraria, la representacin
como cadena especifica el camino desde el nivel ms alto hasta la fila escogida utilizando enteros separados mediante el
carcter ":". De forma similar, la representacin mediante una tupla especifica el camino en el rbol empezando desde el
vrtice hasta la fila como una secuencia de enteros. Como ejemplos correctos de representaciones con cadenas de
caminos tenemos: "0:2" (especifica la fila que es la tercera hija de la primera fila) y "4:0:1" (especifica la fila que es la
segunda hija del primer hijo de la quinta fila). De forma semejante, los mismos caminos son representados
respectivamente por las tuplas (0, 2) y (4, 0, 1).
Un camino de rbol es la nica manera en la que se puede hacer corresponder una fila de una vista de rbol
(TreeView) a una fila de un modelo (TreeModel) debido a que los caminos de una y otra son iguales. Tambin
existen otros problemas con los caminos de rbol:
un camino de rbol puede especificar una fila que no existe en el almacn ListStore o TreeStore.
un camino de rbol puede apuntar a datos distintos tras insertar o borrar una fila en un ListStore o
TreeStore.
PyGTK usa la representacin como tupla al devolver caminos, pero acepta cualquiera de las tres formas de representar
un camino. Por coherencia se debera usar la representacin en forma de tupla.
Se puede obtener un camino de rbol a partir de un iterador TreeIter utilizando el mtodo get_path():
path = store.get_path(iter)
donde iter es un iterador TreeIter que apunta a una fila en el almacn y path es la tupla que representa el camino
a la fila.
14.2.3.2. Iteradores TreeIter
Un iterador TreeIter es un objeto que aporta una referencia transitoria a la fila de un almacn ListStore o
TreeStore. Si los contenidos del almacn cambian (generalmente porque se ha aadido o quitado una fila) el iterador
TreeIter puede no ser ya vlido. Un modelo TreeModel que d soporte a iteradores persistentes debe establecer la
bandera gtk.TREE_MODEL_ITERS_PERSIST. Una aplicacin puede comprobar dicha bandera haciendo uso del
mtodo get_flags().
Los iteradore TreeIter se crean utilizando alguno de los mtodos del modelo TreeModel, que son aplicables tanto
a objetos del tipo TreeStore como ListStore:
treeiter = store.get_iter(path)
donde treeiter apunta a la fila del camino path. La excepcin ValueError se activa en el caso de que el camino no
sea vlido.
treeiter = store.get_iter_first()
donde treeiter es un iterador TreeIter que apunta a la fila en el camino (0,). treeiter ser None si el almacn
est vaco.
treeiter = store.iter_next(iter)
donde treeiter es un iterador TreeIter que apunta a la siguiente fila en el mismo nivel que el iterador TreeIter
especificado por iter. treeiter tendr el valor None si no hay una fila siguiente (iter tambin es invalidado).
185
Los siguientes mtodos son de utilidad nicamente cuando se vaya a obtener un iterador TreeIter a partir de un
almacn de datos de rbol (TreeStore):
treeiter = treestore.iter_children(parent)
donde treeiter es un iterador TreeIter que apunta a la primera fija que desciende de la fila indicada por el
iterador TreeIter indicado por parent. treeiter ser None en caso de que no tenga descendientes.
treeiter = treestore.iter_nth_child(parent, n)
donde treeiter es un iterador TreeIter que seala la fila hija (de ndice n) de la fila especificada por el iterador
TreeIter parent. parent puede ser None para obtener una fila del nivel superior . treeiter ser None si no
hay descendientes.
treeiter = treestore.iter_parent(child)
donde treeiter es un TreeIter que apunta a la fila madre de la fila especificada por el iterador TreeIter
child. treeiter ser None si no hay descendientes.
Se puede obtener un camino a partir de un iterador TreeIter usando el mtodo get_path() :
path = store.get_path(iter)
donde iter es un iterador Treeiter que apunta a una fila del almacn, y path es su camino expresado como tupla.
14.2.3.3. Referencias persistentes a filas (TreeRowReferences)
Una referencia del tipo TreeRowReference es una referencia persistente a una fila de datos en un almacn. Mientras
que el camino (es decir, su localizacin) de una fila puede cambiar a medida que se aden o quitan filas al almacn, una
referencia del tipo TreeRowReference apuntar a la misma fila de datos en tanto exista.
Nota
Las referencias TreeRowReference nicamente estn disponibles a partir de la versin 2.4 de
PyGTK.
Se puede crear una referencia del tipo TreeRowReference utilizando su constructor:
treerowref = TreeRowReference(model, path)
donde model es el modelo TreeModel que contiene la fila y path es el camino de la fila que hay que referenciar. Si
path no es un camino vlido para el modelo model entonces se devuelve None.
14.2.4. Adicin de filas
14.2.4.1. Adicin de filas a un almacn de datos del tipo ListStore
Una vez que se ha creado un almacn del tipo ListStore se han de aadir filas utilizando alguno de los mtodos
siguientes:
iter
iter
iter
iter
iter
=
=
=
=
=
append(row=None)
prepend(row=None)
insert(position, row=None)
insert_before(sibling, row=None)
insert_after(sibling, row=None)
186
Cada uno de estos mtodos insertan una fila en una posicin implcita o especificada en el almacn ListStore. Los
mtodos append() y prepend() usan posiciones implcitas: tras la ltima fila y antes de la primera fila,
respectivamente. El mtodo insert() requiere un entero (el parmetro position) que especifica la localizacin en la
que se insertar la fila. Los otros dos mtodos necesitan un iterador TreeIter (sibling) que hace referencia a una
fila en el almacn ListStore ante o tras la cual se insertar la fila.
El parmetro row especifica los datos que deberan ser insertados en la fila tras su creacin. Si row es None o no se
especifica, se crea una fila vaca. Si row se especifica debe ser una tupla o una lista que contenga tantos elementos como
el nmero de columnas que posea el almacn ListStore. Los elementos tambin deben coincidir con los tipos de las
correspondientes columnas del almacn ListStore.
Todos los mtodos devuelven un iterador TreeIter que apunta a la fila recin insertada. El siguiente fragmento de
cdigo ilustra la creacin de un almacn ListStore y la adicin de filas de datos en l:
...
liststore = gtk.ListStore(int, str, gtk.gdk.Color)
liststore.append([0,'red',colormap.alloc_color('red')])
liststore.append([1,'green',colormap.alloc_color('green')])
iter = liststore.insert(1, (2,'blue',colormap.alloc_color('blue')) )
iter = liststore.insert_after(iter, [3,'yellow',colormap.alloc_color('blue')])
...
14.2.4.2. Adicin de filas a un almacn de datos del tipo TreeStore
La adicin de filas a un almacn del tipo TreeStore es semejante a la operacin sobre el tipo ListStore, salvo que
tambin se debe especificar una fila madre (usando un iterador TreeIter) a la que aadir la nueva fila. Los mtodos
para el amacn TreeStore son:
iter
iter
iter
iter
iter
=
=
=
=
=
append(parent, row=None)
prepend(parent, row=None)
insert(parent, position, row=None)
insert_before(parent, sibling, row=None)
insert_after(parent, sibling, row=None)
187
En el caso de aadir un gran nmero de filas, desconectar el modelo (TreeModel) de su vista (TreeView)
usando el mtodo set_model() con el parmetro model puesto a None evitar la actualizacin de la vista
(TreeView) con cada insercin.
Igualmente til puede ser desactivar la ordenacin al insertar un gran nmero de filas. Para ello se usa el mtodo
set_default_sort_func() con el parmetro sort_func puesto a None.
Limitar el nmero de referencias del tipo TreeRowReference en uso tambin es importante, puesto que se
actualiza su camino con cada adicin o emilinacin de filas.
Fijar la propiedad de la vista (TreeView) "fixed-height-mode" a TRUE, de forma que todas las filas tengan la
misma altura y se evite el clculo individual de la altura de cada fila. Esta opcin solamente est disponible a
partir de la versin 2.4 de PyGTK.
188
189
liststore.reorder(new_order)
donde new_order es una lista de enteros que especifican el nuevo orden de filas de la siguiente manera:
new_order[nuevaposicin] = antiguaposicin
Por ejemplo, si liststore contuviese cuatro filas:
'one'
'two'
'three'
'four'
La llamada al mtodo:
liststore.reorder([2, 1, 3, 0])
producira el siguiente orden:
'three'
'two'
'four'
'one'
Nota
Estos mtodos nicamente reorganizarn almacenes ListStore no ordenados.
En el caso de querer reorganizar las filas de datos en la versin 2.0 de PyGTK es necesario recurrir a la eliminacin e
insercin de filas utilizando los mtodos descritos en las secciones Adicin de filas y Eliminacin de filas.
14.2.6.3. Reorganizacin de filas en almacenes TreeStore
Los mtodos que se usan para reorganizar las filas de un almacn TreeStore son semejantes a los utilizados con los
almacenes del tipo ListStore, exceptuando que los primeros nicamente afectan a las filas hijas de una determinada
fila madre. Es decir, no es posible, por ejemplo, intercambiar filas con filas madre distintas.:
treestore.swap(a, b)
treestore.move_after(iter, position)
treestore.move_before(iter, position)
swap() intercambia las posiciones de las filas hija indicadas por los iteradores (TreeIter) a y b. a y b deben compartir
su fila madre. move_after() y move_before() mueven la fila indicada por el iterador (TreeIter) iter a una
posicin posterior o anterior a la fila sealada por el iterador (TreeIter) position. iter y position deben
compartir su fila madre. Si position tiene el valor None, move_after() situar la fila al principio del almacn,
mientras que el mtodo move_before() lo har al final del mismo.
El mtodo reorder() precisa un parmetro adicional que especifique la fila madre cuyas filas hijas sern reordenadas:
treestore.reorder(parent, new_order)
donde new_order es una lista de enteros que especifican el nuevo orden de filas hijas de la fila madre especificada por
el iterador (TreeIter) parent de la siguiente manera:
new_order[nuevaposicin] = antiguaposicin
Por ejemplo, si treestore contuviese estas cuatro filas:
190
'parent'
'one'
'two'
'three'
'four'
La llamada al mtodo:
treestore.reorder(parent, [2, 1, 3, 0])
producira el siguiente orden:
'parent'
'three'
'two'
'four'
'one'
Nota
Estos mtodos nicamente reorganizarn almacenes TreeStore no ordenados.
14.2.6.4. Gestin de mltiples filas
Uno de los aspectos ms problemticos de la manipulacin de almacenes ListStore y TreeStore es el trabajo con
mltiples filas, como por ejemplo, mover varias filas de una fila madre a otra, o eliminar un conjunto de filas en funcin
de determinados criterios. La dificultad surge de la necesidad de utilizar un iterador TreeIter que puede no ser ya
vlido como resultado de la operacin que se realice. Es posible comprobar si los iteradores de unos almacenes
ListStore y TreeStore con persistentes utilizando el mtodo get_flags() y contrastando la existencia de la
bandera gtk.TREE_MODEL_ITERS_PERSIST. Sin embargo, las clases apilables del tipo TreeModelFilter y
TreeModelSort no poseen iteradores TreeIter persistentes.
Si aceptamos que los iteradores TreeIter no son persistentes, cmo movemos todas las filas hijas de una dada a
otra?. Para ello tenemos que:
No podemos confiar en que el mtodo remove() devuelva un iterador TreeIter correcto, por lo que simplemente
solicitaremos el iterador del primer descendiente hasta que devuelva None. Una funcin posible para mover filas hijas
sera:
def move_child_rows(treestore, from_parent, to_parent):
n_columns = treestore.get_n_columns()
iter = treestore.iter_children(from_parent)
while iter:
values = treestore.get(iter, *range(n_columns))
treestore.remove(iter)
treestore.append(to_parent, values)
iter = treestore.iter_children(from_parent)
return
La funcin anterior abarca el caso sencillo en el que se mueven todas las filas hijas de una nica fila madre, pero qu
ocurre si se desea elminar todas las filas del almacn TreeStore en funcin de determinado criterio, por ejemplo, el
valor de la primera columna?. A primera vista, se podra pensar en la posibilidad de usar el mtodo foreach(), de
manera que se itere sobre todas las filas y entonces se eliminaran las que cumpliesen el criterio elegido:
191
store.foreach(func, user_data)
donde func es una funcin que es llamada por cada fila y tiene la siguiente signatura:
def func(model, path, iter, user_data):
donde model es el almacn de datos TreeModel, path es el camino de una fila en el modelo model, iter es un
iterador TreeIter que apunta al camino path y user_data son los datos aportados. Si func devuelve TRUE
entonces el mtodo foreach() dejar de iterar y retornar.
El problema con este enfoque es que al cambiar los contenidos del almacn mientras se produce la iteracin del mtodo
foreach() puede dar lugar a resultados impredecibles. El uso del mtodo foreach() para crear y guardar referencias
persistentes TreeRowReferences de las filas que van a ser eliminadas, para posteriormente eliminar estas referencias tras
la finalizacin del mtodo foreach() podra ser una estrategia adecuada, salvo que no funcionara en las versiones 2.0
y 2.2 de PyGTK, donde no estn disponibles las referencias persistentes del tipo TreeRowReference.
Una estrategia fiable que abarca todas las variantes de PyGTK consiste en llamar al mtodo foreach() para reunir los
caminos de las filas que se van a eliminar y luego proceder a eliminar dichas filas en orden inverso, de forma que se
preserve la validez de los caminos en el rbol. El siguiente fragmento de cdigo ejemplifica esta estrategia:
...
# esta funcin selecciona una fila si el valor de la primera columna es >= que
el valor de comparacin
# data es una tupla que contiene el valor de comparacin y una lista para
guardar los caminos
def match_value_cb(model, path, iter, data):
if model.get_value(iter, 0) >= data[0]:
data[1].append(path)
return False
# mantiene la iteracin de foreach
pathlist = []
treestore.foreach(match_value_cb, (10, pathlist))
# foreach funciona en orden de llegada (FIFO)
pathlist.reverse()
for path in pathlist:
treestore.remove(treestore.get_iter(path))
...
En el caso de que se quisiese buscar en un almacn TreeStore la primera fila que cumpliese determinado criterio,
probablemente sera preferible llevar a cabo personalmente la iteracin con algo similar a:
treestore = TreeStore(str)
...
def match_func(model, iter, data):
column, key = data # data es una tupla que contiene nmero de columna,
clave (key)
value = model.get_value(iter, column)
return value == key
def search(model, iter, func, data):
while iter:
if func(model, iter, data):
return iter
result = search(model, model.iter_children(iter), func, data)
if result: return result
iter = model.iter_next(iter)
return None
...
match_iter = search(treestore, treestore.iter_children(None),
192
= model[0]
= model['0']
= model["0"]
= model[(0,)]
model.get_iter(0)
= model[i]
model[(0,0)]
Adems, se pueden fijar los valores en una fila existente de forma parecida a esto:
...
liststore = gtk.ListStore(str, int, object)
...
liststore[0] = ['Button', 23, gtk.Button('Label')]
Los objetos TreeModelRow de PyGTK soportan los protocolos de Python de secuencia e iterador. Se puede obtener
un iterador para recorrer los valores de columna en una fila o utilizar la instruccin for as como la comprensin de
listas. Un TreeModelRow utiliza el nmero de columna como ndice para extraer un valor. Por ejemplo:
...
liststore = gtk.ListStore(str, int)
liststore.append(['Random string', 514])
...
row = liststore[0]
value1 = row[1]
193
value0 = liststore['0'][0]
for value in row:
print value
val0, val1 = row
...
Haciendo uso del ejemplo de la seccin anterior para iterar sobre un almacn TreeStore y localizar una fila que
contenga un valor concreto, el cdigo quedara:
treestore = TreeStore(str)
...
def match_func(row, data):
column, key = data # data es una tupla que contiene nmero de columna,
clave (key)
return row[column] == key
...
def search(rows, func, data):
if not rows: return None
for row in rows:
if func(row, data):
return row
result = search(row.iterchildren(), func, data)
if result: return result
return None
...
match_row = search(treestore, match_func, (0, 'foo'))
Tambin se puede fijar el valor de una columna utilizando:
treestore[(1,0,1)][1] = 'abc'
Un TreeModelRow tambin soporta la instruccin del y la conversin a listas y tuplas utilizando las funciones de
Python list() and tuple(). Tal como se ilustra en el ejemplo superior, un objeto TreeModelRow posee el mtodo
iterchildren(), que devuelve un iterador para recorrer las filas hijas del objeto TreeModelRow.
14.2.8. Seales de TreeModel
Las aplicaciones pueden seguir los cambios de un modelo TreeModel conectndose a las seales que son emitidas por
un modelo TreeModel: "row-changed" (fila modificada), "row-deleted" (fila eliminada), "row-inserted" (fila
insertada), "row-has-child-toggled" (cambio de fila tiene descendencia) y "rows-reordered" (filas reordenadas). Estas
seales son utilizadas por las vistas TreeView para seguir los cambios en su modelo TreeModel.
Si una aplicacin se conecta a estas seales es posible que se produzcan grupos de seales al llamar algunos mtodos.
Por ejemplo, una llamada para aadir la primera fila a una fila madre:
treestore.append(parent, ['qwe', 'asd', 123])
causar la emisin de las siguientes seales:
Ntese que no es posible obtener el orden de las fila en la retrollamada de "rows-reordered" puesto que el nuevo orden
de las filas se pasa como un puntero opaco a un vector de enteros.
194
Consulte el Manual de Referencia de PyGTK para saber ms sobre las seales de TreeModel.
14.2.9. Ordenacin de filas de modelos TreeModel
14.2.9.1. La interfaz TreeSortable
Los objetos ListStore y TreeStore implementan la interfaz TreeSortable que les aporta mtodos para
controlar la ordenacin de las filas de un modelo TreeModel. El elemento clave de la interfaz es "sort column ID", que
es un valor entero arbitrario que est referido a una funcin de comparacin de orden y a unos datos asociados de
usuario. Dicho identificador de orden de columna debe ser mayor o igual a cero, y se crea utilizando el siguiente
mtodo:
treesortable.set_sort_func(sort_column_id, sort_func, user_data=None)
donde sort_column_id es un valor entero asignado por el programador, sort_func es una funcin o mtodo
utilizado para comparar filas y user_data son los datos contextuales. sort_func tiene la siguiente signatura:
def sort_func_function(model, iter1, iter2, data)
def sort_func_method(self, model, iter1, iter2, data)
donde model el el modelo TreeModel que contiene las filas sealadas por los iteradores TreeIter iter1 e
iter2 y data es user_data. sort_func debera devolver: -1 si la fila correspondiente a iter1 debe preceder a
la fila correspondiente a iter2; 0, si las filas son iguales; y, 1 si la fila correspondiente a iter2 debe preceder a la
indicada por iter1. La funcin de comparacin de orden debera presuponer siempre que el orden de clasificacin es
ascendente (gtk.SORT_ASCENDING) puesto que el orden de clasificacin ser tenido en cuenta por las
implementaciones de TreeSortable.
Puede usarse la misma funcin de comparacin de orden para varios identificadores de orden de columna, haciendo
variar los datos de informacin contextual contenidos en user_data. Por ejemplo, los datos user_data
especificados en el mtodo set_sort_func() podran consistir en los ndices de las columnas de donde se extraeran
los datos de clasificacin.
Una vez que se ha creado un identificador de orden de columna (sort column ID) en un almacn, es posible usarla para
ordenar los datos llamando al mtodo:
treesortable.set_sort_column_id(sort_column_id, order)
donde order es el orden de clasificacin, bien gtk.SORT_ASCENDING (ascendente) o gtk.SORT_DESCENDING
(descendente).
Un identificador de orden de columna (sort_column_id) de valor igual a -1 indica que el almacn debera utilizar la
funcin de comparacin por defecto que se establece a travs del mtodo:
treesortable.set_default_sort_func(sort_func, user_data=None)
Es posible comprobar si un almacn tiene una funcin de comparacin por defecto haciendo uso del mtodo:
result = treesortable.has_default_sort_func()
que devuelve TRUEsi se ha establecido una funcin por defecto para las comparaciones.
Una vez que se ha establecido un identificador de orden de columna para un modelo TreeModel que implementa la
interfaz TreeSortable este modelo ya no puede volver al estado original no ordenado. Es posible cambiar su funcin
de clasificacin o utilizar la funcin por defecto, pero ya no es posible establecer que ese modelo TreeModel carezca
de dicha funcin.
195
'gboolean'
str
int
long
float
Inicialmente, tanto los almacenes ListStore como los TreeStore se fijan con un identificador de orden de
columna igual a -2, que indica que no se utiliza una funcin de ordenacin y que el almacn no est ordenado. Una vez
que se fija un identificador de clasificacin de columna en un ListStore o TreeStore ya no es posible volver a
establecerlo al valor -2.
En caso de que se desee mantener los identificadores de orden de columna por defecto se debe establecer su valor fuera
del rango del nmero de columnas, tal como 1000 o ms. entonces es posible cambiar entre las funciones de ordenacin
por defecto y las de la aplicacin segn sea necesario.
14.3. TreeViews (Vistas de rbol)
Un TreeView es fundamentalmente un contenedor de objetos de columna TreeViewColumn e intrpretes de celda
CellRenderer que son los responsables de llevar a cabo en ltimo trmino la visualizacin de los datos del almacn
de datos. Tambin aporta una interfaz para las filas de datos mostradas y para las caractersticas que controlan la
visualizacin de esos datos.
14.3.1. Creacin de un TreeView (vista de rbol)
Un TreeView se crea utilizando su constructor:
treeview = gtk.TreeView(model=None)
donde model es un objeto que implementa la interfaz TreeModel (generalmente un ListStore o TreeStore). Si
model es None o no se especifica, entonces el TreeView no estar asociado a un almacn de datos.
14.3.2. Obtencin y establecimiento del Modelo de un TreeView
El modelo de rbol que proporciona el almacn de datos de un TreeView puede obtenerse utilizando el mtodo
get_model() :
model = treeview.get_model()
Un TreeModel puede asociarse simultneamente con ms de un TreeView, que cambia automticamente su
visualizacin en el memento en el que cambian los datos del TreeModel. Mientras que unTreeView siempre muestra
todas las filas de su modelo de rbol, permite mostrar selectivamente algunas de las columnas. Ello significa que dos
TreeViews asociados al mismo TreeModel pueden realizar visualizaciones completamente distintas de los mismos
datos.
Es tambin importante darse cuenta de que no existe relacin preestablecida entre las columnas de un TreeView y las
columnas de su TreeModel. Por tanto, la quinta columna de los datos de un TreeModel pueden mostrarse en la
primera columna de un TreeView y en la tercera columna de otro.
Un TreeView puede cambiar su modelo de rbol utilizando el mtodo set_model() :
196
treeview.set_model(model=None)
donde model es un objeto que implementa la interfaz TreeModel (p.e. ListStore y TreeStore). Si model es
None, entonces se descarta el modelo actual.
14.3.3. Definicin de las propiedades de un TreeView
TreeView tiene una serie de propiedades que se pueden gestionar utilizando sus mtodos:
"enable-search"
LecturaEscritura
"expandercolumn"
LecturaEscritura
"fixed-heightmode"
LecturaEscritura
Si es TRUE, asume que todas las filas tiene la misma altura, lo que acelera la
visualizacin. Disponible a partir de GTK+ 2.4. Por defecto es FALSE
"hadjustment"
LecturaEscritura
El control Adjustment (ajuste) horizontal del control. Se crea uno nuevo por
defecto.
"headersclickable"
Escritura
"headers-visible"
LecturaEscritura
"model"
LecturaEscritura
"reorderable"
LecturaEscritura
"rules-hint"
LecturaEscritura
Si es TRUE, entonces indicar al motor de temas que dibuje las filas en colores
alternados. Por defecto es FALSE
"search-column"
LecturaEscritura
Indica la columna del modelo en la que buscar cuando se hace a travs de cdigo.
Por defecto es -1.
"vadjustment"
LecturaEscritura
El Adjustment (ajuste) vertical para el control. Se crea uno nuevo por defecto.
197
treeview.set_reorderable(reorderable)
riles_hint = treeview.get_rules_hint()
treeview.set_rules_hint(setting)
column = treeview.get_search_column()
treeview.set_search_column(column)
vadjustment = treeview.get_vadjustment()
treeview.set_vadjustment(adjustment)
La funcin de la mayora de ellos resulta obvia por su descripcin. Sin embargo, la propiedad "enable-search" necesita
que se haya definido correctamente la propiedad "search-column" como un nmero de una columna vlida del modelo
de rbol. Entonces, cuando el usuario o usuaria pulsa Control+f aparece un dilogo de bsqueda en el que se puede
escribir. Se selecciona la primera fila coincidente a medida que se teclea..
Anlogamente, la propiedad "headers-clickable" realmente solamente fija la propiedad "clickable" de las columnas
TreeViewColumns subyacentes. Una TreeViewColumn no ser ordenable salvo que su modelo de rbol
implemente la interfaz TreeSortable y se haya llamado el mtodo TreeViewColumn
set_sort_column_id() con un nmero vlido de columna.
La propiedad "reorderable" permite que usuarios y usuarias reordenen el modelo TreeView arrastrando y soltando las
filas mostradas del TreeView.
La propiedad "rules-hint" no debe establecerse salvo en el caso de tener muchas columnas y cuando resulte til
mostrarlas con colores alternos.
14.4. Visualizadores de Celda (CellRenderer)
14.4.1. Introduccin
Las columnas de vista de rbol (TreeViewColumn) y los Visualizadores o Intrpretes de Celda (CellRenderer)
colaboran en la visualizacin de una columna de datos de una vista de rbol (TreeView). La clase
TreeViewColumn proporciona el ttulo de columna y un espacio vertical para que los CellRenderer muestren una
porcin de los datos de los que contiene el almacn del TreeView. Un CellRenderer maneja la visualizacin de
los datos de cada fila y columna dentro de los confines de una TreeViewColumn. Una TreeViewColumn puede
contener ms de un CellRenderer para proporcionar una visualizacin de fila similar a la de una HBox. Un uso
habitual con mltiples CellRenderer es la combinacin de un Visualizador de Imgenes en Celda
(CellRendererPixbuf) y un Visuallizador de Texto en Celda (CellRendererText) en la misma columna.
El ejemplo Figura 14.2, TreeViewColumns con CellRenderers muestra un ejemplo que ilustra la composicin de dos
TreeViewColumn, una con dos CellRenderer y la otra con uno slo:
Figura 14.2. TreeViewColumns con CellRenderers
La aplicacin de cada CellRenderer se indica mediante un color de fondo diferenciado: amarillo en el caso de un
CellRendererPixbuf, cian para un CellRendererText, y rosa para el otro CellRendererText. Hay que
resaltar que el CellRendererPixbuf y el primer CellRendererText estn en la misma columna, encabezada
198
con el texto "Pixbuf and Text". El color de fondo del CellRendererText que muestra "Print File" es el color
predeterminado que muestra el rea de la aplicacin en una fila.
Figura 14.2, TreeViewColumns con CellRenderers se cre con el programa treeviewcolumn.py.
14.4.2. Tipos de Visualizadores CellRenderer
El tipo del CellRenderer que se necesita para cada caso viene determinado por el tipo de visualizacin requerida por
los datos del modelo de rbol usado. PyGTK posee tres CellRenderer predefinidos:
CellRendererPixbuf
visualiza imgenes de pxeles (pixbuf) que pueden haber sido creadas por el programa, o
tratarse de una imagen de serie predefinida.
CellRendererText
visualiza cadenas de texto, as como nmeros que pueden ser convertidos en cadenas
(enteros, reales y booleanos incluidos).
CellRendererToggle visualiza un valor booleano como un botn biestado o como un botn de exclusin
14.4.3. Propiedade de un CellRenderer
Las propiedades de un CellRenderer determinan la forma en que se visualizarn los datos:
"mode"
LecturaEscritura
"visible"
LecturaEscritura
"xalign"
LecturaEscritura
La fraccin de espacio libre a la izquierda de la celda dentro del intervalo 0.0 a 1.0.
"yalign"
LecturaEscritura
La fraccin de espacio libre sobre la celda dentro del intervalo 0.0 a 1.0.
"xpad"
LecturaEscritura
"ypad"
LecturaEscritura
"width"
LecturaEscritura
"height"
LecturaEscritura
"is-expander"
LecturaEscritura
"is-expanded"
LecturaEscritura
"cellbackground"
Escritura
"cellbackgroundgdk"
LecturaEscritura
199
"cellbackgroundset"
LecturaEscritura
Las propiedades anteriores estn disponibles para todas las subclases de CellRenderer. Pero los distintos tipos de
CellRenderer tambin tienen propiedades exclusivas.
Los CellRendererPixbuf tienen estas propiedades:
"pixbuf"
Lectura-Escritura
"pixbuf-expander-open"
Lectura-Escritura
"pixbuf-expander-closed"
Lectura-Escritura
"stock-id"
Lectura-Escritura
"stock-size"
Read-Write
"stock-detail"
Lectura-Escritura
Los CellRendererText tienen un gran nmero de propiedades que tratan fundamentalmente con la especificacin
de estilos:
"text"
LecturaEscritura
"markup"
LecturaEscritura
"attributes"
LecturaEscritura
"background"
Escritura
"foreground"
Escritura
"backgroundgdk"
LecturaEscritura
"foreground-gdk" LecturaEscritura
"font"
LecturaEscritura
"font-desc"
LecturaEscritura
"family"
LecturaEscritura
"style"
LecturaEscritura
Estilo de fuente.
"variant"
LecturaEscritura
Variante de la fuente.
"weight"
LecturaEscritura
Peso de la fuente.
"stretch"
Lectura-
Estirado de la fuente.
200
Escritura
"size"
LecturaEscritura
Tamao de la fuente.
"size-points"
LecturaEscritura
"scale"
LecturaEscritura
"editable"
LecturaEscritura
"strikethrough"
LecturaEscritura
"underline"
LecturaEscritura
"rise"
LecturaEscritura
Elevacin del texto por encima de la lnea base (o por debajo si el valor es negativo)
"language"
LecturaEscritura
El idioma del texto, como cdigo ISO. Pango puede utilizarlo como pista al representar
el texto. Si no se entiende este parmetro... probablemente es que no se necesita.
Solamente disponible a partir de GTK+ 2.4.
"singleparagraph-mode"
LecturaEscritura
"background-set"
LecturaEscritura
"foreground-set"
LecturaEscritura
"family-set"
LecturaEscritura
"style-set"
LecturaEscritura
"variant-set"
LecturaEscritura
"weight-set"
LecturaEscritura
"stretch-set"
LecturaEscritura
"size-set"
LecturaEscritura
"scale-set"
LecturaEscritura
"editable-set"
LecturaEscritura
"strikethroughset"
LecturaEscritura
"underline-set"
LecturaEscritura
"rise-set"
LecturaEscritura
201
"language-set"
LecturaEscritura
Casi cada una de las propiedades de CellRendererText posee una propiedad booleana asociada (con el suffijo "set") que indica si se aplica dicha propiedad. Esto permite fijar globalmente una propiedad y activar o desactivar su
aplicacin selectivamente.
Los CellRendererToggle poseen las siguientes propiedades:
"activatable"
Lectura-Escritura
"active"
Lectura-Escritura
"radio"
Lectura-Escritura
"inconsistent"
Lectura-Escritura
Las propiedades se pueden fijar para todas las filas utilizando el mtodo gobject.set_property(). Vase el
programa treeviewcolumn.py como ejemplo del uso de este mtodo.
14.4.4. Atributos de un CellRenderer
Un atributo asocia una columna de un modelo de rbol a una propiedad de un CellRenderer. El CellRenderer
fija la propiedad en funcin del valor de una columna de la fila antes de representar la celda. Esto permite personalizar la
visualizacin de la celda utilizando los datos del modelo de rbol. Se puede aadir un atributo al conjunto actual con:
treeviewcolumn.add_attribute(cell_renderer, attribute, column)
donde la propiedad especificada por attribute se fija para el cell_renderer en la columna column. Por
ejemplo:
treeviewcolumn.add_attribute(cell, "cell-background", 1)
establece el fondo del CellRenderer al color indicado por la cadena de la segunda columna del almacn de datos.
Para eliminar todos los atributos y establecer varios atributos nuevos de una vez se usa:
treeviewcolumn.set_attributes(cell_renderer, ...)
donde los atributos de cell_renderer se determinan mediante pares clave-valor: propiedad=columna. Por ejemplo,
en el caso de un CellRendererText:
treeviewcolumn.set_attributes(cell, text=0, cell_background=1, xpad=3)
indica, para cada fila, el texto en la primera columna, el color de fondo en la segunda y el margen horizontal desde la
cuarta columna. Vase el programa treeviewcolumn.py para ver ejemplos del uso de estos mtodos.
Los atributos de un CellRenderer se pueden limpiar utilizando:
treeviewcolumn.clear_attributes(cell_renderer)
14.4.5. Funcin de Datos de Celda
Si no es suficiente el uso de atributos para cubrir nuestras necesidades, tambin es posible indicar una funcin que ser
llamada en cada fila y que determine las propiedades del CellRenderer utilizando:
202
Otro posible uso de la funcin de datos de celda es el control del formato de visualizacin de un texto numrico, p.e. un
valor real. Un CellRendererText har una conversin de forma automtica del valor real a una cadena, pero con el
formato predeterminado "%f".
Con funciones de datos de celda se pueden generar incluso los datos de las celdas a partir de datos externos. Por
ejemplo, el programa filelisting.py usa un almacn ListStore con una nica columna que contiene una lista de
nombres de archivos. La TreeView muestra columnas que incluyen una imagen pixbuf, el nombre de archivo y su
tamao, modo y fecha del ltimo cambio. Los datos son generados por las siguientes funciones de datos de celda:
203
204
205
Si se crean etiquetas de marcado sobre la marcha es preciso tener cuidado y sustituir los caracteres con especial
significado en el lenguaje de marcas: "<", ">", "&". La funcin de la biblioteca de Python cgi.escape() permite
hacer estas conversiones bsicas.
14.4.7. Celdas de Texto Editables
Las celdas CellRendererText pueden hacerse editalbes de forma que una usuaria pueda editar los contenidos de la
celda que seleccione haciendo clic en ella o pulsando las teclas Return, Enter, Space o Shift+Space. Se hace editable
un CellRendererText en todas sus filas estableciendo su propiedad "editable" a TRUE de la siguiente manera:
cellrenderertext.set_property('editable', True)
Se pueden establecer individualmente celdas editables aadiendo un atributo a la TreeViewColumn utilizando un
CellRendererText parecido a:
treeviewcolumn.add_attribute(cellrenderertext, "editable", 2)
que establece que el valor de la propiedad "editable" se indica en la tercera columna del almacn de datos.
Una vez que la edicin de la celda termina, la aplicacin debe gestionar la seal "edited" para obtener el nuevo texto y
establecer los datos asociados del almacn de datos. De otro modo, el valor de la celda recuperar su valor inicial. La
signatura del manejador de llamada de la seal "edited" es:
def edited_cb(cell, path, new_text, user_data)
donde cell es el CellRendererText, path es el camino de rbol (como cadena) a la fila que contiene la celda
editada, new_text es el texto editado y user_data son datos de contexto. Puesto que se necesita el TreeModel
para usar el camino path y establecer new_text en el almacn de datos, probablemente se quiera pasar el
TreeModel como user_data en el mtodo connect():
cellrenderertext.connect('edited', edited_cb, model)
Si se tienen dos o ms celdas editables en una fila, se podra pasar el nmero de columna del TreeModel como parte
de los datos adicionales user_data as como el modelo TreeModel:
cellrenderertext.connect('edited', edited_cb, (model, col_num))
As, se puede establecer el nuevo texto en el manejador de la seal "edited" de una forma parecida al siguiente ejemplo
que usa un almacn de lista ListStore:
def edited_cb(cell, path, new_text, user_data):
liststore, column = user_data
liststore[path][column] = new_text
return
14.4.8. Celdas Biestado Activables
Los botones de CellRendererToggle se pueden hacer activables estableciendo la propiedad "activatable" como
TRUE. De forma parecida a la celdas editables de CellRendererText la propiedad "activatable" se puede fijar para
un conjunto completo de celdas con CellRendererToggle utilizando el mtodo set_property() o
individualmente en algunas celdas aadiendo un atributo a la columna TreeViewColumn que contiene el
CellRendererToggle.
cellrenderertoggle.set_property('activatable', True)
treeviewcolumn.add_attribute(cellrenderertoggle, "activatable", 1)
206
La creacin de botones individuales biestado se puede deducir de los valores de una columna de un TreeModel
aadiendo un atributo de manera similar a este ejemplo:
treeviewcolumn.add_attribute(cellrenderertoggle, "active", 2)
Se debe conectar a la seal "toggled" para disponer de notificacin de las pulsaciones del usuario en los botones
biestado, de manera que la aplicacin pueda modificar los valores del almacn de datos. Por ejemplo:
cellrenderertoggle.connect("toggled", toggled_cb, (model, column))
La retrollamada tiene la signatura:
def toggled_cb(cellrenderertoggle, path, user_data)
donde path es el camino de rbol, como cadena, que apunta a la fila que contiene el botn biestado que ha sido
pulsado. Es recomendable pasar el TreeModel y, tal vez, el ndice de la columna como parte de los datos de usuario
user_data para proporcionar el contexto necesario para establecer los valores del almacn de datos. Por ejemplo, la
aplicacin puede conmutar los valores del almacn de datos, as:
def toggled_cb(cell, path, user_data):
model, column = user_data
model[path][column] = not model[path][column]
return
Si la aplicacin desea mostrar los botones biestado como botones de exclusin y que nicamente uno de ellos est
activo, tendr que recorrer los datos del almacn para desactivar el botn de exclusin activo y posteriormente activar el
botn biestado. Por ejemplo:
def toggled_cb(cell, path, user_data):
model, column = user_data
for row in model:
row[column] = False
model[path][column] = True
return
usa la estrategia "vaga" de poner todos los valores a FALSE antes de fijar el valor TRUE en la fila especificada en path.
14.4.9. Programa de Ejemplo de Celda Editable and Activable
El programa cellrenderer.py ilustra la utilizacin de celdas CellRendererText editables y de celdas
CellRendererToggle activables en un almacn TreeStore.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/usr/bin/env python
# vim: ts=4:sw=4:tw=78:nowrap
""" Demonstration using editable and activatable CellRenderers """
import pygtk
pygtk.require("2.0")
import gtk, gobject
tasks = {
"Buy groceries": "Go to Asda after work",
"Do some programming": "Remember to update your software",
"Power up systems": "Turn on the client but leave the server",
"Watch some tv": "Remember to catch ER"
}
class GUI_Controller:
""" The GUI class is the controller for our application """
207
17
def __init__(self):
18
# establecer la ventana principal
19
self.root = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
20
self.root.set_title("CellRenderer Example")
21
self.root.connect("destroy", self.destroy_cb)
22
# Obtener el modelo y vincularlo a la vista
23
self.mdl = Store.get_model()
24
self.view = Display.make_view( self.mdl )
25
# Aadir la vista a la ventana principal
26
self.root.add(self.view)
27
self.root.show_all()
28
return
29
def destroy_cb(self, *kw):
30
""" Destroy callback to shutdown the app """
31
gtk.main_quit()
32
return
33
def run(self):
34
""" run is called to set off the GTK mainloop """
35
gtk.main()
36
return
37
38
class InfoModel:
39
""" The model class holds the information we want to display """
40
def __init__(self):
41
""" Sets up and populates our gtk.TreeStore """
42
self.tree_store = gtk.TreeStore( gobject.TYPE_STRING,
43
gobject.TYPE_BOOLEAN )
44
# colocar los datos globales de la gente en la lista
45
# formamos un rbol simple.
46
for item in tasks.keys():
47
parent = self.tree_store.append( None, (item, None) )
48
self.tree_store.append( parent, (tasks[item],None) )
49
return
50
def get_model(self):
51
""" Returns the model """
52
if self.tree_store:
53
return self.tree_store
54
else:
55
return None
56
57
class DisplayModel:
58
""" Displays the Info_Model model in a view """
59
def make_view( self, model ):
60
""" Form a view for the Tree Model """
61
self.view = gtk.TreeView( model )
62
# configuramos el visualizador de celda de texto y permitimos
63
# la edicin de las celdas.
64
self.renderer = gtk.CellRendererText()
65
self.renderer.set_property( 'editable', True )
66
self.renderer.connect( 'edited', self.col0_edited_cb, model )
67
68
# Se configura el visualizador de botones biestado y permitimos
que se
69
# pueda cambiar por el usuario.
70
self.renderer1 = gtk.CellRendererToggle()
71
self.renderer1.set_property('activatable', True)
72
self.renderer1.connect( 'toggled', self.col1_toggled_cb, model )
73
74
# Conectamos la columna 0 de la visualizacin con la columna o
de nuestro modelo
208
75
76
77
78
79
# El estado de activacin del modelo se vincula a la segunda
columna
80
# del modelo. As, cuando el modelo dice True entonces el botn
81
# se mostrar activo, es decir, encendido.
82
self.column1 = gtk.TreeViewColumn("Complete", self.renderer1 )
83
self.column1.add_attribute( self.renderer1, "active", 1)
84
self.view.append_column( self.column0 )
85
self.view.append_column( self.column1 )
86
return self.view
87
def col0_edited_cb( self, cell, path, new_text, model ):
88
"""
89
Called when a text cell is edited. It puts the new text
90
in the model so that it is displayed properly.
91
"""
92
print "Change '%s' to '%s'" % (model[path][0], new_text)
93
model[path][0] = new_text
94
return
95
def col1_toggled_cb( self, cell, path, model ):
96
"""
97
Sets the toggled state on the toggle button to true or false.
98
"""
99
model[path][1] = not model[path][1]
100
print "Toggle '%s' to: %s" % (model[path][0], model[path][1],)
101
return
102
103
if __name__ == '__main__':
104
Store = InfoModel()
105
Display = DisplayModel()
106
myGUI = GUI_Controller()
107
myGUI.run()
El programa proporcional celdas editables en la primera columna y celdas activables en la segunda columna. Las lneas
64-66 crean un CellRendererText editable y conectan la seal "edited" a la retrollamada col0_edited_cb()
(lneas 87-94), que cambia el valor en la columna correspondiente de la fila en el almacn de rbol TreeStore. De la
misma manera, las lneas 70-72 crean un CellRendererToggle activable y conectan la seal "toggled" a la
retrollamada col1_toggled_cb() (lneas 95-101) para cambiar el valor de la fila correspondiente. Cuando se
modifica una celada editable o activable se muestra un mensaje para indicar cul ha sido el cambio.
Figura 14.6, Celdas Editables y Activables ilustra el programa cellrenderer.py en ejecucin.
Figura 14.6. Celdas Editables y Activables
209
210
Si se usan los encabezados de las TreeViewColumns para hacer la ordenacin, entonces si se utiliza el mtodo
set_sort_column_id() no es necesario utilizar el mtodo TreeSortableset_sort_column_id() .
Se pueden rastrear las operaciones de ordenacin o utilizar el click sobre los encabezados para propsitos especficos
conectndose a la seal "clicked" de la columna de un TreeView. La funcin de retrollamada debera definirse as:
def callback(treeviewcolumn, user_data, ...)
14.6. Manipulacin de TreeViews
14.6.1. Gestin de las Columnas
Las TreeViewColumns de un TreeView pueden obtenerse individualmente o como una lista utilizando los mtodos:
treeviewcolumn = treeview.get_column(n)
columnlist = treeview.get_columns()
donde n es el ndice (empezando desde 0) de la columna que se quiere obtener. Se puede eliminar una columna con el
mtodo:
treeview.remove_column(column)
donde column es una TreeViewColumn en treeview.
Las filas que tienen filas hijas se muestran en el TreeView con una flecha de expansin Figura 14.3, Funcin de
Datos de Celda) que se puede pulsar para ocultar o mostrar las filas hijas. La columna en la que se muestra la flecha de
expansin puede cambiarse utilizando el mtodo:
treeview.set_expander_column(column)
donde column es una TreeViewColumn en treeview. Este mtodo es til cuando no se desea que se indente la
primera columna. Por ejemplo, Figura 14.7, Flecha de Expansin en la segunda Columna ilustra la flecha de
expansin en la segunda columna:
Figura 14.7. Flecha de Expansin en la segunda Columna
211
treeview.expand_all()
treeview.collapse_all()
Estos mtodos son tiles si se quiere inicializar la visualizacin del TreeView en un estado determinado. Las filas
individuales pueden ser expandidas o contradas utilizando:
treeview.expand_row(path, open_all)
treeview.collapse_row(path)
donde path es el camino de rbol a una fila en treeview, y si open_all es TRUE, entonces todas las filas
descendientes de path son expandidas; en otro caso, nicamente son expandidas las descendientes inmediatas.
Se puede determinar si una fila se expande utilizando el mtodo:
is_expanded = treeview.row_expanded(path)
14.7. Seales de TreeView
Los controles TreeView emiten un gran nmero de seales que se pueden usar para seguir los cambios en la
visualizacin del modelo. las seales caen generalmente en una de las siguientes categoras:
Las seales "test-collapse-row" y "test-expand-row" son emitidas antes de que se contraiga o expanda una fila. El valor
devuelto por la retrollamada puede permitir o cancelar la operacin (TRUE para permitirla y FALSE para cancelarla.
def callback(treeview, iter, path, user_data)
donde iter es un TreeIter y path es un camino de rbol que apunta a la fila y user_data son los datos
especificados en el mtodo connect() .
La seal "row-activated" se emite cuando se produce un doble click en una fila o cuando se selecciona una fila no
editable y se pulsa una de las siguientes teclas: Espacio, Shift+Espacio, Return o Enter.
El resto de las seales se emiten tras haber cambiado el TreeView. El cursor es la fila marcada por una caja. En la
mayora de los casos la seleccin se mueve cuando se mueve el cursor. El cursor se puede mover de forma
independiente mediante Control+Abajo o Control+Arriba y otras combinaciones de teclas.
Vase el Manual de Referencia de PyGTK para obtener ms informacin sobre las seales de TreeView.
14.8. Selecciones TreeSelections
14.8.1. Obtencin de TreeSelection
Las TreeSelections son objetos que gestionan las selecciones en un TreeView. Cuando se crea un TreeView
tambin se crea automticamente una TreeSelection. Puede obtenerse la TreeSelection de una TreeView
utilizando el mtodo:
treeselection = treeview.get_selection()
Se puede obtener el TreeView asociado a una TreeSelection con el mtodo:
212
treeview = treeselection.get_treeview()
14.8.2. Modos de una seleccin TreeSelection
Una seleccin TreeSelection soporta los siguientes modos de seleccin:
gtk.SELECTION_NONE
gtk.SELECTION_SINGLE
gtk.SELECTION_BROWSE
gtk.SELECTION_MULTIPLE
213
list.append(path)
...
def my_get_selected_rows(treeselection):
pathlist = []
treeselection.selected_foreach(foreach_cb, pathlist)
model = sel.get_treeview().get_model()
return (model, pathlist)
...
El mtodo selected_foreach() no puede usarse para modificar el modelo de rbol o la seleccin, aunque s permite
cambiar los datos de las filas.
14.8.4. Uso de una Funcin de TreeSelection
Si se desea un control definitivo sobre la seleccin de filas se puede establecer una funcin que ser llamada antes de
que se seleccione o deselecciones una fila mediante el mtodo:
treeselection.set_select_function(func, data)
donde func es una funcin de retrollamada y data son los datos de usuario que se pasarn a func cuando es llamada.
func tiene la signatura:
def func(selection, model, path, is_selected, user_data)
donde selection es la seleccin TreeSelection, model es el TreeModel usado con el TreeView asociado
con selection, path es el camino de rbol de la fila seleccionada, is_selected es TRUE si la fila est
actualmente seleccionada y user_data es data. func debera devolver TRUE si el estado de seleccin de la fila
debera ser conmutado.
Establecer una funcin de seleccin es til en estos casos:
se quiere controlar la seleccin o deseleccin de una fila en funcin de alguna informacin adicional de
contexto. Se necesitar indicar de alguna manera que el cambio de seleccin no se puede llevar a cabo y, tal vez,
el porqu. Por ejemplo, se puede diferenciar visualmente la fila o mostar un dilogo emergente del tipo
MessageDialog.
se necesita mantener una lista propia de filas seleccionadas o deseleccionadas, aunque esto mismo se puede
hacer, con algo ms de esfuerzo, conectndose a la seal "changed".
se quiere hacer algn procesado adicional antes de que una fila sea seleccionada o deseleccionada. Por ejemplo,
cambiar el aspecto de la fila o modificar los datos de la misma.
214
treeselection.unselect_range(start_path, end_path)
El mtodo select_all() precisa que el modo de seleccin sea gtk.SELECTION_MULTIPLE al igual que el
mtodo select_range(). Los mtodos unselect_all() y unselect_range() funcionarn con cualquier modo
de seleccin. Ntese que el mtodo unselect_all() no est disponible en PyGTK 2.0
Se puede comprobar si una fila est seleccionada utilizando uno de los siguientes mtodos:
result = treeselection.path_is_selected(path)
result = treeselection.iter_is_selected(iter)
que devuelven TRUE si la fila especificada por path o iter est actualmente seleccionada. Se puede obtener el
nmero de filas seleccionadas con el mtodo:
count = treeselection.count_selected_rows()
Este mtodo no est disponible en PyGTK 2.0 por lo que es preciso simularlo utilizando el mtodo
selected_foreach() de forma parecida a la simulacin del mtodo get_selected_rows() de la seccin
Obtencin de la Seleccin. Por ejemplo:
...
def foreach_cb(model, path, iter, counter):
counter[0] += 1
...
def my_count_selected_rows(treeselection):
counter = [0]
treeselection.selected_foreach(foreach_cb, counter)
return counter[0]
...
14.9. Arrastrar y Soltar en TreeView
14.9.1. Reordenacin mediante Arrastrar y Soltar
La reordenacin de las filas de un TreeView (y de las filas del modelo de rbol subyacente) se activa usando el mtodo
set_reorderable() que se mencion previamente. El mtodo set_reorderable() fija la propiedad
"reorderable" al valor especificado y permite o impide arrastrar y soltar en las filas del TreeView. Cuando la propiedad
"reorderable" es TRUE es posible arrastar internamente filas del TreeView y soltarlas en una nueva posicin. Esta
accin provoca que las filas del TreeModel subyacente se reorganicen para coincidir con la nueva situacin. La
reordenacin mediante arrastrar y soltar de filas funciona nicamente con almacenes no ordenados.
14.9.2. Arrastar y Soltar Externo
Si se quiere controlar el arrastar y soltar o tratar con el arrastrar y soltar desde fuentes externas de datos es necesario
habilitar y controlar el arrastar y soltar con los siguientes mtodos:
treeview.enable_model_drag_source(start_button_mask, targets, actions)
treeview.enable_model_drag_dest(targets, actions)
Estos mtodos permiten utilizar filas como fuente de arrastre y como lugar para soltar respectivamente.
start_button_mask es una mscara de modificacin (vase referencia de constantes gtk.gtk Constants en el
Manual de Referencia de PyGTK ) que especifica los botones o teclas que deben ser pulsadas para iniciar la operacin
de arrastre. targets es una lista de tuplas de 3 elementos que describen la informacin del objetivo que puede ser
recibido o dado. Para que tenga xito el arrastar o soltar, por lo menos uno de los objetivos debe coincidir en la fuente o
destino del arrastre (p.e. el objetivo "STRING"). Cada tupla de 3 elementos del objetivo contiene el nombre del objetivo,
banderas (una combinacin de gtk.TARGET_SAME_APP y gtk.TARGET_SAME_WIDGET o ninguno) y un
identificador entero nico. actions describe cul debera ser el resultado de la operacin:
215
gtk.gdk.ACTION_DEFAULT,
gtk.gdk.ACTION_COPY
gtk.gdk.ACTION_MOVE
gtk.gdk.ACTION_LINK
gtk.gdk.ACTION_PRIVATE
gtk.gdk.ACTION_ASK
216
Los parmetros de callback son similares a los de la funcin de retrollamada de "drag-data-received". Puesto que a la
retrollamada no se le pasa un camino de rbol o una forma sencilla de obtener informacin sobre la fila que est siendo
arrastrada, asumiremos que la fila que est siendo arrastrada est seleccionada y que el modo de seleccin es
gtk.SELECTION_SINGLE o gtk.SELECTION_BROWSE de modo que podemos tener la fila obteniendo la
TreeSelection, el modelo y el iterador TreeIter que apunta a la fila. Por ejemplo, se podra pasar texto as::
...
treestore = gtk.TreeStore(str, str)
...
treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,
[('text/plain', 0, 0)],
gtk.gdk.ACTION_DEFAULT | gtk.gdk.ACTION_MOVE)
treeview.connect("drag-data-get", drag_data_get_cb)
...
...
def drag_data_get_cb(treeview, context, selection, info, timestamp):
treeselection = treeview.get_selection()
model, iter = treeselection.get_selected()
text = model.get_value(iter, 1)
selection.set('text/plain', 8, text)
return
...
Un TreeView puede ser desactivado como fuente o destino para arrastrar y soltar utilizando los mtodos:
treeview.unset_rows_drag_source()
treeview.unset_rows_drag_dest()
14.9.3. Ejemplo de Arrastrar y Soltar en TreeView
Es necesario un ejemplo para unir las piezas de cdigo descritas ms arriba. Este ejemplo (treeviewdnd.py) es una lista
en la que se pueden arrastrar y soltar URLs. Tambin se pueden reordenar las URLs de la lista arrastrando y soltando en
el interior del TreeView. Un par de botones permiten limpiar la listar y eliminar un elemento seleccionado.
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
#!/usr/bin/env python
# example treeviewdnd.py
import pygtk
pygtk.require('2.0')
import gtk
class TreeViewDnDExample:
TARGETS = [
('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),
('text/plain', 0, 1),
('TEXT', 0, 2),
('STRING', 0, 3),
]
# close the window and quit
def delete_event(self, widget, event, data=None):
gtk.main_quit()
return gtk.FALSE
def clear_selected(self, button):
selection = self.treeview.get_selection()
model, iter = selection.get_selected()
if iter:
217
26
model.remove(iter)
27
return
28
29
def __init__(self):
30
# Create a new window
31
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
32
33
self.window.set_title("URL Cache")
34
35
self.window.set_size_request(200, 200)
36
37
self.window.connect("delete_event", self.delete_event)
38
39
self.scrolledwindow = gtk.ScrolledWindow()
40
self.vbox = gtk.VBox()
41
self.hbox = gtk.HButtonBox()
42
self.vbox.pack_start(self.scrolledwindow, True)
43
self.vbox.pack_start(self.hbox, False)
44
self.b0 = gtk.Button('Clear All')
45
self.b1 = gtk.Button('Clear Selected')
46
self.hbox.pack_start(self.b0)
47
self.hbox.pack_start(self.b1)
48
49
# create a liststore with one string column to use as the model
50
self.liststore = gtk.ListStore(str)
51
52
# create the TreeView using liststore
53
self.treeview = gtk.TreeView(self.liststore)
54
55
# create a CellRenderer to render the data
56
self.cell = gtk.CellRendererText()
57
58
# create the TreeViewColumns to display the data
59
self.tvcolumn = gtk.TreeViewColumn('URL', self.cell, text=0)
60
61
# add columns to treeview
62
self.treeview.append_column(self.tvcolumn)
63
self.b0.connect_object('clicked', gtk.ListStore.clear,
self.liststore)
64
self.b1.connect('clicked', self.clear_selected)
65
# make treeview searchable
66
self.treeview.set_search_column(0)
67
68
# Allow sorting on the column
69
self.tvcolumn.set_sort_column_id(0)
70
71
# Allow enable drag and drop of rows including row move
72
self.treeview.enable_model_drag_source( gtk.gdk.BUTTON1_MASK,
73
self.TARGETS,
74
gtk.gdk.ACTION_DEFAULT|
75
gtk.gdk.ACTION_MOVE)
76
self.treeview.enable_model_drag_dest(self.TARGETS,
77
gtk.gdk.ACTION_DEFAULT)
78
79
self.treeview.connect("drag_data_get", self.drag_data_get_data)
80
self.treeview.connect("drag_data_received",
81
self.drag_data_received_data)
82
83
self.scrolledwindow.add(self.treeview)
84
self.window.add(self.vbox)
218
85
self.window.show_all()
86
87
def drag_data_get_data(self, treeview, context, selection,
target_id,
88
etime):
89
treeselection = treeview.get_selection()
90
model, iter = treeselection.get_selected()
91
data = model.get_value(iter, 0)
92
selection.set(selection.target, 8, data)
93
94
def drag_data_received_data(self, treeview, context, x, y,
selection,
95
info, etime):
96
model = treeview.get_model()
97
data = selection.data
98
drop_info = treeview.get_dest_row_at_pos(x, y)
99
if drop_info:
100
path, position = drop_info
101
iter = model.get_iter(path)
102
if (position == gtk.TREE_VIEW_DROP_BEFORE
103
or position == gtk.TREE_VIEW_DROP_INTO_OR_BEFORE):
104
model.insert_before(iter, [data])
105
else:
106
model.insert_after(iter, [data])
107
else:
108
model.append([data])
109
if context.action == gtk.gdk.ACTION_MOVE:
110
context.finish(True, True, etime)
111
return
112
113
def main():
114
gtk.main()
115
116
if __name__ == "__main__":
117
treeviewdndex = TreeViewDnDExample()
118
main()
El resultado de la ejecucin del programa de ejemplo treeviewdnd.py se ilustra en Figura 14.8, Ejemplo de Arrastrar y
Soltar en TreeView:
Figura 14.8. Ejemplo de Arrastrar y Soltar en TreeView
La clave para permitir tanto arrastrar y soltar externo como la reorganizacin interna de filas es la organizacin de los
objetivos (el atributo TARGETS de la lnea 11). Se crea y usa un objetivo especfico de la aplicacin
219
(MY_TREE_MODEL_ROW) para indicar un arrastar y soltar dentro del TreeView estableciendo la bandera
gtk.TARGET_SAME_WIDGET. Estableciendo ste como el primer objetivo para el destino del arrastre har que se
intente hacerlo coincidir primero con los objetivos del origen de arrastre. Despus, las acciones de fuente de arrastre
deben incluir gtk.gdk.ACTION_MOVE y gtk.gdk.ACTION_DEFAULT (vanse las lneas 72-75). Cuando el
destino recibe los datos de la fuente, si la accin DragContext es gtk.gdk.ACTION_MOVE, entonces se indica a la
fuente que borre los datos (en este caso la fila) llamando al mtodo del DragContext finish() (vanse las lneas
109-110). Un TreeView proporciona un conjunto de funciones internas que estamos aprovechando para arrastrar,
soltar y eliminar los datos.
14.10. TreeModelSort y TreeModelFilter
Los objetos TreeModelSort y TreeModelFilter son modelos de rbol que se interponen entre el TreeModel
de base (bien un TreeStore o un ListStore) y el TreeView que proporciona un modelo modificado mientras
retiene la estructura original del modelo base. Estos modelos interpuestos implementan las interfaces TreeModel y
TreeSortable pero no proporcionan ningn mtodo para insertar o eliminar filas del modelo. Estas se deben insertar
o eliminar del almacn subyacente. TreeModelSort proporciona un modelo cuyas filas estn siempre ordenadas,
mientras que TreeModelFilter proporciona un modelo que contiene un subconjunto de las filas del modelo base.
Si se desea, estos modelos pueden encadenarse en una sucesin arbitraria; es decir, un TreeModelFilter podra
tener un TreeModelSort hijo, que podra tener otro TreeModelFilter hijo, y as sucesivamente. Mientras haya
un almacn TreeStore o ListStore en el extrremo final de la cadena, todo debera funcionar. En PyGTK 2.0 y 2.2
los objetos TreeModelSort y TreeModelFilter no soportan el protocolo de mapeado de Python para
TreeModel.
14.10.1. TreeModelSort (Modelo de rbol Ordenado)
TreeModelSort mantiene un modelo ordenado del modelo hijo especificado en su constructor. El uso principal de un
TreeModelSort el el de proporcionar mltiples vistas de un modelo que puedan ser ordenadas de forma distinta. Si
se tienen mltiples vistas del mismo modelo entonces cualquier actividad de ordenacin se ve reflejada en todas las
vistas. Al usar un TreeModelSort el almacn base permanecen en su estado original, mientras que los modelos
ordenados absorben toda la actividad de ordenacin. Para crear un TreeModelSort se ha de usar el constructor:
treemodelsort = gtk.TreeModelSort(child_model)
donde child_model es un TreeModel. La mayora de los mtodos de un TreeModelSort tienen que ver con la
conversin de los caminos de rbol e iteradores TreeIter desde el modelo hijo al modelo ordenado y viceversa:
sorted_path = treemodelsort.convert_child_path_to_path(child_path)
child_path = treemodelsort.convert_path_to_child_path(sorted_path)
Estos mtodos de conversin de caminos devuelven None si el camino dado no puede ser convertido en un camino del
modelo ordenado o el modelo hijo respectivamente. Los mtodos de conversin de los iteradores TreeIter son:
sorted_iter = treemodelsort.convert_child_iter_to_iter(sorted_iter,
child_iter)
child_iter = treemodelsort.convert_iter_to_child_iter(child_iter, sorted_iter)
Los mtodos de conversin de iteradores TreeIter duplican el argumento convertido (es tanto el valor de retorno
como el primer argumento) debido a cuestiones de compatiblidad con versiones anteriores; se deben fijar el primer
argumento como None y simplemente utilizar el valor devuelto. Por ejemplo:
sorted_iter = treemodelsort.convert_child_iter_to_iter(None, child_iter)
child_iter = treemodelsort.convert_iter_to_child_iter(None, sorted_iter)
Al igual que los mtodos de conversin de caminos, estos mtodos devuelven None si el TreeIter dado no puede ser
convertido.
220
En cada una de las columnas de las ventanas se puede hacer click para cambiar el orden de los elementos de forma
independiente al de otras ventanas. Cuando se pulsa el botn "Add a Row" se aade una nueva fila al ListStore base
y la nueva fila se muestra en cada TreeView como la fila seleccionada.
14.10.2. TreeModelFilter (Modelo de rbol filtrado)
Nota
El objeto TreeModelFilter est disponible a partir de la versin 2.4 de PyGTK y posteriores.
Un objeto TreeModelFilter proporciona varias formas de modificar la visualizacin del TreeModel de base, y
permiten:
mostrar un subconjunto de las filas del modelo hijo en base a datos booleanos en una "columna visible", o en
funcin del valor de retorno de una "funcin visible", que toma el modelo hijo, un iterador TreeIter que
apunta a la fila del modelo hijo, y datos de usuario. En ambos casos si el valor booleano es TRUE la fila se
mostrar; de otro modo, la fila quedar oculta.
usar un nodo raiz virtual para proporcionar una vista de un subrbol de los desdendientes de una fila en el
modelo hijo. Esto nicamente tiene sentido si el almacn subyacente es del tipo TreeStore.
sintetizar las columnas y datos de un modelo en base a los datos del modelo hijo. Por ejemplo, se puede
proporcionar una columna cuyos datos son calculados a partir de los datos de varias columnas del modelo hijo.
221
222
Conmutando los botones de la parte inferior se cambian los contenidos del TreeView para mostrar nicamente las filas
que coinciden con uno de los botones activos.
Una funcin de modificacin proporciona otro nivel de control sobre la visualizacin en el TreeView hasta el punto en
el que es posible sintetizar una o ms columnas (incluso todas), que son representadas por el TreeModelFilter.
Todava es preciso usar un modelo hijo que sea un almacn TreeStore o ListStore para determinar el nmero de
filas y la jerarqua, pero las columnas pueden ser las que se especifiquen en el mtodo:
treemodelfilter.set_modify_func(types, func, data=None)
donde types es una secuencia (lista or tupla) que especifica los tipos de las columnas que se representan, func es una
funcin llamada para devolver el valor para una fila y columna y data es un argumento que pasar a func. La signatura
de func es:
def func(model, iter, column, user_data)
223
donde model es el TreeModelFilter, iter es un TreeIter que apunta a una fila del modelo, column es el
nmero de la columna para el que se precisa un valor y user_data es el parmetro data. func debe devolver un
valor que coincida con el tipo de column.
Una funcin de modificacin es til cuando se quiere proporcionar una columna de datos que necesita ser generada
utilizando los datos de las columnas del modelo hijo. Por ejemplo, si se tuviese una columna que contiene fechas de
nacimiento y se quisiese mostrar una columna de edades, una funcin de modificacin podra generar la informacin de
edad usando la fecha de nacimiento y la fecha actual. Otro ejemplo sera el decidir qu imagen mostrar en base al
anlisis de los datos (por ejemplo, el nombre de un archivo) de una columna. Este efecto tambin se puede conseguir
utilizando el mtodo TreeViewColumn set_cell_data_func().
Generalmente, dentro de la funcin de modificacin, se tendr que convertir el TreeModelFilter TreeIter a un
iterador TreeIter del modelo hijo haciendo uso de:
child_iter = treemodelfilter.convert_iter_to_child_iter(filter_iter)
Naturalmente, tambin es necesario obtener el modelo hijo usando:
child_model = treemodelfilter.get_model()
Estos mtodos dan acceso a la fila del modelo hijo y a sus valores para generar el valor de la fila y columna
especificadas del TreeModelFilter. Tambin hay un mtodo para convertir un TreeIter hijo a un TreeIter de
un modelo filtrado y mtodos para convertir caminos del modelo filtrado a y desde caminos de los rboles hijo:
filter_iter = treemodelfilter.convert_child_iter_to_iter(child_iter)
child_path = treemodelfilter.convert_path_to_child_path(filter_path)
filter_path = treemodelfilter.convert_child_path_to_path(child_path)
Naturalmente, es posible combinar los modos de visibilidad y la funcin de modificacin para filtrar y sintetizar
columnas. Para obtener incluso un mayor control sobre la vista sera necesario utilizar un TreeModel personalizado.
14.11. El Modelo de rbol Genrico (GenericTreeModel)
En el momento en el que se encuentra que los modelo de rbol estndar TreeModels no son suficientemente potentes
para cubrir las necesidades de una aplicacin se puede usar la clase GenericTreeModel para construir modelos
TreeModel personalizados en Python. La creacin de un GenericTreeModel puede ser de utilidad cuando existen
problemas de rendimiento con los almacenes estndar TreeStore y ListStore o cuando se desea tener una interfaz
directa con una fuente externa de datos (por ejemplo, una base de datos o el sistema de archivos) para evitar la copia de
datos dentro y fuera de los almacenes TreeStore o ListStore.
14.11.1. Visin general de GenericTreeMode
Con GenericTreeModel se construye y gestiona un modelo propio de datos y se proporciona acceso externo a travs
de la interfaz estndar TreeModel al definir un conjunto de mtodos de clase. PyGTK implementa la interfaz
TreeModel y hace que los nuevos mtodos TreeModel sean llamados para proporcionar los datos del modelo
existente.
Los detalles de implementacin del modelo personalizado deben mantenerse ocultos a la aplicacin externa. Ello
significa que la forma en la que se identifica el modelo, guarda y obtiene sus datos es desconocido a la aplicacin. En
general, la nica informacin que se guarda fuera del GenericTreeModel son las referencia de fila que se
encapsulan por los iteradores TreeIter externos. Y estas referencias no son visibles a la aplicacin.
Examinemos en detalle la interfaz GenericTreeModel que es necesario proporcionar.
14.11.2. La Interfaz GenericTreeModel
224
La interfaz GenericTreeModel consiste en los siguientes mtodos que deben implementarse en el modelo de rbol
personalizado:
def
def
def
def
def
def
def
def
def
def
def
def
on_get_flags(self)
on_get_n_columns(self)
on_get_column_type(self, index)
on_get_iter(self, path)
on_get_path(self, rowref)
on_get_value(self, rowref, column)
on_iter_next(self, rowref)
on_iter_children(self, parent)
on_iter_has_child(self, rowref)
on_iter_n_children(self, rowref)
on_iter_nth_child(self, parent, n)
on_iter_parent(self, child)
Debe advertirse que estos mtodos dan soporte a toda la interfaz de TreeModel includa:
def
def
def
def
def
def
def
def
def
def
def
def
def
def
def
def
def
def
get_flags()
get_n_columns()
get_column_type(index)
get_iter(path)
get_iter_from_string(path_string)
get_string_from_iter(iter)
get_iter_root()
get_iter_first()
get_path(iter)
get_value(iter, column)
iter_next(iter)
iter_children(parent)
iter_has_child(iter)
iter_n_children(iter)
iter_nth_child(parent, n)
iter_parent(child)
get(iter, column, ...)
foreach(func, user_data)
Para ilustrar el uso de GenericTreeModel modificaremos el programa de ejemplo filelisting.py y veremos cmo se
crean los mtodos de la interfaz. El programa filelisting-gtm.py muestra los archivos de una carpeta con un pixbuf que
indica si el archivo es una carpeta o no, el nombre de archivo, el tamao del mismo, el modo y hora del ltimo cambio.
El mtodo on_get_flags() debera devolver un valor que es una combinacin de:
gtk.TREE_MODEL_ITERS_PERSIST
gtk.TREE_MODEL_LIST_ONLY
Si el modelo tiene referencias de fila que son vlidas entre cambios de fila (reordenacin, adicin o borrado) entonces se
dbe establecer gtk.TREE_MODEL_ITERS_PERSIST. Igualmente, si el modelo es una lista entonces de debe fijar
gtk.TREE_MODEL_LIST_ONLY. De otro modo, se debe devolver 0 si el modelo no tiene referencias de fila
persistentes y es un modelo de rbol. Para nuestro ejemplo, el modelo es una lista con iteradores TreeIter persitentes.
def on_get_flags(self):
return gtk.TREE_MODEL_LIST_ONLY|gtk.TREE_MODEL_ITERS_PERSIST
El mtodo on_get_n_columns() debera devolver el nmero de columnas que el modelo exporta a la aplicacin.
Nuestro ejemplo mantiene una lista de los tipos de las columnas, de forma que devolvemos la longitud de la lista:
225
class FileListModel(gtk.GenericTreeModel):
...
column_types = (gtk.gdk.Pixbuf, str, long, str, str)
...
def on_get_n_columns(self):
return len(self.column_types)
El mtodo on_get_column_type() debera devolver el tipo de la columna con el valor de ndice index
especificado. Este mtodo es llamado generalmente desde una TreeView cuando se establece su modelo. Se puede,
bien crear una lista o tupla que contenga la informacin de tipos de datos de las columnas o bien generarla al vuelo. En
nuestro ejemplo:
def on_get_column_type(self, n):
return self.column_types[n]
La interfaz GenericTreeModel convierte el tipo de Python a un GType, de forma que el siguiente cdigo:
flm = FileListModel()
print flm.on_get_column_type(1), flm.get_column_type(1)
mostrara:
<type 'str'> <GType gchararray (64)>
Los siguientes mtodos usan referencias de fila que se guardan como datos privados en un TreeIter. La aplicacin no
puede ver la referencia de fila en un TreeIter por lo que se puede usar cualquier elemento nico que se desee como
referencia de fila. Por ejemplo, en un modelo que contiene filas como tuplas, se podra usar la identificacin de tupla
como referencia de la fila. Otro ejemplo sera el uso de un nombre de archivo como referencia de fila en un modelo que
represente los archivos de un directorio. En ambos casos la referencia de fila no se modifica con los cambios en el
modelo, as que los TreeIters se podran marcar como persistentes. La interfaz de aplicacin de PyGTK
GenericTreeModel extraer esas referencias de fila desde los TreeIters y encapsular las referencias de fila en
TreeIters segn sea necesario.
En los siguientes mtodos rowref se refiere a una referencia de fila interna.
El mtodo on_get_iter() debera devolver una rowref del camino de rbol indicado por path. El camino de rbol
se representar siempre mediante una tupla. Nuestro ejemplo usa la cadena del nombre de archivo como rowref. Los
nombres de archivo se guardan en una lista en el modelo, de manera que tomamos el primer ndice del camino como
ndice al nombre de archivo:
def on_get_iter(self, path):
return self.files[path[0]]
Es necesario ser coherente en el uso de las referencias de fila, puesto que se obtendrn referencias de fila tras las
llamadas a los mtodos de GenericTreeModel que toman argumentos con iteradores TreeIter:
on_get_path(), on_get_value(), on_iter_next(), on_iter_children(), on_iter_has_child(),
on_iter_n_children(), on_iter_nth_child() y on_iter_parent().
El mtodo on_get_path() debera devolver un camino de rbol dada una rowref. Por ejemplo, siguiendo con el
ejemplo anterior donde el nombre de archivo se usa como rowref, se podra definir el mtodo on_get_path() as:
def on_get_path(self, rowref):
return self.files.index(rowref)
Este mtodo localiza el ndice de la lista que contiene el nombre de archivo en rowref. Es obvio viendo este ejemplo
que una eleccin juiciosa de la referencia de fila har la implementacin ms eficiente. Se podra usar, por ejemplo, un
diccionario de Python para traducir una rowref a un camino.
226
El mtodo on_get_value() debera devolver los datos almacenados en la fila y columna especificada por rowref y
la columna column. En nuestro ejemplo:
def on_get_value(self, rowref, column):
fname = os.path.join(self.dirname, rowref)
try:
filestat = statcache.stat(fname)
except OSError:
return None
mode = filestat.st_mode
if column is 0:
if stat.S_ISDIR(mode):
return folderpb
else:
return filepb
elif column is 1:
return rowref
elif column is 2:
return filestat.st_size
elif column is 3:
return oct(stat.S_IMODE(mode))
return time.ctime(filestat.st_mtime)
tiene que extraer la informacin de archivo asociada y devolver el valor adecuado en funcin de qu columna se
especifique.
El mtodo on_iter_next() debera devolver una rerferencia de fila a la fila (en el mismo nivel) posterior a la
especificada porrowref. En nuestro ejemplo:
def on_iter_next(self, rowref):
try:
i = self.files.index(rowref)+1
return self.files[i]
except IndexError:
return None
El ndice del nombre de archivo rowref es determinado y se devuelve el siguiente nombre de archivo o None si no
existe un archivo despus.
El mtodo on_iter_children() debera devolver una referencia de fila a la primera fila hija de la fila especificada
por rowref. Si rowref es None entonces se devuelve una referencia a la primera fila de nivel superior. Si no existe
una fila hija se devuelve None. En nuestro ejemplo:
def on_iter_children(self, rowref):
if rowref:
return None
return self.files[0]
Puesto que el modelo es una lista nicamente la fila superior puede tener filas hijas (rowref=None). Se devuelve
None si rowref contiene un nombre de archivo.
El mtodo on_iter_has_child() debera devolver TRUE si la fila especificada por rowref tiene filas hijas o
FALSE en otro caso. Nuestro ejemplo devuelve FALSE puesto que ninguna fila puede tener hijas:
def on_iter_has_child(self, rowref):
return False
227
El mtodo on_iter_n_children() debera devolver el nmero de fijas hijas que tiene la fila especificada por
rowref. Si rowref es None entonces se devuelve el nmero de las filas de nivel superior. Nuestro ejemplo devuelve
0 si rowref no es None:
def on_iter_n_children(self, rowref):
if rowref:
return 0
return len(self.files)
El mtodo on_iter_nth_child() debera devolver una referencia de fila a la n-sima fila hija de la fila especificada
por parent. Si parent es None entonces se devuelve una referencia a la n-sima fila de nivel superior. Nuestro
ejemplo devuelve la n-sima fila de nivel superior si parent es None. En otro caso devuelve None:
def on_iter_nth_child(self, rowref, n):
if rowref:
return None
try:
return self.files[n]
except IndexError:
return None
El mtodo on_iter_parent() debera devolver una referencia de fila a la fila padre de la fila especificada por
rowref. Si rowref apunta a una fila de nivel superior se debe devolver None. Nuestro ejemplo siempre devuelve
None asumiendo que rowref debe apuntar a una fila de nivel superior:
def on_iter_parent(child):
return None
Este ejemplo se ve de una vez en el programa filelisting-gtm.py. Figura 14.11, Programa de Ejemplo de Modelo de
rbol Genrico muestra el resultado de la ejecucin del programa.
Figura 14.11. Programa de Ejemplo de Modelo de rbol Genrico
228
insert()
insert_before()
insert_after()
prepend()
append()
remove()
clear()
Naturalmente, ni todos ni cualquiera de estos mtodos neceista ser implementado. Se pueden crear mtodos propios que
se relacionen de manera ms ajustada al modelo.
Utilizando el programa de ejemplo anterior para ilustrar la adicin de mtodos para eliminar y aadir archivos,
implementemos esos mtodos:
def remove(iter)
def add(filename)
El mtodo remove() elimina el archivo especificado por iter. Adems de eliminar la fila del modelo el mtodo
tambin debera eliminar el archivo de la carpeta. Naturalmente, si el usuario no tiene permisos para eliminar el archivo
no se debera eliminar tampoco la fila. Por ejemplo:
def remove(self, iter):
path = self.get_path(iter)
pathname = self.get_pathname(path)
try:
if os.path.exists(pathname):
os.remove(pathname)
del self.files[path[0]]
self.row_deleted(path)
except OSError:
pass
return
Al mtodo se le pasa un iterador TreeIter que ha de ser convertido a un camino que se usa para obtener el camino del
archivo usando el mtodo get_pathname(). Es posible que el archivo ya haya sido eliminado por lo que debemos
comprobar si existe antes de intentar su eliminacin. Si se emite una excepcin OSError durante la eliminacin del
archivo, probablemente se deba a que es un directorio o que la usuaria no tiene los privilegios necesarios para elimnarlo.
Finalmente, el archivo se elimina y la seal "row-deleted" se emite desde el mtodo rows_deleted(). La seal "filedeleted" notifica a las TreeViews que usan el modelo que ste ha cambiado, por lo que pueden actualizar su estado
interno y mostrar el modelo actualizado.
El mtodo add() necesita crear un archivo con el nombre dado en la carpeta actual. Si se crea el archivo su nombre se
aade a la lista de archivos del modelo. Por ejemplo:
def add(self, filename):
pathname = os.path.join(self.dirname, filename)
229
if os.path.exists(pathname):
return
try:
fd = file(pathname, 'w')
fd.close()
self.dir_ctime = os.stat(self.dirname).st_ctime
files = self.files[1:] + [filename]
files.sort()
self.files = ['..'] + files
path = (self.files.index(filename),)
iter = self.get_iter(path)
self.row_inserted(path, iter)
except OSError:
pass
return
Este ejemplo sencillo se asegura de que el archivo no existe y luego intenta abrir el archivo para escritura. Si tiene xito,
el archivo se cierra y el nombre de archivo ordenado en la lista de archivos. La ruta y el iterador TreeIter de la fila de
archivo aadida se obtienen para usarlos en el mtodo row_inserted() que emite la seal "row-inserted". La seal
"row-inserted" se usa para notificar a las TreeViews que usan el modelo que necesitan actualizar su estado interno y
revisar su visualizacin.
Los otros mtodos mencionados anteriormente (por ejemplo, append y prepend) no tienen sentido en el ejemplo,
puesto que el modelo mantiene la lista de archivos ordenada.
Otros mtodos que puede merecer la pena implementar en un TreeModel que herede de GenericTreeModel son:
set_value()
reorder()
swap()
move_after()
move_before()
La implementacin de estos mtodos es similar a de los mtodos anteriores. Es necesario sincronizar el modelo con el
estado externo y luego notificar a las TreeViews cuando el modelo cambie. Los siguientes mtodos se usan para
notificar a las TreeViews de cambios en el modelo emitiendo la seal apropiada:
def row_changed(path, iter)
# fila cambi def row_inserted(path, iter)
# fila insertada def row_has_child_toggled(path, iter)
# cambio en la existencia de hijas en la fila def row_deleted(path)
# fila eliminada def rows_reordered(path, iter, new_order) # filas reordenadas
14.11.4. Gestin de Memoria
Uno de los problemas de GenericTreeModel es que los TreeIters guardan una referencia a un objeto de Python
devuelto por el modelo de rbol personalizado. Puesto que se puede crear e inicializar un TreeIter en cdigo en C y
que permanezca en la pila, no es posible saber cundo se ha destruido el TreeIter y ya no se usa la referencia al
objeto de Python. Por lo tanto, el objeto de Python referenciado en un TreeIter incrementa por defecto su cuenta de
referencias, pero no se decrementa cuando se destruye el TreeIter. Esto asegura que el objeto de Python no ser
destruido mientras est en uso por un TreeIter, lo que podra causar una violacin de segmento. Desgraciadamente,
la cuenta de referencias extra lleva a la situacin en la que, como bueno, el objeto de Python tendr un contador de
referencias excesivo, y como malo, nunca se liberar esa memoria incluso cuando ya no se usa. Este ltimo caso lleva a
prdidas de memoria y el primero a prdidas de referencias.
Para prever la situacin en la que el TreeModel personalizado mantiene una referencia al objeto de Python hasta que
ya no se dispone de l (es decir, el TreeIter ya no es vlido porque el modelo ha cambiado) y no hay necesidad de
230
231
La clase GenericTreeModel debera usarse como ltimo recurso. Existen mecanismos muy poderosos en el grupo
estndar de objetos TreeView que deberan ser suficientes para la mayor parte de aplicaciones. Sin duda que existen
aplicaciones que pueden requerir el suo de GenericTreeModel pero se debera probar antes a utilizar lo siguiente:
Como se ilustra en la seccin Funcin de Datos de Celda, las funciones de datos de celda se
pueden usar para modificar e incluso generar los datos de una columna de TreeView. Se
Funciones de Datos de
pueden crear de forma efectiva tantas columnas con datos generados como se precise. Esto
Celda
proporciona un gran control sobre la presentacin de los datos a partir de una fuente de datos
subyacente.
Modelo de rbol
Filtrado
(TreeModelFilter)
la interfaz TreeModel completa debe ser creada y debe funcionar tal como se document. Hay sutilezas que
pueden conducir a errores. Por el contrario, los TreeModels estndar estn muy revisados.
la gestin de referencias a objetos de Python utilizados por iteradores TreeIter puede ser complicada,
especialmente en el caso de programas que se ejecuten durante mucho tiempo y con gran variedad de
visualizaciones.
es necesaria la adicin de una interfaz para aadir, borrar y cambiar los contenidos de las filas. Hay cierta
complicacin con la traduccin de los TreeIter a objetos de Python y a las filas del modelo en esta interfaz.
la implementacin de las interfaces de ordenacin y arrastrar y soltar exigen un esfuerzo considerable. La
aplicacin posiblemente necesite implicarse en hacer que estas interfaces sean completamente funcionales.
232
Los elementosClipboard estn hechos sobre las interfaces de seleccin y SelectionData. El portapapeles usado
por defecto por los controles TextView, Label y Entry es "CLIPBOARD". Otros portapapeles comunes son
"PRIMARY" y "SECONDARY", que se corresponden con las selecciones primaria y secundaria (Win32 las ignora).
Estos objetos pueden igualmente ser especificados utilizando los objetos gtk.gdk.Atom siguientes:
gtk.gdk.SELECTION_CLIPBOARD, gtk.gdk.SELECTION_PRIMARY y
gtk.gdk.SELECTION_SECONDARY. Para mayor informacin vase la documentacin de referencia de
gtk.gdk.Atom.
15.1.1. Creacin de un objeto Clipboard (Portapapeles)
Los objetos Clipboard se crean mediante el constructor:
clipboard = gtk.Clipboard(display, selection)
donde display es el gtk.gdk.Display asociado con el Clipboard denominado por selection. La funcin
auxiliar que sigue crea un portapapeles (Clipboard) haciendo del gtk.gdk.Display por defecto:
clipboard = gtk.clipboard_get(selection)
Finalmente, un Clipboard tambin puede crearse utilizando el mtodo de la clase Widget:
clipboard = widget.get_clipboard(selection)
El control widget debe haber sido realizado y ha de formar parte de una ventana de una jerarqua de ventana de primer
nivel.
15.1.2. Utilizacin de Clipboards con elementos Entry, Spinbutton y TextView
Los controles Entry, SpinButton y TextView poseen mens emergentes que proporcionan la capacidad de copiar
y pegar el texto seleccionado desde el portapapeles 'CLIPBOARD' as como pegarlo desde el mismo. Adems, se
dispone de combinaciones de teclas que sirven de atajos para cortar, copiar y pegar. Cortar se activa con Control+X;
copiar con Control+C; y pegar con Control+V.
Esos controles (Entry y SpinButton) implementan la interfaz Editable, de manera que poseen los siguientes
mtodos para cortar, copiar y pegar de y hacia el portapapeles "CLIPBOARD":
editable.cut_clipboard()
editable.copy_clipboard()
editable.paste_clipboard()
Una etiqueta Label que sea seleccionable (su propiedad "selectable" es TRUE) tambin permite la copia del texto
seleccionado al portapapeles "CLIPBOARD" utilizando un men emergente o la combinacin de teclas rpidas
Control+C.
Los TextBuffers poseen mtodos simiares, aunque permiten indicar el portapapeles que se ha de usar:
textbuffer.copy_clipboard(clipboard)
El texto de la seleccin ser copiado al portapapeles especificado por clipboard.
textbuffer.cut_clipboard(clipboard, default_editable)
El texto seleccionado ser copiado a clipboard. Si default_editable es TRUE, entonces el texto seleccionado
ser borrado del TextBuffer. En otro caso, cut_clipboard() funcionar como el mtodo copy_clipboard().
textbuffer.paste_clipboard(clipboard, override_location, default_editable)
233
Si default_editable es TRUE, los contenidos del portapapeles clipboard sern insertados en el TextBuffer
en la posicin indicada por el iterador TextIter override_location. Si default_editable es FALSE,
entonces paste_clipboard() no insertar los contenidos de clipboard. Si override_location es None los
contenidos del portapapeles clipboard se insertarn en la posicin del cursor.
Los TextBuffers tambin tienen dos mtodos para gestionar un conjunto de objetos Clipboard que se establecen
de forma automtica con los contenidos de la seleccin vigente:
textbuffer.add_selection_clipboard(clipboard) # aadir portapapeles de
seleccin
textbuffer.remove_selection_clipboard(clipboard) # eliminar portapapeles de
seleccin
Cuando se aade un TextBuffer a una vista de texto (TextView) se aade automticamente el portapapeles
"PRIMARY" a los portapapeles de seleccin. La aplicacin puede aadir otros portapapeles a conveniencia (por
ejemplo, el portapapeles "CLIPBOARD").
15.1.3. Incorporacin de datos en un portapapeles
Se pueden establecer los datos de un portapapeles Clipboard de forma programtica utilizando los mtodos:
clipboard.set_with_data(targets, get_func, clear_func, user_data)
clipboard.set_text(text, len=-1)
El mtodo set_with_data() indica que destinos se soportan para los datos seleccionados y proporciona las funciones
(get_func y clear_func), que se llaman al solicitar o cuando cambian los datos del portapapeles. user_data se
pasa a get_func o clear_func cuando son llamadas. targets es una lista de tuplas de tres elementos que
contiene:
234
return
def text_clear_func(clipboard, data):
del data
return
self.set_with_data(targets, text_get_func, text_clear_func, text)
return
Una vez que se introducen datos en un portapapeles, stos estarn disponibles hasta que se termine el programa o se
cambien los datos en el portapapeles.
Para proporcionar el comportamiento tpico de cortar al portapapeles la aplicacin tendr que eliminar el texto u objeto
correspondiente tras copiarlo al portapapeles.
15.1.4. Obtencin de los Contenidos del Portapapeles
Los contenidos de un portapapeles Clipboard se pueden obtener utilizando los siguientes mtodos:
clipboard.request_contents(target, callback, user_data=None)
Los contenidos especificados por target son obtenidos de forma asncrona en la funcin indicada por callback,
que es llamada con user_data. La signatura de callback es:
def callback(clipboard, selectiondata, data):
donde selectiondata es un objeto SelectionData con los contenidos del portapapeles clipboard. data son
datos de usuario user_data. El mtodo request_contents() es la forma ms general de obtener los contenidos
de un portapapeles Clipboard. El siguiente mtodo auxiliar recupera los contenidos de texto de un portapapeles
Clipboard:
clipboard.request_text(callback, user_data=None)
Se devuelve la cadena de texto a la retrollamada en vez de un objeto Selectiondata. Se puede comprobar los
destinos disponibles para el portapapeles Clipboard utilizando el mtodo:
clipboard.request_targets(callback, user_data=None)
Los destinos se devuelven a la funcin de retrollamada como tuplas de objetos gtk.gdk.Atom.
Se proporcionan dos mtodos auxiliares que devuelven los contenidos del portapapeles Clipboard de forma sncrona:
selectiondata = clipboard.wait_for_contents(target)
text = clipboard.wait_for_text()
15.1.5. Ejemplo de Portapapeles
Para ilustrar el uso de un portapapeles Clipboard el programa de ejemplo clipboard.py sigue los elementos de texto
que se copian o cortan al cortapapeles "CLIPBOARD" y guarda las ltimas diez entradas del cortapapeles. Hay diez
botones que proporcionan acceso al texto de las entradas guardadas. La etiqueta del botn muestra los primeros dieciseis
caracteres del texto almacenado y las etiquetas de ayuda (pistas) muestran los destinos que tena la entrada original.
Cuando se pulsa un botn de entrada se carga el texto correspondiente, que puede ser editado. A su vez, ell botn bajo la
ventana de texto permite guardar los contenidos de la ventana de texto al portapapeles..
Figura 15.1, Programa de ejemplo de Portapapeles ilustra el resultado de la ejecucin del programa clipboard.py:
Figura 15.1. Programa de ejemplo de Portapapeles
235
El programa de ejemplo consulta el portapapeles cada 1.5 segundos para ver si los contenidos han cambiado. El
programa podra modificarse para duplicar el conjunto completo de destinos disponibles y tomar luego control del
portapapeles utilizando el mtodo set_with_data(). Posteriormente, si otro programa fija los contenidos del
portapapeles, se llamar a la funcin clear_func, que se puede usar para recargar los contenidos del portapapeles y
retomar la propiedad del mismo.
Captulo 16. Nuevos Controles de PyGTK 2.4
Tabla de contenidos
16.1. Objetos de Accin (Action) y Grupo de Acciones (ActionGroup)
16.1.1. Acciones (Actions)
16.1.2. Grupos de Acciones (ActionGroups)
16.2. Controles de Lista Desplegable (ComboBox) y Lista Desplegable con Entrada (ComboBoxEntry)
16.2.1. Controles ComboBox
16.2.2. Controles ComboBoxEntry
16.3. Controles Botn de Color y de Fuente (ColorButton y FontButton)
16.3.1. Control Botn de Color (ColorButton)
16.3.2. Control Botn de Fuente (FontButton)
16.4. Controles de Entrada con Completado (EntryCompletion)
16.5. Controles de Expansin (Expander)
16.6. Selecciones de Archivos mediante el uso de Controles basados en el Selector de Archivos FileChooser
16.7. El gestor de Interfaces de Usuario UIManager
16.7.1. Perspectiva general
16.7.2. Creacin de un gestor UIManager
16.7.3. Adicin y Eliminacin de Grupos de Acciones (ActionGroups)
16.7.4. Descripciones de la Interfaz de Usuario
16.7.5. Adicin y Eliminacin de Descripciones de Interfaz de Usuario
16.7.6. Acceso a los Controles de la Interfaz de Usuario
16.7.7. Ejemplo sencillo de Gestor de Interfaz UIManager
16.7.8. Combinacin de Descripciones de Interfaz de Usuario
16.7.9. Seales de UIManager
En PyGTK 2.4 se han aadido unos cuantos controles y objetos auxiliares nuevos, includos:
Action (Accin), RadioAction (Accin de Exclusin Mtua), ToggleAction (Accin Biestado) objetos que representan acciones que puede tomar la usuaria. Las acciones contienen informacin necesaria para
236
crear controles delegados (proxy) (por ejemplo: iconos, elementos de men y elementos de barra de
herramientas).
ActionGroup (Grupo de Acciones) - un objeto que contiene Actions que estn relacionadas de alguna
manera, por ejemplo, acciones para abrir, cerrar e imprimir un documento.
Border (Borde) - un objeto que contiene los valores de un borde.
ColorButton (Botn de Color) - un botn usado para laznar un dilogo de seleccin de Color
(ColorSelectionDialog).
ComboBox (Lista desplegable) - un control que proporciona una lista de elementos entre los que elegir.
Sustituye al men de opciones OptionMenu.
ComboBoxEntry (Lista con entrada) - un control que proporciona un campo de entrada de texto con una lista
desplegable de elementos entre los que elegir. Remplaza al control de lista desplegable Combo.
EntryCompletion (Entrada con completado) - un objeto que proporciona completado a un control de
entrada Entry.
Expander (Elemento de Expansin) - un contenedor que puede mostrar u ocultar sus hijos en respuesta a su
pulsacin de botn.
FileChooser (Selector de Ficheros) - una interfaz para seleccionar ficheros.
FileChooserWidget (Control de Seleccin de Ficheros) - un control que implementa la interfaz
FileChooser. Sustituye al control FileSelection.
FileChooserDialog (Dilogo de Seleccin de Ficheros) - un dilogo utilizado para las accines
"Archivo/Abrir" y "Archivo/Guardar". Sustituye a FileSelectionDialog.
FileFilter (Filtro de Ficheros) - un objeto usado para filtrar archivos en base a un conjunto interno de
reglas.
FontButton (Botn de Fuentes) - un botn que lanza el dilogo de seleccin de fuentes
FontSelectionDialog.
IconInfo (Informacin de Icono) - un objeto que contiene informacin sobre un icono de un tema
IconTheme.
IconTheme (Tema de Iconos) - un objeto que proporciona bsqueda de iconos por nombre y tamao.
ToolItem (Elemento genrico), ToolButton (Botn), RadioToolButton (Botn de exclusin),
SeparatorToolItem (Separador), ToggleToolButton (Botn biestado) - controles que se pueden
aadir a una barra de herramientas Toolbar. Todos estos sustituyen a los anteriores elementos de Toolbar.
TreeModelFilter (Modelo de rbol Filtrado) - un objeto que proporciona un potente mecanismo para
transformar la representacin de un modelo TreeModel subyacente. Se describe en la seccin Seccin de
TreeModelFilter.
UIManager (Gestor de Interfaz de Usuario) - un objeto que proporciona una manera de construir mens y
barras de herramientas a partir de una descripcin de la interfaz en XML. Tambin tiene mtodos para gestionar
la combinacin y separacin de mltiples descripciones de interfaces de usuario.
237
Una accin Action que puede agruparse de manera que solamente una de ellas puede
estar activa.
Por ejemplo, el elemento de men estndar Archivo Salir puede ser representado mediante un icono, un texto
mnemnico y un acelerador. Cuando se activa, el elemento de men dispara una retrollamada que podra cerrar la
aplicacin. De la misma manera, un botn Salir de una barra de herramientas Toolbar podra compartir con aquel el
icono, el texto mnemnico y la retrollamada. Ambos elementos de la interfaz podran ser elementos delegados (proxies)
de la misma accin Action.
Los controles de botn normal (Button), botn conmutado (ToggleButton) y botn de exclusin (RadioButton)
pueden actuar como delegados (proxies) de una accin (Action), aunque no existe soporte para ellos en la clase
UIManager.
16.1.1.1. Creacin de acciones
Las acciones Action se crean haciendo uso del constructor:
action = gtk.Action(name, label, tooltip, stock_id)
name (nombre) es una cadena que identifica la accin (Action) dentro de un grupo (ActionGroup) o en la
especificacin de un gestor de interfaz (UIManager). label y tooltip son cadenas de texto usadas
respectivamente como etiqueta y texto de ayuda en los objetos delegados (proxy). Si label es None entonces
stock_id ha de ser una cadena de texto que especifique un elemento de serie (Stock item) del que se obtendr la
etiqueta. Si tooltip es None, entonces la accin (Action) no dispondr de texto de ayuda.
Como veremos en la seccin correspondiente a grupos de acciones (ActionGroups) es mucho ms fcil la creacin de
objetos de accin utilizando algunos mtodos de la clase ActionGroup:
actiongroup.add_actions(entries, user_data=None)
actiongroup.add_toggle_actions(entries, user_data=None)
actiongroup.add_radio_actions(entries, value=0, on_change=None,
user_data=None)
Ms adelante profundizaremos en stos, aunque primeramente describiremos cmo usar una accin (Action) con un
botn (Button) para ilustrar las operaciones bsicas necesarias para conectar una accin (Action) a un objeto
delegado.
16.1.1.2. Uso de acciones
El procedimiento bsico para usar una accin (Action) con un botn (Button) como objeto delegado se ilustra en el
programa de ejemplo simpleaction.py. El botn (Button) se conecta a la accin (Action) con el mtodo:
action.connect_proxy(proxy)
donde proxy es un control delegado del tipo: elemento de men MenuItem, elemento de barra de herramientas
ToolItem o botn Button.
Una accin Action tiene una seal, la seal "activate" que se dispara cuando la accin (Action) se activa,
generalmente como resultado de la activacin de un control delegado (por ejemplo, cuando se pulsa sobre un botn de
una barra de herramientas ToolButton). Simplemente se debe conectar una retrollamada a esta seal para gestionar la
activacin de cualquiera de los controles delegados.
Este es el cdigo fuente del programa de ejemplo simpleaction.py:
1
2
#!/usr/bin/env python
238
3
import pygtk
4
pygtk.require('2.0')
5
import gtk
6
7
class SimpleAction:
8
def __init__(self):
9
# Creacin de la ventana principal
10
window = gtk.Window()
11
window.set_size_request(70, 30)
12
window.connect('destroy', lambda w: gtk.main_quit())
13
14
# Creacin de un grupo de aceleradores
15
accelgroup = gtk.AccelGroup()
16
# Aadimos el grupo de aceleradores a la ventana principal
17
window.add_accel_group(accelgroup)
18
19
# Creacin de una accin para salir del programa usando un
elemento de serie
20
action = gtk.Action('Quit', None, None, gtk.STOCK_QUIT)
21
# Conexin de una retrollamada a la accin
22
action.connect('activate', self.quit_cb)
23
24
# Creacin de un grupo ActionGroup llamado SimpleAction
25
actiongroup = gtk.ActionGroup('SimpleAction')
26
# Adicin de la accin al grupo con un acelerador
27
# None significa que se utilice el acelerador el elemento de
serie
28
actiongroup.add_action_with_accel(action, None)
29
30
# Hacer que la accin use el grupo de aceleradores
31
action.set_accel_group(accelgroup)
32
33
# Conectar el acelerador a la accin
34
action.connect_accelerator()
35
36
# Crear el botn para usarlo como control delegado para la
accin
37
quitbutton = gtk.Button()
38
# aadimos el botn a la ventana principal
39
window.add(quitbutton)
40
41
# Conectamos la accin a su control delegado
42
action.connect_proxy(quitbutton)
43
44
window.show_all()
45
return
46
47
def quit_cb(self, b):
48
print 'Saliendo del programa'
49
gtk.main_quit()
50
51
if __name__ == '__main__':
52
sa = SimpleAction()
53
gtk.main()
El ejemplo crea una accin (Action) (lnea 20) que usa un elemento de serie que aporta el texto de etiqueta, con un
mnemnico, un icono, un acelerador y un dominio de traduccin. Si no se usa un elemento de serie, sera necesario
especificar en su lugar una etiqueta. La lnea 22 conecta la seal "activate" del la accin action al mtodo
self.quit_cb() de forma que sea invocado cuando la accin Action es activada por el botn quitbutton. La
lnea 42 conecta quitbutton a action como control delegado. Cuando se pulsa sobre quitbutton ste activar
239
action y, por tanto, llamar al mtodo self.quit_cb(). El ejemplo simpleaction.py usa bastante cdigo (lneas 15,
17, 31 y 34 para establecer el acelerador del botn). El proceso es similar para los elementos MenuItem y ToolItem.
Figura 16.1, Ejemplo Simple de Accin muestra el resultado de la ejecucin del programa simpleaction.py.
Figura 16.1. Ejemplo Simple de Accin
240
30
# Agregamos la accin al grupo con un acelerador
31
# None significa que se usa el acelerador del elemento de serie
32
actiongroup.add_action_with_accel(action, None)
33
34
# Hacemos que la accin use el grupo de aceleradores
35
action.set_accel_group(accelgroup)
36
37
# Creacin de una barra de men
38
menubar = gtk.MenuBar()
39
menubar.show()
40
vbox.pack_start(menubar, False)
41
42
# Creacin de la accin para Archivo y el elemento de men
43
file_action = gtk.Action('File', '_File', None, None)
44
actiongroup.add_action(file_action)
45
file_menuitem = file_action.create_menu_item()
46
menubar.append(file_menuitem)
47
48
# Creacin del men de Archivo
49
file_menu = gtk.Menu()
50
file_menuitem.set_submenu(file_menu)
51
52
# Creacin de un elemento delegado
53
menuitem = action.create_menu_item()
54
file_menu.append(menuitem)
55
56
# Creacin de una barra de herramientas
57
toolbar = gtk.Toolbar()
58
toolbar.show()
59
vbox.pack_start(toolbar, False)
60
61
# Creacin de un elemento delegado de la barra de herramientas
62
toolitem = action.create_tool_item()
63
toolbar.insert(toolitem, 0)
64
65
# Creacin y empaquetado de un control de Etiqueta
66
label = gtk.Label('''
67
Select File->Quit me! or
68
click the toolbar Quit button or
69
click the Quit button below or
70
press Control+q
71
to quit.
72
''')
73
label.show()
74
vbox.pack_start(label)
75
76
# Creacin de un botn para usarlo como otro control delegado
77
quitbutton = gtk.Button()
78
# add it to the window
79
vbox.pack_start(quitbutton, False)
80
81
# Conexin de la accin a su control delegado
82
action.connect_proxy(quitbutton)
83
# Se establece el texto de ayuda despus de que se aade el
elemento a la barra
84
action.set_property('tooltip', action.get_property('tooltip'))
85
tooltips = gtk.Tooltips()
86
tooltips.set_tip(quitbutton, action.get_property('tooltip'))
87
88
window.show()
241
89
90
91
92
93
94
95
96
97
return
def quit_cb(self, b):
print 'Saliendo del programa'
gtk.main_quit()
if __name__ == '__main__':
ba = BasicAction()
gtk.main()
Este ejemplo presenta un grupo de acciones ActionGroup como contenedor de las acciones Action que se usan en
el programa. El apartado sobre grupos de acciones ActionGroups se adentrar en mayor detalle en su uso.
El cdigo de las lneas 9-14 establece una ventana principal que contiene una VBox. Las lneas 16-35 establecen la
accin Action "Quit" (salir) de la misma manera que en el programa de ejemplo simpleaction.py y la aaden con el
acelerador de serie gtk.STOCK_QUIT (lnea 32) al grupo de accin ActionGroup "BasicAction" (creado en la lnea
29). Es de advertir el hecho de que, a diferencia del ejemplo simpleaction.py, aqu no es necesario llamar al mtodo
connect_accelerator() para la accin puesto que se hace de forma automtica cuando se llama al mtodo
create_menu_item() en la lnea 53.
Las lneas 38-40 crean una barra de men (MenuBar) y la empaquetan en la VBox. Las lneas 43-44 crean una accin
Action (file_action) para el men File y la aaden al grupo actiongroup. Los elementos de men File y Quit
son creados en las lneas 45 y 53 y aadidos a la barra de mens (menubar) y al men (file_menu) respectivamente
en las lneas 46 y 54.
Igualmente se crea una barra de herramientas (Toolbar) que se aade a la VBox en las lneas 57-59. El elemento de la
barra de herramientas ToolItem que acta como delegado se crea y aade a la barra de herramientas en las lneas 6263. Ha de advertirse que el texto de ayuda de la accin Action debe establecerse (lnea 84) tras haber aadido el
elemento de la barra de herramientas ToolItem a la propia barra de herramientas (Toolbar) para que pueda ser
usado. De la misma manera, el texto de ayuda del botn Button debe aadirse de forma manual (lneas 84-86).
Figura 16.2, Ejemplo Bsico de Accin muestra el programa de ejemplo basicaction.py en accin:
Figura 16.2. Ejemplo Bsico de Accin
Es posible desconectar un control delegado (proxy) de una accin (Action) usando el mtodo:
action.disconnect_proxy(proxy)
16.1.1.4. Propiedades de Accin
242
Una accin Action tiene una serie de propiedades que controlan la representacin y funcionamiento de sus controles
delegados. Las ms importantes entre ellas son las propiedades "sensitive" y "visible". La propiedad "sensitive"
determina la sensibilidad de los controles delegados. Si "sensitive" es FALSE los controles delegados no pueden
activarse y normalmente se representarn engrisecidos. De la misma manera, la propiedad "visible" determina si los
controles delegados son o no visibles. Si la propiedad "visible" de una accin Action es FALSE entonces sus controles
delegados permanecern ocultos.
Como veremos en el siguiente apartado, la sesibilidad o visibilidad de una accin Action tambin est controlada por
la sensibilidad o visibilidad del grupo de acciones ActionGroup al que pertenece. Por lo tanto, para que una accin
Action sea visible o sensible tanto sus propiedades como las del grupo ActionGroup al que pertenece deben
concordar. Para establecer la sensibilidad o visibilidad efectiva de una accin Action se deben usar los siguientes
mtodos:
result = action.is_sensitive()
result = action.is_visible()
El nombre asignado a una accin Action se almacena en su propiedad "name", que se establece en el momento en que
se crea la accin Action. Se puede obtener dicho nombre usando el mtodo:
name = action.get_name()
Otras propiedades que controlan la visualizacin de los controles delegados de una accin Action son:
"hide-if-empty" Si es TRUE, se ocultan los delegados de men vacos para esta accin.
"is-important"
Si es TRUE, los elementos de barra de herramientas ToolItem delegados para esta accin muestran
su texto en el modo gtk.TOOLBAR_BOTH_HORIZ.
"visiblehorizontal"
"visiblevertical"
La etiqueta usada para los elementos de men y botones que activan esta accin.
"shortlabel"
Una etiqueta ms breve que puede usarse en botones de barras de herramientas y en botones.
"stock-id"
El Elemento de Serie utilizado para obtener el icono, etiqueta y acelerador que se usar en los controles que
representen esta accin.
"tooltip"
Advirtase que el programa de ejemplo basicaction.py anula la etiqueta de gtk.STOCK_QUIT sustituyndola por
"_Quit me!" y fija la propiedad "short-label" a "_Quit". La etiqueta corta se usa para las etiquetas del botn de barra de
herramientas ToolButton y para la del botn Button, pero se usa la etiqueta completa en el caso del elemento de
men MenuItem. Tambin es de resear que no es posible establecer el texto de ayuda de un elemento ToolItem
hasta que es aadido a su barra de herramientas (Toolbar).
16.1.1.5. Acciones y Aceleradores
Una accin Action tiene tres mtodos usados para establecer un acelerador:
action.set_accel_group(accel_group)
action.set_accel_path(accel_path)
243
action.connect_accelerator()
stos, adems del mtodo gtk.ActionGroup.add_action_with_accel(), deberan ser suficientes en los casos
en los que se haya de establecer un acelerador.
Un grupo de aceleracin AccelGroup debe estabecerse siempre para una accinAction. El mtodo
set_accel_path() es llamado por el mtodo gtk.ActionGroup.add_action_with_accel(). Si se usa
set_accel_path() entonces el camino del acelerador debera utilizar el formato por defecto:
"<Actions>/nombre_del_grupo_de_acciones/nombre_de_la_accin". Finalmente, el mtodo
connect_accelerator() es llamado para completar la configuracin de un acelerador.
Nota
Una accin Action debe tener un grupo AccelGroup y un camino de acelerador asociado a l antes
de la llamada a connect_accelerator().
Puesto que el mtodo connect_accelerator() puede ser llamado varias veces (por ejemplo, una vez por cada
control delegado), el nmero de llamadas es almacenado, de forma que se produzca un nmero igual de llamadas al
mtodo disconnect_accelerator() antes de que se elimine el acelerador.
Tal como se ilustr en los programas de ejemplo anteriores, un acelerador de una accin Action puede ser usado por
todos los controles delegados. Una accin Action debera ser parte de un grupo ActionGroup para poder usar el
camino de acelerador por defecto, que tiene el formato:
"<Actions>/nombre_del_grupo_de_acciones/nombre_de_la_accin". La forma ms sencilla de aadir un acelerador es
usar el mtodo gtk.ActionGroup.add_action_with_accel() y el siguiente mtodo general:
Cualquier control delegado creado por o conectado a la accin Action elegida utilizar el acelerador as establecido.
16.1.1.6. Acciones Conmutables
Tal como se mencion previamente, una accin ToggleAction es una subclase de Action que permite la
conmutacin entre dos estados. El constructor de una accin del tipo ToggleAction requiere los mismos parmetros
que una del tipo Action:
toggleaction = gtk.ToggleAction(name, label, tooltip, stock_id)
Adems de los mtodos de la clase Action, tambin dispone de los siguientes mtodos propios de ToggleAction:
toggleaction.set_active(is_active)
is_active = toggleaction.get_active()
establecer u obtener el valor actual de toggleaction. is_active es un valor booleano.
Es posible conectarse a la seal "toggled" especificando una retrollamada con la signatura:
244
245
Este ejemplo es suficientemente parecido a basicaction.py como para que no sea precisa una descripcin detallada del
mismo.
16.1.2. Grupos de Acciones (ActionGroups)
Tal como se mencion en el apartado anterior, los objetos Action relacionados deberan aadirse a un grupo
ActionGroup de manera que se disponga de un control conjunto sobre su visibilidad y sensibilidad. Por ejemplo, en
una aplicacin de procesamiento de textos, los elementos de men y los botones de la barra de herramientas que
especifican la justificacin del texto podran estar agrupados en un ActionGroup. Es de esperar que una interfaz de
usuario tenga mltiples objetos ActionGroup que abarquen los diversos aspectos de la aplicacin. Por ejemplo, las
acciones globales tales como la creacin de nuevos documentos, abrir y guardar un documento y salir de la aplicacin
probablemente formen un grupo ActionGroup, mientras que acciones tales como la modificacin de la vista del
documento formaran otro.
16.1.2.1. Creacin de grupos de acciones (ActionGroups)
Un ActionGroup se crea mediante el constructor:
actiongroup = gtk.ActionGroup(name)
donde name es un nombre nico para el grupo ActionGroup. El nombre debera ser nico puesto que se usa para
construir el camino de acelerador por defecto de sus objetos Action.
El nombre de un ActionGroup puede obtenerse usando el mtodo:
name = actiongroup.get_name()
u obteniendo el contenido de la propiedad "name".
16.1.2.2. Adicin de Acciones
Tal como se ilustra en el apartado Acciones, se puede aadir una accin Action existente a un grupo ActionGroup
utilizando uno de los mtodos siguientes:
actiongroup.add_action(action)
actiongroup.add_action_with_accel(action, accelerator)
donde action es la accin Action que se va a aadir y accelerator es una especificacin de cadena de
acelerador que resulte aceptable a la funcin gtk.accelerator_parse(). Si accelerator es None se usar el
acelerador (si existe) asociado con la propiedad "stock-id" de action. Tal como se indic anteriormente, el mtodo
add_action_wih_accel() es el preferible si se desea utilizar aceleradores.
246
El grupo ActionGroup ofrece tres mtodos para hacer ms sencilla la creacin y adicin de objetos de accin
(Action) a un grupo de accin (ActionGroup):
actiongroup.add_actions(entries, user_data=None)
actiongroup.add_toggle_actions(entries, user_data=None)
actiongroup.add_radio_actions(entries, value=0, on_change=None,
user_data=None)
El parmetro entries es una secuencia de tuplas de entradas de acciones que aportan la informacin necesaria para
crear las acciones que se aaden al grupo ActionGroup. El objeto RadioAction con el valor value se fija
inicialmente como activo. on_change es una retrollamada que est conectada a la seal "changed" del primer objeto
RadioAction del grupo. La signatura de on_changed es:
def on_changed_cb(radioaction, current, user_data)
Las tuplas de entradas de objetos Action contienen:
Como mnimo se debe especificar un valor para el campo name y un valor bien en el campo stock id o el campo
label. Si se especifica una etiqueta entonces se puede indicar None como identificador de elemento estndar (stock
id) si no se usa uno. Por ejemplo, la siguiente llamada al mtodo:
actiongroup.add_actions([('quit', gtk.STOCK_QUIT, '_Quit me!', None,
'Quit the Program', quit_cb)])
aade una accin a actiongroup para salir del programa.
Las tuplas de entradas para los objetos ToggleAction son similares a las tuplas de entradas para objetos Action
salvo que existe un campo optativo adicional flag que contiene un valor booleano que indica si la accin est activa. El
valor por defecto del campo flag es FALSE. Por ejemplo, la siguiente llamada:
actiongroup.add_toggle_actions([('mute, None, '_Mute', '<control>m',
'Mute the volume', mute_cb, True)])
aade un objeto ToggleAction al grupo actiongroup y lo configura inicialmente para que est activo.
Las tuplas de entradas para objetos RadioAction son semejantes a las de los objetos Action pero incorporan un
campo value en vez del campo callback:
247
248
249
combobox.append_text(text)
combobox.insert_text(position, text)
combobox.remove_text(position)
donde text es la cadena que se aadir a la ComboBox y position es el ndice donde se insertar o eliminar el
texto text. En la mayora de los casos las funciones y mtodos auxiliares es todo lo que se necesitar.
El programa de ejemplo comboboxbasic.py demuestra el uso de las anteriores funciones y mtodos. Figura 16.5,
ComboBox Bsica ilustra el programa en ejecucin:
Figura 16.5. ComboBox Bsica
Sin embargo, hasta la versin 2.6 no se proporciona en GTK+ un mtodo cmodo para obtener el texto activo. Para ello
se podra usar una implementacin similar a:
def get_active_text(combobox):
model = combobox.get_model()
active = combobox.get_active()
if active < 0:
return None
return model[active][0]
El ndice del elemento activo se obtiene a travs del mtodo:
active = combobox.get_active()
El elemento activo se puede establecer con el mtodo:
combobox.set_active(index)
donde index es un entero mayor que -2. Si index es -1 no hay elemento activo y el control ComboBox estar en
blanco. Si index es menor que -1, la llamada ser ignorada. Si index es mayor que -1 el elemento de la lista con
dicho ndice ser mostrado.
Se puede conectar a la seal "changed" de un ComboBox para recibir notificacin del cambio del elemento activo. La
signatura del manejador de "changed" es:
def changed_cb(combobox, ...):
250
El uso de los objetos TreeModel y CellRenderer se detalla en el captulo de Controles de Vista de rbol.
Los elementos de la lista de la ComboBox se pueden mostrar en una tabla si se tiene un gran nmero de elementos que
visualizar. En otro caso, la lista tendr flechas de desplazamiento si la lista no puede ser mostrada en su totalidad. El
siguiente mtodo se usa para determinar el nmero de columnas que se mostrarn:
combobox.set_wrap_width(width)
donde width es el nmero de columnas de la tabla que muestra los elementos de la lista. Por ejemplo, el programa
comboboxwrap.py muestra una lista de 50 elementos en 5 columnas. Figura 16.6, ComboBox con una Disposicin
Asociada ilustra el programa en accin:
Figura 16.6. ComboBox con una Disposicin Asociada
251
Con un gran nmero de elementos, digamos, por ejemplo, 50, el uso del mtodo set_wrap_width() tendr un mal
rendimiento debido al clculo de la disposicin en tabla. Para tener una nocin del efecto se puede modificar el
programa comboboxwrap.py en su lnea 18 de forma que muestre 150 elementos.
for n in range(150):
Ejecute el programa para obtener una idea aproximada del tiempo de inicializacin. Luego modifquelo comentando la
lnea 17:
#combobox.set_wrap_width(5)
Ejecute y cronometre de nuevo. Debera ejecutarse significativamente ms rpido. Unas 20 veces ms rpido.
Adems del mtodo get_active() antes descrito, se puede obtener un iterador TreeIter que seala la fila activa
mediante el mtodo:
iter = combobox.get_active_iter()
Tambin se puede establecer el elemento de la lista activa usando un iterador TreeIter con el mtodo:
combobox.set_active_iter(iter)
Los mtodos set_row_span_column() y set_column_span_column() permiten la especificacin de un
nmero de columna de un TreeModel que contiene el nmero de filas o columnas que debe abarcar el elemento de la
lista en una disposicin de tabla. Desgraciadamente, en GTK+ 2.4 estos mtodos funcionan mal.
Puesto que ComboBox implementa la interfaz CellLayout, que tiene capacidades similares a las de una
TreeViewColumn (vase la seccin de TreeViewColumn para ms informacin). En resumen, la interfaz
proporciona:
combobox.pack_start(cell, expand=True)
combobox.pack_end(cell, expand=True)
combobox.clear()
Los dos primeros mtodos empaquetan un CellRenderer en la ComboBox y el mtodo clear() elimina todos los
atributos de todos los CellRenderers.
Los siguientes mtodos:
252
253
Obsrvese que cuando se modifica el texto de la entrada Entry debido a la seleccin de un elemento de la lista
desplegable se llama dos veces al manejador de la seal "changed": una vez cuando se elimina el texto, y, otra cuando el
texto se establece desde el elemento seleccionado de la lista.
16.2.2.2. Uso Avanzado de ComboBoxEntry
El constructor de una ComboBoxEntry es:
comboboxentry = gtk.ComboBoxEntry(model=None, column=-1)
donde model es un TreeModel y column es el nmero de la columna en el modelo model que se usar para fijar
los elementos de la lista. Si no se indica la columna el valor predeterminado es -1 que significa que el texto de la
columna no est especificado.
La creacin de una ComboBoxEntry utilizando la funcin auxiliar gtk.combo_box_entry_new_text() es
equivalente al siguiente cdigo:
liststore = gtk.ListStore(str)
comboboxentry = gtk.ComboBoxEntry(liststore, 0)
La ComboBoxEntry aade un par de mtodos que se usan para establecer y recuperar el nmero de columna del
TreeModel que se usar para fijar las cadenas de los elementos de la lista:
comboboxentry.set_text_column(text_column)
text_column = comboboxentry.get_text_column()
La columna de texto tambin se puede obtener y especificar utilizando la propiedad "text-column". Vase la Seccin de
Uso Avanzado de ComboBox para ms informacin sobre el uso avanzado de una ComboBoxEntry.
Nota
La aplicacin debe establecer la columna de texto para que la ComboBoxEntry fije los contenidos de
la entrada Entry desde la lista desplegable. La columna de texto nicamente puede determinarse una
vez, bien utilizando el constructor o utilizando el mtodo set_text_column().
Al crear una ComboBoxEntry sta se empaqueta con un nuevo CellRendererText que no es accesible. El
atributo 'text' del CellRendererText se establece como un efecto colateral de la determinacin de la columna de
texto utilizando el mtodo set_text_column(). Se pueden empaquetar CellRenderers adicionales en una
ComboBoxEntry para la visualizacin en la lista desplegable. Vase la Seccin de Uso Avanzado de ComboBox para
ms informacin.
16.3. Controles Botn de Color y de Fuente (ColorButton y FontButton)
254
255
256
fontbutton.set_use_font(use_font)
use_font = fontbutton.get_use_font()
fontbutton.set_use_size(use_size)
use_size = fontbutton.get_use_size()
El uso de la fuente actual en la etiqueta resulta til a pesar de los cambios inevitables que produce en el tamao del
botn, sin embargo, no ocurre lo mismo con el tamao de la fuente, especialmente si se usan tamaos muy grandes o
muy pequeos. Obsrvese adems que, si se cambian las propiedades "use-font" o "use-size" a TRUE y posteriormente
se vuelven a cambiar a FALSE, se retiene el ltimo valor de fuente y tamao visible. Por ejemplo, si "use-font" y "usesize" son TRUE y la fuente actual es Monospace Italic 20, entonces la etiqueta de FontButton se muestra
usando la fuente Monospace Italic 20; si entonces cambiamos "use-font" y "use-size" a FALSE y la fuente actual
a Sans 12 la etiquteta todava mostrar la fuente Monospace Italic 20. Use el programa de ejemplo
fontbutton.py para ver cmo funciona todo esto.
Finalmente, el ttulo del dilogo de seleccin de fuente FontSelectionDialog se puede modificar y obtener con
los mtodos:
fontbutton.set_title(title)
title = fontbutton.get_title()
Al igual que un botn ColorButton, es posible seguir los cambios en la fuente actual conectndose a la seal "fontset" que se emite cuando la usuaria modifica la fuente. La signatura de la funcin de retrollamada es la que sigue:
def font_set_cb(fontbutton, user_data):
El programa de ejemplo fontbutton.py ilustra el uso del control FontButton. En l se pueden modificar las
propiedades "use-font", "use-size", "show-size" y "show-style" mediante botones biestado. Figura 16.9, Ejemplo de
Botn de Fuente - FontButton muestra el programa en ejecucin.
Figura 16.9. Ejemplo de Botn de Fuente - FontButton
257
El programa comienza con un pequeo nmero de cadenas para el completado que puede ser aumentado escribiendo en
el campo de entrada y presionando la tecla Enter. Si la cadena es nica entonce se agrega a la lista de cadenas de
completado.
258
La funcin de coincidencias preconstruida no diferencia entre maysculas y minsculas. Si se necesita una funcin ms
especializada, se puede usar el siguiente mtodo para instalar una funcin propia:
completion.set_match_func(func, user_data)
La signatura de func es:
def func(completion, key_string, iter, data):
donde key_string contiene el texto actual de la entrada Entry, iter es un iterador TreeIter que seala la fila
del modelo TreeModel, y data son datos de usuario user_data. func debe devolver TRUE si la cadena de
completado de la fila tiene que ser desplegada.
El fragmento de cdigo mostrado a continuacin usa una funcin de coincidencia para desplegar los nombres de
completado que comienzan con el contenido de la entrada y tienen el sufijo dado, en este caso, un nombre terminado en
.png para un archivo PNG.
...
completion.set_match_func(end_match, (0, '.png'))
...
def end_match(completion, entrystr, iter, data):
column, suffix = data
model = completion.get_model()
modelstr = model[iter][column]
return modelstr.startswith(entrystr) and modelstr.endswith(suffix)
...
Por ejemplo, si el usuario teclea 'foo' y el modelo de completado contiene cadenas como 'foobar.png', 'smiley.png',
'foot.png' y 'foo.tif', las cadenas 'foobar.png' y 'foot.png' deberan mostrarse como alternativas.
16.5. Controles de Expansin (Expander)
El control Expander es un contenedor bastante simple que permite mostrar u ocultar su control hijo haciendo clic en
un tringulo similar al de un TreeView. Se crean nuevos Expander con el constructor:
expander = gtk.Expander(label=None)
donde label es una cadena de texto utilizada como etiqueta del expansor. Si label es None o no se especifica, no se
crea ninguna etiqueta. Alternativamente, se puede usar la funcin:
expander = gtk.expander_new_with_mnemonic(label=None)
que establece el carcter de la etiqueta precedido por un guin bajo como atajo de teclado mnemnico.
El control Expander usa la API de Container para aadir y eliminar su control hijo:
expander.add(widget)
expander.remove(widget)
El control hijo se puede obtener utilizando el atributo de Bin "child" o el mtodo get_child().
La opcin que controla la interpretacin de los guiones bajos de la etiqueta se puede obtener y cambiar con los mtodos:
use_underline = expander.get_use_underline()
expander.set_use_underline(use_underline)
259
Si se desea usar etiquetas de marcado de Pango (vase la Referencia de Marcas de Pango para ms detalles) en la cadena
de la etiqueta se usan los siguientes mtodos para establecer y obtener el estado de la propiedad "use-markup":
expander.set_use_markup(use_markup)
use_markup = expander.get_use_markup()
Finalmente, se puede utilizar cualquier control como control de etiqueta utilizando el mtodo siguiente:
expander.set_label_widget(label_widget)
Que permite, por ejemplo, poder utilizar una HBox empaquetada con una imagen y un texto de etiqueta.
Se puede obtener y establecer el estado del Expander utilizando los mtodos:
expanded = expander.get_expanded()
expander.set_expanded(expanded)
Si expanded es TRUE entonces e muestra el control hijo.
En la mayora de los casos Expander hace automticamente lo que se desea, al revelar y ocultar el control hijo. En
algunos casos la aplicacin puede necesitar la creacin de un control hijo en el momento de la expancin. Se puede usar
la seal "notify::expanded" para seguir los cambios en el estado de tringulo expansor. El manejador de la seal puede
entonces crear o modificar el control hijo segn se necesite.
El programa de ejemplo expander.py muestra el uso de Expander. Figura 16.11, Control de Expansin ilustra la
ejecucin del programa:
Figura 16.11. Control de Expansin
El programa crea una etiqueta Label que contiene la hora actual y la muestra cuando se expande el expansor.
16.6. Selecciones de Archivos mediante el uso de Controles basados en el Selector de Archivos FileChooser
El nuevo modo de seleccionar archivos en PyGTK 2.4 es el uso de variantes del control FileChooser. Los dos
objetos que implementan esta nueva interfaz en PyGTK 2.4 son el control de Seleccin de Archivo
FileChooserWidget y el dilogo de Seleccin de Archivo FileChooserDialog. Este ltimo es el dilogo
completo con la ventana y botones fcilmente definidos. El primero es un control til para embeber en otro control.
Tanto el FileChooserWidget como el FileChooserDialog poseen los medios necesarios para navegar por el
rbol del sistema de archivos y seleccionar ficheros. El aspecto de los controles depende de la accin utilizada para abrir
el control.
Para crear un nuevo dilogo de seleccin de archivos y seleccionar un archivo existente (como en la opcin de una
aplicacin tpica Archivo Abrir ), se usa:
chooser =
gtk.FileChooserDialog(title=None,action=gtk.FILE_CHOOSER_ACTION_OPEN,
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
260
Para crear un nuevo dilogo de seleccin de archivos para seleccionar un nuevo nombre de archivo (como en la opcin
de una aplicacin tpica Archivo Guardar ), se usa:
chooser =
gtk.FileChooserDialog(title=None,action=gtk.FILE_CHOOSER_ACTION_SAVE,
buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
En los ejemplos anteriores, los dos botones (los elementos de serie Cancelar y Abrir) se crean y conectan a sus
respuestas respectivas (las respuestas estndar Cancelar y OK).
Para fijar la carpeta que se mostrar en el selector de archivos se usa el mtodo:
chooser.set_current_folder(pathname)
Para establecer el nombre de archivo sugerido, tal como lo introducira un usuario (la tpica situacin Archivo
Guardar como ), se usa el mtodo:
chooser.set_current_name(name)
El mtodo anterior no necesita que exista el nombre de archivo. Si se quiere seleccionar un archivo existente concreto
(tal como en la situacin Archivo Abrir ), se debe usar el mtodo:
chooser.set_filename(filename)
Para obtener el nombre que ha seleccionado la usuaria o sobre la que ha hecho clic se usa el mtodo:
filename = chooser.get_filename()
Es posible permitir selecciones mltiples (nicamente para la accin gtk.FILE_CHOOSER_ACTION_OPEN)
utilizando el mtodo:
chooser.set_select_multiple(select_multiple)
donde select_mutiple debe ser TRUE para permitir selecciones mltiples. En este caso, se necesitar utilizar el
mtodo siguiente para obtener una lista de los nombres de archivo seleccionados:
filenames = chooser.get_filenames()
Una caracterstica importante de todos los selectores de archivos es la capacidad de aadir filtros de seleccin de
archivos. El filtro se aade con el mtodo:
chooser.add_filter(filter)
En el ejemplo anterior, filter debe ser una instancia de la clase FileFilter.
El panel izquierdo del selector de archivos da una lista de atajos a algunas carpetas tales como Inicio, Filesystem,
CDROM, etc. Es posible aadir una carpeta a la lista de estos atajos y eliminarla de ella con los siguientes mtodos:
chooser.add_shortcut_folder(folder)
chooser.remove_shortcut_folder(folder)
donde folder es la ruta de la carpeta. El programa de ejemplo filechooser.py ilustra el uso del control de seleccin de
archivos. Figura 16.12, Ejemplo de Seleccin de Archivos muestra el resultado de la ejecucin:
Figura 16.12. Ejemplo de Seleccin de Archivos
261
262
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
filter.add_mime_type("image/png")
filter.add_mime_type("image/jpeg")
filter.add_mime_type("image/gif")
filter.add_pattern("*.png")
filter.add_pattern("*.jpg")
filter.add_pattern("*.gif")
filter.add_pattern("*.tif")
filter.add_pattern("*.xpm")
dialog.add_filter(filter)
response = dialog.run()
if response == gtk.RESPONSE_OK:
print dialog.get_filename(), 'selected'
elif response == gtk.RESPONSE_CANCEL:
print 'Closed, no files selected'
dialog.destroy()
263
accelgroup = uimanager.get_accel_group()
El grupo de atajos (AccelGroup) debera aadirse a la ventana de nivel superior de la aplicacin para que las usuarias
de la aplicacin puedan usar los atajos de la accin (Action). Por ejemplo:
window = gtk.Window()
...
uimanager = gtk.UIManager()
accelgroup = uimanager.get_accel_group()
window.add_accel_group(accelgroup)
16.7.3. Adicin y Eliminacin de Grupos de Acciones (ActionGroups)
Tal como se describe en Seccin 16.1.2, Grupos de Acciones (ActionGroups), los grupos de acciones
ActionGroups pueden llenarse con acciones (Actions) utilizando los mtodos auxiliares add_actions(),
add_toggle_actions() y add_radio_actions(). Los grupos de acciones (ActionGroup) se pueden usar
desde un gestor UIManager una vez que han sido aadidos a su lista de grupo de acciones (ActionGroup) con el
mtodo:
uimanager.insert_action_group(action_group, pos)
donde pos es el ndice de la posicin en la que debera insertarse action_group. Un gestor de interfaces
UIManager puede contener varios grupos de acciones (ActionGroups) con nombres de acciones (Action)
repetidos. Por ello es importante el orden de los objetos ActionGroup, puesto que la bsqueda de una accin
(Action) finaliza cuando se encuentra la primera accin (Action) con el nombre dado. Ello implica que las acciones
que estn en objetos ActionGroup que estn situados antes ocultan a los que estn colocados despus.
Las acciones referenciadas en una descripcin XML de la interfaz de usuario deben aadirse al UIManager antes que
la propia descripcin.
Se puede eliminar un grupo de acciones ActionGroup de un gestor de interfaz de usuario UIManager con el
mtodo:
uimanager.remove_action_group(action_group)
Se puede obtener la lista de los objetos ActionGroup asociados con un determinado UIManager con el mtodo:
actiongrouplist = uimanager.get_action_groups()
16.7.4. Descripciones de la Interfaz de Usuario
Las descripciones de la Interfaz de Usuario aceptadas por UIManager son simples definiciones XML con los
siguientes elementos:
ui
El elemento raz de una descripcin de una Interfaz de Usuario. Se puede omitir. Puede contener elementos
menubar, popup, toolbar y accelerator.
menubar
Elemento de nivel superior que describe una estructura de barra de men (MenuBar) y puede contener
elementos MenuItem, separator, placeholder y menu. Tiene un atributo opcional name (nombre) que, si
se omite, toma el valor "menubar".
popup
Elemento de nivel superior que describe una estructura de men emergente (Menu) y que puede contener
elementos menuitem, separator, placeholder, y menu. Tiene un atributo opcional name (nombre) que, si
se omite, toma el valor "popup".
toolbar
Elemento de nivel superior que describe una estructura de barra de herramientas (Toolbar) y que puede
contener otros elementos toolitem, separator y placeholder. Posee un atributo opcional name (nombre)
que, si se omite, toma el valor "toolbar".
264
Elemento que identifica una posicin dentro de un menubar, toolbar, popup o menu. Este elemento
puede contener otros elementos menuitem, separator, placeholder, y menu. Los elementos Placeholder
placeholder se usan al incorporar descripciones de Interfaz de Usuario para permitir, por ejemplo, la construccin de un
men a partir de descripciones de Interfaz de usuario utilizando nombres de contenedores placeholder
compartidos. Posee un atributo opcional name (nombre), que, si se omite, toma el valor "placeholder".
menu
Elemento que describe una estructura de men Menu y puede contener otros elementos menuitem,
separator, placeholder y menu. Un elemento menu tiene un atributo obligatorio action que denomina un
objeto de accin Action que se usar en la creacin del Menu. Opcionalmente puede incluir los atributos
name (nombre) y position (posicin). Si no se especifica name se usa el valor correspondiente al elemento
action como nombre. El atributo position puede tomar el valor "top" (superior) o "bottom" (inferior),
usndose ste ltimo si no se especifica position.
menuitem
Elemento que escribe un elemento de men MenuItem. El elemento menuitem posee un atributo
obligatorio action que denomina un objeto de accin Action que se usa para crear el elemento de men
MenuItem. Tambin posee los atributos optativos name (nombre) y position (posicin). Si no se indica
name entonces se usa el nombre correspondiente al elemento action. El atributo position puede tomar el
valor "top" (superior) o el valor "bottom" (inferior), usndose ste ltimo si no se especifica position.
toolitem
Elemento que describe un elemento de barra de herramientas ToolItem. El elemento toolitem posee un
atributo obligatorio action que denomina un objeto de accin Action que se usa para crear la barra de
herramientas Toolbar. Tambin posse los atributos optativos name (nombre) y position (posicin). Si no
se indica name entonces se usa el nombre correspondiente al elemento action. El atributo position puede
tomar el valor "top" (superior) o el valor "bottom" (inferior), usndose ste ltimo si no se especifica
position.
separator
Elemento que describe un atajo de teclado (acelerador). El elemento accelerator posee un atributo
obligatorio action que denomina un objeto de accin Action que define la combinacin de teclas del
accelerator
atajo y que se activa con el atajo. Tambin tiene un atributo opcional de nombre name. Si no se especifica
name se usa el nombre de action como nombre.
Como ejemplo, una descripcin de Interfaz de Usuario que se podra usar para la creacin de una interfaz similar a la de
Figura 16.4, Ejemplo de ActionGroup es:
<ui>
<menubar name="MenuBar">
<menu action="File">
<menuitem action="Quit"/>
</menu>
<menu action="Sound">
<menuitem action="Mute"/>
</menu>
<menu action="RadioBand">
<menuitem action="AM"/>
<menuitem action="FM"/>
<menuitem action="SSB"/>
</menu>
</menubar>
<toolbar name="Toolbar">
<toolitem action="Quit"/>
<separator/>
<toolitem action="Mute"/>
<separator name="sep1"/>
<placeholder name="RadioBandItems">
<toolitem action="AM"/>
<toolitem action="FM"/>
<toolitem action="SSB"/>
</placeholder>
</toolbar>
265
</ui>
Obsrvese que esta descripcin simplemente usa los nombres de los elementos action como nombres de la mayora de
los elementos, en lugar de especificar sus atributos name. Tambin es desaconsejable el uso del elemento ui puesto que
parece innecesario.
La jerarqua de objetos que se crea al usar una descripcin de Interfaz de Usuario es muy parecida a la jerarqua de
elementos XML exceptuando que los elementos contenedores placeholder se introducen en sus elementos padre.
Se puede acceder a un control de la jerarqua creada por una descripcin de Interfaz de Usuario mediante su ruta, que se
compone del nombre del elemento de control y sus elementos anteriores separados por barras inclinadas ("/"). Por
ejemplo, si se usa la descripcin anterior, estas seran rutas vlidas de algunos controles:
/MenuBar
/MenuBar/File/Quit
/MenuBar/RadioBand/SSB
/Toolbar/Mute
/Toolbar/RadioBandItems/FM
Hay que observar que el nombre de los contenedores placeholder deben incluirse en la ruta. Generalmente simplemente
se acceder a los controles de nivel superior (por ejemplo, "/MenuBar" y "/Toolbar") pero es posible que sea necesario
acceder a otros controles de nivel inferior para, por ejemplo, cambiar una propiedad.
16.7.5. Adicin y Eliminacin de Descripciones de Interfaz de Usuario
Una vez que se configura un gestor de Interfaz de Usuario UIManager con un grupo de acciones ActionGroup
entonces es posible aadir una descripcin de Interfaz de Usuario e integrarla con la interfaz existente mediante los
siguientes mtodos:
merge_id = uimanager.add_ui_from_string(buffer)
merge_id = uimanager.add_ui_from_file(filename)
donde buffer es una cadena que contiene una descripcin de Interfaz de Usuario y filename es el archivo que
contiene una descripcin de Interfaz de Usuario. Ambos mtodos devuelven un identificador merge_id que consiste
en un valor entero nico. Si falla el mtodo se emite la excepcin GError. El identificador merge_id puede usarse
para eliminar la descripcin de Interfaz de Usuario del gestor UIManager con el mtodo:
uimanager.remove_ui(merge_id)
Los mismos mtodos se pueden usar ms de una vez para aadir descripciones adicionales, que se incorporarn para
obtener una descripcin XML de Interfaz de Usuario combinada. Se hablar ms de las Interfaces de Usuario
combinadas de forma ms detallada en la seccin Seccin 16.7.8, Combinacin de Descripciones de Interfaz de
Usuario.
Se puede aadir un elemento sencillo de Interfaz de Usuario a la descripcin existente con el mtodo:
uimanager.add_ui(merge_id, path, name, action, type, top)
donde merge_id es un valor entero nico, path es la ruta donde se aadir el nuevo elemento, action es el nombre
de una accin Action o None para aadir un elemento separator, type es el tipo de elemento que se ha de aadir y
top (superior) es un valor booleano. Si top es TRUE el elemento se aadir antes que sus elementos hermanos, y
despus en caso contrario.
merge_id se obtendra con el mtodo:
merge_id = uimanager.new_merge_id()
266
Los valores enteros devueltos por el mtodo new_merge_id() son montonamente crecientes.
path (ruta) es una cadena compuesta por el nombre del elemento y los nombres de sus ancestros unidos por una barra
inclinada ("/") pero sin incluir el nudo optativo raz "/ui". Por ejemplo, "/MenuBar/RadioBand" es la ruta del elemento
menu llamado "RadioBand" de la siguiente descripcin de Interfaz de Usuario:
<menubar name="MenuBar">
<menu action="RadioBand">
</menu>
</menubar>
El valor de type (tipo) debe ser uno de los siguientes:
gtk.UI_MANAGER_AUTO
gtk.UI_MANAGER_MENUBAR
gtk.UI_MANAGER_MENU
Un men.
gtk.UI_MANAGER_TOOLBAR
Un men emergente.
gtk.UI_MANAGER_MENUITEM
Un elemento de men.
gtk.UI_MANAGER_TOOLITEM
gtk.UI_MANAGER_SEPARATOR
Un separador.
267
<menuitem action="FM"/>
<menuitem action="SSB"/>
</menu>
</menubar>
<toolbar name="Toolbar">
<toolitem action="Quit"/>
<separator/>
<toolitem action="Mute"/>
<separator name="sep1"/>
<placeholder name="RadioBandItems">
<toolitem action="AM"/>
<toolitem action="FM"/>
<toolitem action="SSB"/>
</placeholder>
</toolbar>
que haya sido aadida al gestor UIManager uimanager, es posible acceder a la barra de mens (MenuBar) y barra
de herramientas (Toolbar) para su uso en una ventana de aplicacin (Window) utilizando el cdigo que sigue:
window = gtk.Window()
vbox = gtk.VBox()
menubar = uimanager.get_widget('/MenuBar')
toolbar = uimanager.get_widget('/Toolbar')
vbox.pack_start(meunbar, False)
vbox.pack_start(toolbar, False)
De la misma forma, se accede a los controles de los niveles inferiores mediante sus rutas. Por ejemplo, se accede al
elemento de la clase RadioToolButton llamado "SSB" as:
ssb = uimanager.get_widget('/Toolbar/RadioBandItems/SSB')
Para facilitar las cosas, se pueden obtener todos los controles de nivel superior de un determinado tipo mediante el
mtodo:
toplevels = uimanager.get_toplevels(type)
donde type especifica el tipo de los controles que se devolvern usando una combinacin de las banderas:
gtk.UI_MANAGER_MENUBAR, gtk.UI_MANAGER_TOOLBAR y gtk.UI_MANAGER_POPUP. Se puede usar el
mtodo gtk.Widget.get_name() para determinar qu control de nivel superior se tiene.
Se puede obtener la accin Action que usa el control auxiliar asociado con un elemento de Interfaz de Usuario usando
el mtodo:
action = uimanager_get_action(path)
donde path (ruta) es una cadena que contiene la ruta a un elemento de la Interfaz de Usuario de uimanager. Si el
elemento no posee una accin Action asociada entonces se devuelve None.
16.7.7. Ejemplo sencillo de Gestor de Interfaz UIManager
uimanager.py es un ejemplo sencillo de programa que ilustra el uso de un gestor de interfaz de usuario UIManager.
Figura 16.13, Programa sencillo de Gestor de Interfaz de Usuario UIManager muestra el programa en funcionamiento.
Figura 16.13. Programa sencillo de Gestor de Interfaz de Usuario UIManager
268
El programa de ejemplo uimanager.py usa la descripcin XML de Seccin 16.7.6, Acceso a los Controles de la Interfaz
de Usuario. El texto de las dos etiquetas se cambian como respuesta a la activacin de la accin biestado
(ToggleAction) "Mute" y de las acciones de exclusin mtua (RadioAction) "AM", "FM" and "SSB". Todas las
acciones estn contenidas en un nico grupo de acciones (ActionGroup) que permite que se pueda conmutar la
sensibilidad y visibilidad de todos los controles auxiliares de las acciones mediante los botones biestado "Sensitive" y
"Visible". El uso del elemento contenedor placeholder se describe posteriormente en Seccin 16.7.8, Combinacin de
Descripciones de Interfaz de Usuario.
16.7.8. Combinacin de Descripciones de Interfaz de Usuario
La combinacin de descripciones de Interfaz de Usuario se hace en funcin del nombre de los elementos XML. Tal
como se indic ms arriba, los elementos individuales de la jerarqua pueden ser accedidos usando un nombre de ruta
que est formado por el nombre del elemento y los nombres de sus ancestros. Por ejemplo, si usamos la descripcin de
Interfaz de Usuario de Seccin 16.7.4, Descripciones de la Interfaz de Usuario, el elemento toolitem "AM" tiene la
ruta "/Toolbar/RadioBandItems/AM" mientras que el elemento menuitem "FM" tiene la ruta
"/MenuBar/RadioBand/FM".
Si se combina una descripcin de Interfaz de Usuario con esa descripcin de Interfaz entonces sus elementos se aaden
como elementos del mismo nivel que los elementos existentes. Por ejemplo, si la descripcin de Interfaz de Usuario:
<menubar name="MenuBar">
<menu action="File">
<menuitem action="Save" position="top"/>
<menuitem action="New" position="top"/>
</menu>
<menu action="Sound">
<menuitem action="Loudness"/>
</menu>
<menu action="RadioBand">
<menuitem action="CB"/>
<menuitem action="Shortwave"/>
</menu>
</menubar>
<toolbar name="Toolbar">
<toolitem action="Save" position="top"/>
<toolitem action="New" position="top"/>
<separator/>
<toolitem action="Loudness"/>
<separator/>
<placeholder name="RadioBandItems">
<toolitem action="CB"/>
<toolitem action="Shortwave"/>
</placeholder>
</toolbar>
se aade a nuestra descripcin de Interfaz de Usuario de ejemplo:
269
<menubar name="MenuBar">
<menu action="File">
<menuitem action="Quit"/>
</menu>
<menu action="Sound">
<menuitem action="Mute"/>
</menu>
<menu action="RadioBand">
<menuitem action="AM"/>
<menuitem action="FM"/>
<menuitem action="SSB"/>
</menu>
</menubar>
<toolbar name="Toolbar">
<toolitem action="Quit"/>
<separator/>
<toolitem action="Mute"/>
<separator name="sep1"/>
<placeholder name="RadioBandItems">
<toolitem action="AM"/>
<toolitem action="FM"/>
<toolitem action="SSB"/>
</placeholder>
</toolbar>
se creara la siguiente descripcin de Interfaz de Usuario combinada:
<menubar name="MenuBar">
<menu name="File" action="File">
<menuitem name="New" action="New"/>
<menuitem name="Save" action="Save"/>
<menuitem name="Quit" action="Quit"/>
</menu>
<menu name="Sound" action="Sound">
<menuitem name="Mute" action="Mute"/>
<menuitem name="Loudness" action="Loudness"/>
</menu>
<menu name="RadioBand" action="RadioBand">
<menuitem name="AM" action="AM"/>
<menuitem name="FM" action="FM"/>
<menuitem name="SSB" action="SSB"/>
<menuitem name="CB" action="CB"/>
<menuitem name="Shortwave" action="Shortwave"/>
</menu>
</menubar>
<toolbar name="Toolbar">
<toolitem name="New" action="New"/>
<toolitem name="Save" action="Save"/>
<toolitem name="Quit" action="Quit"/>
<separator/>
<toolitem name="Mute" action="Mute"/>
<separator name="sep1"/>
<placeholder name="RadioBandItems">
<toolitem name="AM" action="AM"/>
<toolitem name="FM" action="FM"/>
<toolitem name="SSB" action="SSB"/>
<toolitem name="CB" action="CB"/>
<toolitem name="Shortwave" action="Shortwave"/>
</placeholder>
<separator/>
270
Los controles de botn biestado (ToggleButton) "Sensitive" y Visible" controlan la sensibilidad y visibilidad de
nicamente el segundo grupo de acciones (ActionGroup).
16.7.9. Seales de UIManager
271
UIManager posee una par de seales interesantes a las que se puede conectar una aplicacin. La seal "actionschanged" se emite cuando se aade o elimina un grupo de acciones ActionGroup de un gestor UIManager. La
signatura de la retrollamada es:
def callback(uimanager, ...)
La seal "add-widget" se emite cuando se crea un control auxiliar MenuBar o Toolbar. La signatura de la
retrollamada es:
def callback(uimanager, widget, ...)
donde widget es el control recin creado.
Captulo 17. Controles sin documentar
Tabla de contenidos
17.1. Etiqueta de Aceleracin (Atajo)
17.2. Men de Opciones
17.3. Elementos de Men
17.3.1. Elemento de Men de Activacin
17.3.2. Elemento de Men de Exclusin Mtua
17.3.3. Elemento de Men de Separacin
17.3.4. Elemento de Men de Cascada
17.4. Curvas
17.5. Dilogo de Mensaje
17.6. Curva Gamma
Todos estos controles necesitan voluntarios! :) Por favor, considere contribuir a mejorar este tutorial.
Si se necesita usar alguno de estos controles que estn sin documentar, es muy recomendable echar un vistazo a los
archivos *.c de la distribucin de PyGTK. Los nombres de los mtodos de PyGTK son muy descriptivos y, una vez que
se comprende cmo funcionan las cosas, no es muy difcil averiguar como usar un control simplemente echando un
vistazo a la definicin de sus mtodos. Esto, junto con la lectura de algunos ejemplos del cdigo de otros, debera bastar
para no encontrar mayor problema.
En cuanto realmente se entiendan todos los mtodos de un control nuevo, que todava se encuentra sin documentar, por
favor, piense en escribir un fragmento de tutorial sobre l para que otros puedan beneficiarse del tiempo invertido.
17.1. Etiqueta de Aceleracin (Atajo)
17.2. Men de Opciones
17.3. Elementos de Men
17.3.1. Elemento de Men de Activacin
17.3.2. Elemento de Men de Exclusin Mtua
17.3.3. Elemento de Men de Separacin
17.3.4. Elemento de Men de Cascada
17.4. Curvas
17.5. Dilogo de Mensaje
272
cambia la sensibilidad del control (es decir, si reacciona a eventos). Si sensitive es TRUE (verdadero) el control
recibir eventos; si es FALSE (falso) el control no recibir eventos. Un control que est insensible se visualiza
normalmente en un tono gris.
El mtodo:
widget.set_size_request(width, height)
establece el tamao del control de forma que tenga el ancho dado por el parmetro width y la altura dada por el
parmetro height.
18.1. Mtodos de Banderas de los Controles
Los mtodos:
widget.set_flags(flags)
widget.unset_flags(flags)
flags = widget.flags()
ponen, quitan y leen las banderas de los objetos gtk.Object y gtk.Widget . Las flags (banderas) pueden ser
cualquiera de las banderas estndar:
IN_DESTRUCTION
FLOATING
RESERVED_1
RESERVED_2
#
#
#
#
en destruccin
flotannte
reservada 1
reservada 2
273
TOPLEVEL
NO_WINDOW
REALIZED
MAPPED
VISIBLE
SENSITIVE
PARENT_SENSITIVE
CAN_FOCUS
HAS_FOCUS
CAN_DEFAULT
HAS_DEFAULT
HAS_GRAB
RC_STYLE
COMPOSITE_CHILD
NO_REPARENT
APP_PAINTABLE
RECEIVES_DEFAULT
DOUBLE_BUFFERED
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
#
de nivel superior
sin ventana
realizado
mapeado
visible
sensible
padre sensible
puede recibir el foco
tiene el foco
puede ser el control predeterminado
es el control predeterminado
tiene la exclusividad de los eventos
estilo rc
hijo compuesto
no reparentado
aplicacin pintable
recibe predeterminado
tiene doble buffer
El mtodo:
widget.grab_focus()
permite a un control adquirir el foco en caso de que tenga la bandera CAN_FOCUS activada.
18.2. Mtodos de Visualizacin de Controles
Los mtodos:
widget.show()
widget.show_all()
widget.hide()
widget.hide_all()
widget.realize()
widget.unrealize()
widget.map()
widget.unmap()
274
El mtodo realize() ("realizar") hace que se reserven los recursos que necesita el control, incluida su ventana.
El mtodo unrealize() ("desrealizar") libera la ventana del control y otros recursos asociados al mismo.
"Desrealizar" un control tambin lo oculta y "desmapea".
El mtodo map() ("mapear") hace que se reserve espacio en el display (pantalla) para el control; sto slo se aplica a los
controles que necesitan ser manipulados por el gestor de ventanas. Mapear un control tambin lo realiza si es necesario.
El mtodo unmap() (desmapear) elimina un control del display (pantalla) y tambin lo oculta si es necesario.
18.3. Atajos de Teclado de los Controles
Los siguientes mtodos:
widget.add_accelerator(accel_signal, accel_group, accel_key, accel_mods,
accel_flags)
widget.remove_accelerator(accel_group, accel_key, accel_mods)
aaden y eliminan atajos de teclado a un Grupo de Atajos de Teclado (gtk.AcceleratorGroup) que, asociado al
control de nivel ms alto de un conjunto de ellos, hace que se gestionen los atajos de teclado de stos.
El parmetro accel_signal es una seal que puede emitir el control .
El parmetro accel_key es la tecla que se usar como atajo de teclado.
El parmetro accel_mods es un grupo de modificadores que se aaden a la tecla accel_key (por ejemplo Shift
(maysculas), Control, etc.):
SHIFT_MASK
LOCK_MASK
CONTROL_MASK
MOD1_MASK
MOD2_MASK
MOD3_MASK
MOD4_MASK
MOD5_MASK
BUTTON1_MASK
BUTTON2_MASK
BUTTON3_MASK
BUTTON4_MASK
BUTTON5_MASK
RELEASE_MASK
El parmetro accel_flags es un conjunto de opciones sobre cmo se muestra la informacin del atajo de teclado.
Los valores posibles son:
ACCEL_VISIBLE
ACCEL_LOCKED
275
El grupo accel_group se aade a una ventana de nivel superior con el siguiente mtodo:
window.add_accel_group(accel_group)
El parmetro name es la cadena de caracteres que se asocia al control widget. Es til para definir estilos usados en
controles particulares de una aplicacin. El nombre del control puede usarse para limitar la aplicacin del estilo en vez
de usar la clase del control. En el captulo cmo usar los ficheros rc de GTK+ se profundiza ms en el uso de estilos de
esta manera.
18.5. Estilo de los Controles
Los siguientes mtodos cambian y leen el estilo asociado a un control:
widget.set_style(style)
style = widget.get_style()
La siguiente funcin:
style = get_default_style()
#
#
#
#
#
El
El
El
El
El
276
mid
set_style()
text
base
text_aa
black
white
font_desc
# el color negro
# el color blanco
# la descripcin de fuente pango predeterminada
xthickness
ythickness
#
#
fg_gc
# una
mtodo set_style()
bg_gc
# una
set_style()
light_gc
# una
mtodo set_style()
dark_gc
# una
mtodo set_style()
mid_gc
# una
mtodo set_style()
text_gc
# una
set_style()
base_gc
# una
set_style()
black_gc
# una
set_style()
white_gc
# una
mtodo set_style()
bg_pixmap
que copia los atributos del estilo salvo las listas de contextos grficos y las listas de colores light, dark y mid (claros,
oscuros y medios).
El estilo actual se puede obtener con:
style = widget.get_style()
Para cambiar el estilo de un control (por ejemplo, para cambiar el color de primer plano), se deben usar los siguientes
mtodos de un control:
widget.modify_fg(state, color) #
widget.modify_bg(state, color) #
widget.modify_text(state, color)
widget.modify_base(state, color)
277
Cuando se establece el estilo style se reservan los colores del estilo y se crean los contextos grficos. La mayora de
los controles se redibujan tambin automticamente tras cambiar de estilo. Si el estilo style es None entonces el
control regresar al estilo predeterminado.
No todos los cambios del estilo afectan al control. Por ejemplo, el cambio del color de fondo de una etiqueta Label no
tendr efecto dado que el control Label no tiene una ventana propia gtk.gdk.Window. El color de fondo de una
Etiqueta depende del color de fondo de su control padre. Pero es posible meter la Etiqueta en un control EventBox que
aade una ventana y permite as cambiar su color de fondo. En la seccin EventBox se proporciona un ejemplo de esta
tcnica.
Captulo 19. Temporizadores, Entrada/Salida y Funciones de Inactividad
Tabla de contenidos
19.1. Temporizadores
19.2. Monitorizar la Entrada/Salida
19.3. Funciones de Inactividad
19.1. Temporizadores
Puede que estes preguntndote cmo hacer que GTK haga algo til mientras est dentro de la funcin main(). Bien,
tienes varias opciones. Usando la siguiente funcin puedes crear un temporizador que ser llamado en intervalos
regulares (en milisegundos).
source_id = gobject.timeout_add(interval, function, ...)
Tambin se puede parar el temporizador devolviendo cero o FALSE (falso) desde tu funcin. Obviamente esto significa
que si quieres que el temporizador se siga llamando, debes devolver un valor distinto de cero, como TRUE (verdadero).
Tu funcin ser algo parecido a:
def timeout_callback(...):
El nmero de argumentos a tu funcin debe coincidir con el nmero de argumentos de datos especificados en
timeout_add().
19.2. Monitorizar la Entrada/Salida
Puedes comprobar si hay algo para leer o escribir a un fichero (bien a un fichero Python o a un fichero de ms bajo nivel
del Sistema Operativo) y automticamente invocar una funcin. sto es especialmente til para aplicaciones de red. La
funcin:
source_id = gobject.io_add_watch(source, condition, callback)
278
donde el primer argumento (source) es el fichero abierto (objeto de fichero de Python o descriptor de fichero de ms
bajo nivel) que quieres monitorizar. La funcin gobject.io_add_watch() usa internamente el descriptor de fichero
de bajo nivel, pero la funcin lo podr extraer usando el mtodo fileno() cuando sea necesario. El segundo argumento
(condition) especifica qu es lo que se quiere monitorizar. Puede ser cualquiera de:
gobject.IO_IN - Llama a tu funcin cuando en el fichero hay datos disponibles
para leer.
gobject.IO_OUT - Llama a tu funcin cuando el fichero est listo para
escritura.
gobject.IO_PRI - Llama a tu funcin cuando el fichero tiene datos urgentes
para leer.
gobject.IO_ERR - Llama a tu funcin cuando se da una condicin de error.
gobject.IO_HUP - Llama a tu funcin cuando se ha producido un "cuelgue" (se ha
roto la conexin, de uso para pipes y sockets generalmente).
Estas constantes se definen en el mdulo gobject module. Supongo que ya te has dado cuenta que el tercer argumento,
callback, es la funcin que quieres que se llame cuando las condiciones anteriores se cumplen.
El valor de retorno, source_id se puede usar para parar de monitorizar el fichero usando la siguiente funcin:
gobject.source_remove(source_id)
donde source y condition son los que especificaste antes. El valor de source ser el descriptor de fichero de bajo
nivel y no el objeto fichero Python (es decir, el valor que devuelve el mtodo de fichero de Python fileno()).
Tambin se puede parar la monitorizacin devolviendo cero o FALSE (falso) desde tu funcin. Obviamente esto
significa que si quieres que el monitorizacin se siga llamando, debes devolver un valor distinto de cero, como TRUE
(verdadero).
19.3. Funciones de Inactividad
Qu pasa si quieres que se llame a una funcin cuando no est pasando nada? Usa la funcin:
source_id = gobject.idle_add(callback, ...)
Cualquier argumento tras el primero (indicados con ...) se pasan a la funcin callback en orden. El valor de retorno
source_id se utiliza como una referencia al manejador.
Esta funcin hace que GTK llame a la funcin especificada cuando no est pasando nada ms.
Y la funcin ha ser llamada debe ser parecida a:
def callback(...):
279
donde los argumentos pasados a callback son los mismos especificados en la funcin gobject.idle_add(). Al
igual que en otras funciones, devolviendo FALSE (falso) dejar de ser llamada de nuevo, y devolviendo TRUE
(verdadero) se la seguir llamando la prxima ocasin que haya un tiempo de inactividad.
Se puede eliminar una funcin de inactividad de la cola llamando la funcin siguiente:
gobject.source_remove(source_id)
Los primeros cuatro mtodos conectan un manejador de seales (cb) a un objeto gtk.Object (object) para la seal
especificada por name, y devuelven un valor handler_id que identifica la conexin. cb_args son cero o ms
argumentos que sern pasados al final de la llamada al manejador cb. Los mtodos connect_after() y
connect_object_after() harn que se llame a sus manejadores despus de haber llamado a todos los dems
manejadores (incluyendo los manejadores predeterminados) que estn conectados a ese mismo objecto y seal. Cada
manejador de seales de un objeto tiene su propio conjunto de argumentos. Resulta til consultar la documentacin de
GTK+ para averiguar qu argumentos se deben usar en cada manejador de seales, aunque se proporciona informacin
sobre los controles ms comunes en el apndice Seales de GTK+. El manejador de seales genrico es similar a este:
def signal_handler(object, ...., cb_args):
Los manejadores de seales que se definen como mtodos de una clase Python (especificados en los mtodos
connect() como self.cb) tendrn un argumento adicional como primer argumento, la instancia del objeto self:
signal_handler(self, object, ...., cb_args)
280
emiten y paran, respectivamente, la seal especificada en el argumento name. La emisin de la seal hace que se
ejecuten el manejador predeterminado y los definidos por el usuario. El mtodo emit_stop_by_name() abortar la
emisin de seales actual.
20.2. Emisin y Propagacin de Seales
La emisin de seales es el proceso por el cual GTK+ ejecuta todos los manejadores de una seal y un objeto
especficos.
Debe tenerse en cuenta en primer lugar que el valor de retorno de una emisin de seal es el valor de retorno del ltimo
manejador ejecutado. Como las seales de eventos son todas del tipo RUN_LAST, este valor ser el del manejador
predeterminado (dado por GTK+) a menos que se use el mtodo connect_after().
La forma en la que se trata un evento (por ejemplo "button_press_event") es:
281
El valor de retorno de tu manejador no tendr efecto si hay un manejador predeterminado, a menos que lo
conectes con connect_after().
Para evitar que se llame al manejador predeterminado, tienes que usar el mtodo connect() y utilizar
emit_stop_by_name() - el valor de retorno slo afecta a si la seal se propaga, pero no a la emisin actual.
Que convierte la seleccin selection en el formato especificado por el objetivo target. La seleccin selection
es un tomo correspondiente al tipo de seleccin. Las selecciones comunes son las cadenas de texto:
PRIMARY
# primaria
SECONDARY
# secundaria
Si es posible, el campo tiempo time debera ser el momento en el que se produjo el evento que ocasion la seleccin.
Esto ayuda a cerciorarse que los eventos ocurren en el orden en el que el usuario los solicita. Sin embargo, si no est
disponible (por ejemplo, si la conversin se produjo en una seal "clicked"), entonces se puede utilizar 0, que significa el
momento actual. El resultado ser TRUE (verdadero) si la conversin tuvo xito, o FALSE (falso) en caso contrario.
Cuando el dueo de la seleccin responde a la peticin, una seal "selection_received" (seleccin_recibida) se envia a la
aplicacin. El manejador de esta seal recibe un objeto de tipo gtk.SelectionData, que tiene los siguientes
atributos:
282
selection
target
type
format
data
#
#
#
#
#
seleccin
objetivo
tipo
formato
datos
283
SUPERSCRIPT_X
SUPERSCRIPT_Y
SUBSCRIPT_X
SUBSCRIPT_Y
UNDERLINE_POSITION
UNDERLINE_THICKNESS
STRIKEOUT_ASCENT
STRIKEOUT_DESCENT
ITALIC_ANGLE
X_HEIGHT
QUAD_WIDTH
WEIGHT
POINT_SIZE
RESOLUTION
COPYRIGHT
NOTICE
FONT_NAME
FAMILY_NAME
FULL_NAME
CAP_HEIGHT
WM_CLASS
WM_TRANSIENT_FOR
CLIPBOARD
format da la longitud de las unidades (por ejemplo caracteres) en bits. Normalmente se puede obviar este parmetro al
recibir datos.
data contiene los datos devueltos en forma de cadena de texto.
PyGTK empaqueta todos los datos recibidos en una cadena de texto. Esto hace que sea fcil manipular objetivos de
cadenas de texto. Para obtener objetivos de otros tipos (como ATOM o INTEGER) el programa debe extraer la
informacin de la cadena de texto devuelta. PyGTK proporciona dos mtodos para obtener texto y una lista de objetivos
a partir de los datos de la seleccin:
text = selection_data.get_text()
targets = selection_data.get_targets()
donde text es una cadena de texto que contiene el texto de la seleccin y targets es una lista de los objetivos que
acepta la seleccin.
Dado un gtk.SelectionData que contiene una lista de objetivos, el mtodo:
has_text = selection_data.targets_include_text()
284
285
48
window.set_title("Get Selection")
49
window.set_border_width(10)
50
window.connect("destroy", lambda w: gtk.main_quit())
51
52
vbox = gtk.VBox(gtk.FALSE, 0)
53
window.add(vbox)
54
vbox.show()
55
56
# Creamos un botn que al pulsarlo se obtiene la cadena de
objetivo
57
button = gtk.Button("Get String Target")
58
eventbox = gtk.EventBox()
59
eventbox.add(button)
60
button.connect_object("clicked", self.get_stringtarget,
eventbox)
61
eventbox.connect("selection_received", self.selection_received)
62
vbox.pack_start(eventbox)
63
eventbox.show()
64
button.show()
65
66
# Creamos un botn que devuelve los objetivos al pulsarlo
67
button = gtk.Button("Get Targets")
68
eventbox = gtk.EventBox()
69
eventbox.add(button)
70
button.connect_object("clicked", self.get_targets, eventbox)
71
eventbox.connect("selection_received", self.selection_received)
72
vbox.pack_start(eventbox)
73
eventbox.show()
74
button.show()
75
76
window.show()
77
78
def main():
79
gtk.main()
80
return 0
81
82
if __name__ == "__main__":
83
GetSelectionExample()
84
main()
Las lineas 30-38 se encargan de la obtencin de los datos de seleccin de "TARGETS" (objetivos) e imprime la lista de
los nombres de los objetivos. Los botones estn insertados en sus propias cajas de eventos puesto que una seleccin debe
estar asociada a una gtk.gdk.Window y los botones son controles sin ventana propia en GTK+2.0.
21.3. Proporcionar la Seleccin
Proporcionar la seleccin es un poco ms complicado. Se deben registrar los manejadores que se llamarn cuando se
solicite la seleccin. Para cada par seleccin-objetivo a manejar, se debe hacer una llamada a:
widget.selection_add_target(selection, target, info)
widget (control), selection (seleccin), y target (objetivo) identifican las peticiones que gestionar este
manejador. Cuando llegue una peticin para una seleccin, se llamar a la seal "selection_get". info es un entero que
se puede usar como identificador para el objetivo especfico dentro de la retrollamada.
La retrollamada tiene la siguiente signatura:
286
El argumento gtk.SelectionData es el mismo que antes, pero en esta ocasin se deben rellenar los campos type,
format y data. (El campo format es importante aqu, ya que el servidor X lo usa para saber si el campo data
necesita que se le cambie el orden de sus bytes o no. Normalmente ser 8 - un caracter - 32 - un entero). Esto se hace
llamando al mtodo:
selection_data.set(type, format, data)
Este mtodo de PyGTK slo puede tratar datos de cadenas de caracteres, por lo que data debe cargarse en una cadena
Python pero format tendr el tamao apropiado para los datos (por ejemplo 32 para tomos y enteros, 8 para cadenas).
Los mdulos Python struct o StringIO pueden servir para convertir datos que no son cadenas de caracteres a
cadenas de caracteres. Por ejemplo, se puede convertir una lista de enteros en una cadena y ponerlos en el campo
selection_data as:
ilist = [1, 2, 3, 4, 5]
data = apply(struct.pack, ['%di'%len(ilist)] + ilist)
selection_data.set("INTEGER", 32, data)
result ser TRUE (verdadero) si el programa reclama la seleccin selection con xito. Si otra aplicacin reclama
la posesin de selection, entonces llegar un evento "selection_clear_event".
Como ejemplo de proporcionar la seleccin, el programa setselection.py aade la funcionalidad de seleccin a un botn
biestado que est dentro de una gtk.EventBox. (Se necesita una gtk.Eventbox porque la seleccin debe asociarse
a una gtk.gdk.Window y un gtk.Button es un control sin ventana en GTK+ 2.0.). Cuando se pulsa el botn
biestado el programa reclama la seleccin primaria. El nico objetivo soportado (aparte de algunos objetivos que
proporciona la propia GTK+ como "TARGETS"), es el objetivo "STRING". Al solicitar este objetivo se devuelve una
representacin en cadena de caracteres de la hora actual. La figura Figura 21.2, Ejemplo de Fijar la Seleccin muestra
la ventana del programa cuando ste ha conseguido la posesin de la seleccin primaria:
Figura 21.2. Ejemplo de Fijar la Seleccin
#!/usr/bin/env python
# ejemplo setselection.py
287
4
5
import pygtk
6
pygtk.require('2.0')
7
import gtk
8
import time
9
10
class SetSelectionExample:
11
# Retrollamada cuando la usuaria conmuta la seleccin
12
def selection_toggled(self, widget, window):
13
if widget.get_active():
14
self.have_selection = window.selection_owner_set("PRIMARY")
15
# si la solicitud de la seleccin falla, se devuelve el
botn al
16
# estado inactivo
17
if not self.have_selection:
18
widget.set_active(gtk.FALSE)
19
else:
20
if self.have_selection:
21
# No es posible "liberar" la seleccin en PyGTK
22
# simplemente se seala que no se posee
23
self.have_selection = gtk.FALSE
24
return
25
26
# Llamada cuando otra aplicacin reclama la seleccin
27
def selection_clear(self, widget, event):
28
self.have_selection = gtk.FALSE
29
widget.set_active(gtk.FALSE)
30
return gtk.TRUE
31
32
# Proporciona la hora actual como seleccin
33
def selection_handle(self, widget, selection_data, info,
time_stamp):
34
current_time = time.time()
35
timestr = time.asctime(time.localtime(current_time))
36
37
# Al devolver una cadena nica no debe terminar en valor nulo
38
# Esto se hace automticamente
39
selection_data.set_text(timestr, len(timestr))
40
return
41
42
def __init__(self):
43
self.have_selection = gtk.FALSE
44
# Creamos la ventana principal
45
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
46
window.set_title("Set Selection")
47
window.set_border_width(10)
48
window.connect("destroy", lambda w: gtk.main_quit())
49
self.window = window
50
# Creamos una caja de eventos que contenga el botn, ya que no
tiene su
51
# propia gtk.gdk.Window
52
eventbox = gtk.EventBox()
53
eventbox.show()
54
window.add(eventbox)
55
56
# Creamos un botn biestado que acte como seleccin
57
selection_button = gtk.ToggleButton("Claim Selection")
58
eventbox.add(selection_button)
59
288
60
selection_button.connect("toggled", self.selection_toggled,
eventbox)
61
eventbox.connect_object("selection_clear_event",
self.selection_clear,
62
selection_button)
63
64
eventbox.selection_add_target("PRIMARY", "STRING", 1)
65
eventbox.selection_add_target("PRIMARY", "COMPOUND_TEXT", 1)
66
eventbox.connect("selection_get", self.selection_handle)
67
selection_button.show()
68
window.show()
69
70
def main():
71
gtk.main()
72
return 0
73
74
if __name__ == "__main__":
75
SetSelectionExample()
76
main()
Se empieza a arrastrar. El control fuente puede recibir la seal "drag-begin". Puede configurar el icono de
arrastrar, etc.
289
Se mueve lo arrastrado sobre el rea de soltar. El control destino puede recibir la seal "drag-motion".
Se suelta el botn. El control destino puede recibir la seal "drag-drop". El control destino debera solicitar los
datos fuente.
Se solicitan los datos de arrastrar (al soltar el botn). El control fuente puede recibir la seal "drag-data-get".
Se reciben los datos (puede ser en la misma o en otra aplicacin). El control destino puede recibir la seal "dragdata-received".
Se borran los datos de arrastrar (si el arrastre fue un movimiento). El control fuente puede recibir la seal "dragdata-delete".
El proceso arrastrar-y-soltar termina. El control fuente puede recibir la seal "drag-end".
Hay algunos pasos intermedios adicionales pero se vern en detalle un poco ms tarde.
22.2. Propiedades de Arrastrar y Soltar
Los datos de arrastrar tienen las siguientes propiedades:
Tipo de accin de arrastrar (por ejemplo ACTION_COPY (accin copiar), ACTION_MOVE (accin mover)).
Tipo de arrastrar-y-soltar especfico del cliente (un par de nombre y nmero).
Tipo de formato de los datos enviados y recibidos.
Las acciones de arrastrar son bastante obvias, especifican si el control puede arrastrar con la/s accin/es especificada/s,
por ejemplo gtk.gdk.ACTION_COPY y/o gtk.gdk.ACTION_MOVE. Una accin gtk.gdk.ACTION_COPY
sera el tpico arrastrar y soltar sin que la fuente se elimine mientras que una accin gtk.gdk.ACTION_MOVE sera
exactamente igual, pero se 'sugiere' que se borren los datos orgen tras la llamada a la seal de recepcin. Hay ms
acciones como gtk.gdk.ACTION_LINK que se pueden investigar en cuanto se adquiera un poco ms de destreza con
el mecanismo de arrastrar-y-soltar.
El tipo de arrastrar-y-soltar especificado por el cliente es mucho ms flexible, porque ser la aplicacin la que lo defina
y compruebe. Se tendrn que configurar los controles destino para que reciban ciertos tipos de arrastrar-y-soltar,
especificando un nombre y/o un nmero. Es ms fiable el uso de un nombre ya que otra aplicacin puede estar usando el
mismo nmero con un significado completamente diferente.
Los tipos de emisin y recepcin de datos (objetivo de seleccin) entran en juego slo en los propios manejadores de
datos solicitados y recibidos. El trmino objetivo de seleccin es un poco confuso. Es un trmino adaptado de la
seleccin GTK+ (cortar/copiar y pegar). Lo que selection target realmente significa es el tipo de formato de datos (por
ejemplo gtk.gdk.Atom, entero, o cadena de caracteres) que se est enviando o recibiendo. El manejador de datos
solicitados tiene que especificar el tipo (selection target) de datos que est enviando y el manejador de datos recibidos
tiene que manejar el tipo de datos recibidos (selection target).
22.3. Mtodos de Arrastrar y Soltar
22.3.1. Configuracin del Control Origen
El mtodo drag_source_set() especifica un conjunto de tipos objetivo para una operacin de arrastrar en un
control.
widget.drag_source_set(start_button_mask, targets, actions)
Los parmetros significan lo siguiente:
290
El manejador de seal "drag-begin" se puede usar para configurar algunas condiciones iniciales tales como el icono de
arrastre usando para ello uno de los siguientes mtodos del la clase Widget: drag_source_set_icon(),
drag_source_set_icon_pixbuf(), drag_source_set_icon_stock(). El manejador de seal "drag-end"
puede usarse para deshacer las acciones del manejador de seal "drag-begin".
El manejador de seal "drag-data-get" debera devolver los datos de arrastre que coincidan con el objetivo especificado
por info. Rellena gtk.gdk.SelectionData con los datos de arrastre.
El manejador de seal "drag-delete" se usa para borrar los datos de arrastre de una accin gtk.gdk.ACTION_MOVE
tras haberlos copiado.
22.3.3. Configuracin de un Control Destino
drag_dest_set() especifica que este control puede ser destino de operaciones arrastrar-y-soltar y define los tipos
que admite.
drag_dest_unset() especifica que el control no puede recibir ms operaciones arrastrar-y-soltar.
widget.drag_dest_set(flags, targets, actions)
widget.drag_dest_unset()
291
flags especifica qu acciones debe realizar GTK+ por parte del control cuando se suelte algo en l. Los valores
posibles son:
gtk.DEST_DEFAULT_MOTION
gtk.DEST_DEFAULT_HIGHLIGHT
gtk.DEST_DEFAULT_DROP
gtk.DEST_DEFAULT_ALL
Si est activo, especifica que todas las acciones anteriores deben ejecutarse.
targets es una lista de tuplas de informacin de objetivos tal y como se describe ms arriba.
actions es una mscara de bits de las posibles acciones que realizar cuando se arrastre sobre este control. Los valores
posibles se pueden componer con la operacin OR y son los siguientes:
gtk.gdk.ACTION_DEFAULT
gtk.gdk.ACTION_COPY
gtk.gdk.ACTION_MOVE
gtk.gdk.ACTION_LINK
gtk.gdk.ACTION_PRIVATE
gtk.gdk.ACTION_ASK
#
#
#
#
#
#
accin
accin
accin
accin
accin
accin
predeterminada
copiar
mover
enlazar
privada
preguntar
#!/usr/local/env python
import pygtk
pygtk.require('2.0')
import gtk
def motion_cb(wid, context, x, y, time):
context.drag_status(gtk.gdk.ACTION_COPY, time)
return True
def drop_cb(wid, context, x, y, time):
l.set_text('\n'.join([str(t) for t in context.targets]))
292
13
14
15
16
17
18
19
20
21
22
23
24
25
return True
w = gtk.Window()
w.set_size_request(200, 150)
w.drag_dest_set(0, [], 0)
w.connect('drag_motion', motion_cb)
w.connect('drag_drop', drop_cb)
w.connect('destroy', lambda w: gtk.main_quit())
l = gtk.Label()
w.add(l)
w.show_all()
gtk.main()
El programa crea una ventana que se configura como destino de arrastre para ningn objetivo y ninguna accin poniendo
las banderas (flags) a cero. Se conectan los manejadores motion_cb() y drop_cb() a las seales "drag-motion" y
"drag-drop" respectivamente. El manejador motion_cb() configura el estado de arrastre para que se permita soltar.
drop_cb() modifica el texto de la etiqueta a una cadena de caracteres que contenga los objetivos de arrastre e ignora
los datos de tal modo que la accin de soltar no se completa en ningn caso..
22.3.4. Seales en el Control Destino
Durante una operacin arrastrar-y-soltar se envian las siguientes seales al control destino.
Tabla 22.2. Seales del Control Destino
drag_motion (movimiento de
arrastre)
drag_data_received (datos
recibidos)
El programa de ejemplo dragndrop.py muestra el uso de arrastrar y soltar en una aplicacin. Un botn con un icono
xpm (en gtkxpm.py) es el origen del arrastre y proporciona tanto texto como datos xpm. Un control de disposicin es
el destino para soltar el xpm mientras que un botn es el destino para soltar el texto. La figura Figura 22.1, Ejemplo de
Arrastrar y Soltar ilustra la ventana del programa tras soltar el xpm en el control de disposicin y el texto en el botn:
Figura 22.1. Ejemplo de Arrastrar y Soltar
293
#!/usr/bin/env python
# ejemplo dragndrop.py
import pygtk
pygtk.require('2.0')
import gtk
import string, time
import gtkxpm
class DragNDropExample:
HEIGHT = 600
WIDTH = 600
TARGET_TYPE_TEXT = 80
TARGET_TYPE_PIXMAP = 81
fromImage = [ ( "text/plain", 0, TARGET_TYPE_TEXT ),
( "image/x-xpixmap", 0, TARGET_TYPE_PIXMAP ) ]
toButton = [ ( "text/plain", 0, TARGET_TYPE_TEXT ) ]
toCanvas = [ ( "image/x-xpixmap", 0, TARGET_TYPE_PIXMAP ) ]
def layout_resize(self, widget, event):
x, y, width, height = widget.get_allocation()
if width > self.lwidth or height > self.lheight:
self.lwidth = max(width, self.lwidth)
self.lheight = max(height, self.lheight)
widget.set_size(self.lwidth, self.lheight)
def makeLayout(self):
self.lwidth = self.WIDTH
self.lheight = self.HEIGHT
box = gtk.VBox(gtk.FALSE,0)
box.show()
table = gtk.Table(2, 2, gtk.FALSE)
table.show()
box.pack_start(table, gtk.TRUE, gtk.TRUE, 0)
294
37
layout = gtk.Layout()
38
self.layout = layout
39
layout.set_size(self.lwidth, self.lheight)
40
layout.connect("size-allocate", self.layout_resize)
41
layout.show()
42
table.attach(layout, 0, 1, 0, 1, gtk.FILL|gtk.EXPAND,
43
gtk.FILL|gtk.EXPAND, 0, 0)
44
# se crean las barras de desplazamiento que se empaquetan en la
tabla
45
vScrollbar = gtk.VScrollbar(None)
46
vScrollbar.show()
47
table.attach(vScrollbar, 1, 2, 0, 1, gtk.FILL|gtk.SHRINK,
48
gtk.FILL|gtk.SHRINK, 0, 0)
49
hScrollbar = gtk.HScrollbar(None)
50
hScrollbar.show()
51
table.attach(hScrollbar, 0, 1, 1, 2, gtk.FILL|gtk.SHRINK,
52
gtk.FILL|gtk.SHRINK,
53
0, 0)
54
# indicamos que las barras de desplazamiento usen los ajustes
del control disposicin
55
vAdjust = layout.get_vadjustment()
56
vScrollbar.set_adjustment(vAdjust)
57
hAdjust = layout.get_hadjustment()
58
hScrollbar.set_adjustment(hAdjust)
59
layout.connect("drag_data_received", self.receiveCallback)
60
layout.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
61
gtk.DEST_DEFAULT_HIGHLIGHT |
62
gtk.DEST_DEFAULT_DROP,
63
self.toCanvas, gtk.gdk.ACTION_COPY)
64
self.addImage(gtkxpm.gtk_xpm, 0, 0)
65
button = gtk.Button("Text Target")
66
button.show()
67
button.connect("drag_data_received", self.receiveCallback)
68
button.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
69
gtk.DEST_DEFAULT_HIGHLIGHT |
70
gtk.DEST_DEFAULT_DROP,
71
self.toButton, gtk.gdk.ACTION_COPY)
72
box.pack_start(button, gtk.FALSE, gtk.FALSE, 0)
73
return box
74
75
def addImage(self, xpm, xd, yd):
76
hadj = self.layout.get_hadjustment()
77
vadj = self.layout.get_vadjustment()
78
style = self.window.get_style()
79
pixmap, mask = gtk.gdk.pixmap_create_from_xpm_d(
80
self.window.window, style.bg[gtk.STATE_NORMAL], xpm)
81
image = gtk.Image()
82
image.set_from_pixmap(pixmap, mask)
83
button = gtk.Button()
84
button.add(image)
85
button.connect("drag_data_get", self.sendCallback)
86
button.drag_source_set(gtk.gdk.BUTTON1_MASK, self.fromImage,
87
gtk.gdk.ACTION_COPY)
88
button.show_all()
89
# ajustamos segn el desplazamientos de la disposicin - la
posicin del evento
90
# es relativo a la zona visible, no al tamao del control
disposicin
91
self.layout.put(button, int(xd+hadj.value), int(yd+vadj.value))
92
return
295
93
94
def sendCallback(self, widget, context, selection, targetType,
eventTime):
95
if targetType == self.TARGET_TYPE_TEXT:
96
now = time.time()
97
str = time.ctime(now)
98
selection.set(selection.target, 8, str)
99
elif targetType == self.TARGET_TYPE_PIXMAP:
100
selection.set(selection.target, 8,
101
string.join(gtkxpm.gtk_xpm, '\n'))
102
103
def receiveCallback(self, widget, context, x, y, selection,
targetType,
104
time):
105
if targetType == self.TARGET_TYPE_TEXT:
106
label = widget.get_children()[0]
107
label.set_text(selection.data)
108
elif targetType == self.TARGET_TYPE_PIXMAP:
109
self.addImage(string.split(selection.data, '\n'), x, y)
110
111
def __init__(self):
112
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
113
self.window.set_default_size(300, 300)
114
self.window.connect("destroy", lambda w: gtk.main_quit())
115
self.window.show()
116
layout = self.makeLayout()
117
self.window.add(layout)
118
119
def main():
120
gtk.main()
121
122
if __name__ == "__main__":
123
DragNDropExample()
124
main()
En donde filename contiene el nombre de un fichero rc. Entonces GTK+ analiza el fichero y usa los valores de estilo
a los tipos de controles que se definan en l.
296
Si se desea tener un conjunto especial de controles que puedan tener un estilo diferente a los dems, o cualquier otra
divisin lgica de controles, se debe usar una llamada a:
widget.set_name(name)
Se asignar el nombre especificado en el parmetro name al control widget. Esto permitir modificar los atributos de
este control en el fichero rc usando su nombre.
Si se usa una llamada parecida a:
button = gtk.Button("Special Button")
button.set_name("special button")
Entonces se le dar el nombre "special button" al botn button lo que permite localizarlo en el fichero rc como
"special button.GtkButton". [--- Verifquese!]
El ejemplo de fichero rc de ms abajo, modifica las propiedades de la ventana principal y permite a todos sus controles
hijos heredar el estilo descrito como el estilo "main button" (botn principal). El cdigo usado por la aplicacin es:
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.set_name("main window")
Que aplica a todos los controles Button de la "main window" (ventana principal) el estilo "main_buttons" tal como se
define en el fichero rc.
Como se puede ver, ste es un sistema bastante potente y flexible. Use su imaginacin para sacarle el mejor provecho.
23.2. Formato de los Ficheros rc de GTK+
El formato del fichero rc se muestra en el siguiente ejemplo. ste es el fichero testgtkrc de la distribucin GTK+,
pero se le han aadido unos algunos comentarios y cosas varias. Se puede incluir esta explicacin en los programas para
permitir al usuario afinar su aplicacin.
Existen varias directivas para modificar los atributos de un control.
Adems de estas propiedades tambin existen varios estados en los que puede encontrarse un control y se pueden
cambiar los diferentes colores, pixmaps y fuentes de cada estado. Estos estados son:
NORMAL (Normal)
El estado normal de un control, sin que el ratn est encima de l, sin que haya sido pulsado,
etc.
PRELIGHT
(Preiluminado)
Cuando el ratn est encima del control, los colores definidos en este estado tendrn efecto.
297
ACTIVE (Activo)
Cuando el control est presionado o se ha hecho clic en l estar activo y los atributos
asignados con este valor tendrn efecto.
INSENSITIVE
(Insensible)
Cuando un control est insensible, y no puede ser activado, tendr estos atributos.
SELECTED
(Seleccionado)
Cuando se usan las palabras "fg" y "bg" para modificar los colores de los controles, el formato es:
fg[<STATE>] = { Rojo, Verde, Azul }
Donde STATE es uno de los estados anteriores (PRELIGHT, ACTIVE, etc), y Rojo, Verde y Azul son valores en el
rango de 0 - 1.0, siendo { 1.0, 1.0, 1.0 } el blanco. Deben estar en formato float (decimal), o se les asignar 0. Por tanto,
un simple "1" no es correcto, debe ser "1.0". Un simple "0" funciona, ya que los valores no reconocidos se establecen
como 0.
bg_pixmap es muy similar al anterior excepto que los colores se sustituyen por un nombre de fichero.
pixmap_path es una lista de rutas separadas por ":". Cuando se especifique un pixmap, se buscar en esta lista.
La directiva "font" (fuente) es muy sencilla:
font = "<font name>"
Lo nico dificil es saber la cadena de la fuente font. Y, para ello, el programa xfontsel o una utilidad similar puede ser
de gran ayuda.
La directiva "widget_class" determina el estilo de una clase de controles. Estas clases se enumeran en el resumen general
de controles en la jerarqua de clases.
La directiva "widget" determina el estilo de un conjunto de controles con un nombre especfico, reemplazando cualquier
otro estilo de la clase del control en cuestin. Estos controles se registran en la aplicacin usando el mtodo
set_name() . Esto permite cambiar los atributos de un control para cada control concreto, en vez de especificar los
atributos de toda su clase. Se recomienda documentar todos los controles especiales para que los usuarios puedan
personalizarlos.
Si se usa la palabra parent (padre) como atributo el control usar los atributos de su padre en la aplicacin.
Al definir un estilo, se pueden asignar los atributos de un estilo definido previamente al estilo actual.
style "main_button" = "button"
{
font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
bg[PRELIGHT] = { 0.75, 0, 0 }
}
Este ejemplo usa el estilo "button", y crea un nuevo estilo "main_button" cambiando simplemente la fuente y el color de
fondo del estado preiluminado del estilo "button" (botn).
Por supuesto, muchos de estos atributos no funcionan con todos los controles. Es slo cuestin de sentido comn. Todo
lo que podra ser de aplicacin, debera funcionar.
23.3. Ejemplo de fichero rc
298
299
300
widget_class
widget_class
widget_class
widget_class
widget_class
widget_class
widget_class
widget_class
# Esto hace que todos los hijos de "main window" tengan el estilo main_button.
# Debiera ser documentado para poder sacarle partido.
widget "main window.*GtkButton*" style "main_button"
# tipo
# ventana
# tiempo
301
y
...
state
...
# estado
DELETE
el manejador de ventanas ha pedido que se oculte o
destruya la ventana de ms alto nivel, normalmente cuando el usuario hace clic
en un icono especial de la barra de ttulo.
DESTROY
EXPOSE
redibujarse.
MOTION_NOTIFY
BUTTON_PRESS
_2BUTTON_PRESS
se ha hecho doble clic en un botn del ratn (2 veces
dentro de un corto periodo de tiempo). Ntese que cada clic genera tambin un
evento BUTTON_PRESS.
_3BUTTON_PRESS
se ha hecho clic 3 veces seguidas dentro de un corto
periodo de tiempo en un botn del ratn. Ntese que cada clic genera tambin un
evento BUTTON_PRESS.
BUTTON_RELEASE
KEY_PRESS
KEY_RELEASE
ENTER_NOTIFY
LEAVE_NOTIFY
FOCUS_CHANGE
CONFIGURE
el tamao, posicin u orden de apilamiento de la ventana
ha cambiado. Obsrvese que GTK+ no usa estos eventos en ventanas hijas
(GDK_WINDOW_CHILD).
MAP
UNMAP
PROPERTY_NOTIFY
SELECTION_CLEAR
SELECTION_REQUEST
302
SELECTION_NOTIFY
PROXIMITY_IN
se ha hecho contacto en una superficie sensible de un
dispositivo de entrada (por ejemplo, una tableta grfica o pantalla sensible al
tacto).
PROXIMITY_OUT
DRAG_ENTER
arrastrando algo.
DRAG_LEAVE
arrastrando algo.
DRAG_MOTION
arrastrando algo.
DRAG_STATUS
ventana ha cambiado.
DROP_START
DROP_FINISHED
terminado.
CLIENT_EVENT
VISIBILITY_NOTIFY
NO_EXPOSE
indica que la regin fuente estaba disponible
completamente cuando partes de un dibujable fueron copiadas. No es muy til.
SCROLL
WINDOW_STATE
SETTING
state especifica el modificador de estado cuando ocurre el evento (es decir, especifica que teclas auxiliares y botones
del ratn estaban presionados). Es una combinacin con el operador OR de algunas de las siguientes constantes
(incluidas en el mdulo gtk.gdk):
SHIFT_MASK
LOCK_MASK
CONTROL_MASK
MOD1_MASK
MOD2_MASK
MOD3_MASK
MOD4_MASK
MOD5_MASK
BUTTON1_MASK
BUTTON2_MASK
BUTTON3_MASK
BUTTON4_MASK
BUTTON5_MASK
#
#
#
#
#
#
#
#
#
#
#
#
#
mscara
mscara
mscara
mscara
mscara
mscara
mscara
mscara
mscara
mscara
mscara
mscara
mscara
de maysculas
de bloqueo
de control
del modificador
del modificador
del modificador
del modificador
del modificador
del botn 1
del botn 2
del botn 3
del botn 4
del botn 5
1
2
3
4
5
303
Como con las dems seales, para determinar qu sucede cuando ocurre un evento se llama al mtodo connect(). Pero
tambin es necesario indicar a GTK+ qu eventos queremos tratar. Para ello llamamos al mtodo:
widget.set_events(events)
El argumento events especifica los eventos en los que se est interesado. Es una combinacin con el operador OR de
constantes que especifican los distintos tipos de eventos. Los tipos de eventos (del mdulo gtk.gdk) son:
EXPOSURE_MASK
POINTER_MOTION_MASK
POINTER_MOTION_HINT_MASK
BUTTON_MOTION_MASK
BUTTON1_MOTION_MASK
BUTTON2_MOTION_MASK
BUTTON3_MOTION_MASK
BUTTON_PRESS_MASK
BUTTON_RELEASE_MASK
KEY_PRESS_MASK
KEY_RELEASE_MASK
ENTER_NOTIFY_MASK
LEAVE_NOTIFY_MASK
FOCUS_CHANGE_MASK
STRUCTURE_MASK
PROPERTY_CHANGE_MASK
VISIBILITY_NOTIFY_MASK
PROXIMITY_IN_MASK
PROXIMITY_OUT_MASK
SUBSTRUCTURE_MASK
Hay un par de cuestiones que es necesario tener en cuenta al llamar al mtodo set_events() . En primer lugar, que
debe ser llamado antes de que se cree la ventana X del control PyGTK (en la prctica esto significa que es preciso
llamarlo inmediatamente despus de crear el control). En segundo lugar, que el control debe tener una ventana X
asociada. Por eficiencia, la mayoria de los controles no tienen su propia ventana sino que se dibujan en la ventana del
control padre. Entre estos controles se incluyen:
gtk.Alignment
gtk.Arrow
gtk.Bin
gtk.Box
gtk.Image
gtk.Item
gtk.Label
gtk.Layout
gtk.Pixmap
gtk.ScrolledWindow
gtk.Separator
gtk.Table
gtk.AspectFrame
gtk.Frame
gtk.VBox
gtk.HBox
gtk.VSeparator
gtk.HSeparator
Para capturar eventos en estos controles se debe usar un control EventBox. Vase la seccin del control EventBox
para ms detalles.
304
Los atributos de los eventos que PyGTK usa para cada tipo de evento son:
todos los eventos
type
window
send_event
NOTHING
DELETE
DESTROY
# tipo
# ventana
# evento enviado
EXPOSE
area
count
# rea
# cuenta
MOTION_NOTIFY
time
x
y
pressure
xtilt
ytilt
state
is_hint
source
deviceid
x_root
y_root
#
#
#
#
#
#
#
#
#
#
#
#
tiempo
x
y
presin
inclinacin x
inclinacin y
estado
es pista
fuente
identificador de dispositivo
x raz
y raz
time
x
y
pressure
xtilt
ytilt
state
button
source
deviceid
x_root
y_root
#
#
#
#
#
#
#
#
#
#
#
#
tiempo
x
y
presin
inclinacin x
inclinacin y
estado
botn
fuente
identificador de dispositivo
x raz
y raz
time
state
keyval
string
#
#
#
#
tiempo
estado
valor de tecla
cadena de caracteres
subwindow
time
x
y
x_root
y_root
mode
detail
focus
state
#
#
#
#
#
#
#
#
#
#
subventana
tiempo
x
y
x raz
y raz
modo
detalle
foco
estado
BUTTON_PRESS
_2BUTTON_PRESS
_3BUTTON_PRESS
BUTTON_RELEASE
KEY_PRESS
KEY_RELEASE
ENTER_NOTIFY
LEAVE_NOTIFY
305
FOCUS_CHANGE
_in
# dentro
CONFIGURE
x
y
width
height
#
#
#
#
MAP
UNMAP
PROPERTY_NOTIFY
x
y
ancho
alto
# tomo
# tiempo
# estado
selection
target
property
requestor
time
#
#
#
#
#
time
source
deviceid
# tiempo
# fuente
# identificador de dispositivo
context
time
x_root
y_root
#
#
#
#
CLIENT_EVENT
message_type
data_format
data
# tipo de mensaje
# formato de los datos
# datos
VISIBILTY_NOTIFY
state
# estado
SELECTION_CLEAR
SELECTION_REQUEST
SELECTION_NOTIFY
PROXIMITY_IN
PROXIMITY_OUT
DRAG_ENTER
DRAG_LEAVE
DRAG_MOTION
DRAG_STATUS
DROP_START
DROP_FINISHED
NO_EXPOSE
seleccin
objetivo
propiedad
solicitante
tiempo
contexto
tiempo
x raz
y raz
306
Lo que se puede hacer es tratar un evento de movimiento por cada evento que procesemos. Para hacer esto debemos
especificar POINTER_MOTION_HINT_MASK.
Cuando se especifica POINTER_MOTION_HINT_MASK, el servidor manda un evento de movimiento la primera vez
que el puntero se mueve tras entrar en la ventana o despus de que se pulse o suelte el botn. Los movimientos
posteriores se suprimen hasta que se solicite explcitamente la posicin del puntero con el siguiente mtodo de
gtk.gdk.Window:
x, y, mask = window.get_pointer()
window es un objeto gtk.gdk.Window. x e y son las coordenadas del puntero y mask es la mscara de
modificacin para detectar qu teclas estn pulsadas. (Hay un mtodo de gtk.Widget, get_pointer(), que
devuelve la misma informacin que el mtodo gtk.gdk.Window.get_pointer() pero sin la informacin de
mscara).
El programa de ejemplo scribblesimple.py demuestra el uso bsico de eventos y manejadores de eventos. La figura
Figura 24.2, Ejemplo sencillo - Scribble muestra el programa en accin:
Figura 24.2. Ejemplo sencillo - Scribble
Los manejadores de eventos se conectan a la drawing_area (rea de dibujo) en las siguientes lneas:
92
93
94
95
96
97
98
99
100
101
102
103
104
307
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
Este tamao predeterminado se puede cambiar, como en todos los controles, llamando al mtodo
set_size_request(), y tambin se puede modificar si el usuario cambia manualmente el tamao de la ventana que
contiene el rea de dibujo.
Hay que aclarar que, cuando creamos un control DrawingArea, se adquiere toda la responsabilidad de dibujar su
contenido. Si la ventana se tapa y luego se muestra, se recibe un evento de exposicin y se debe redibujar lo que antes se
ocult.
Tener que recordar lo que haba dibujado en la pantalla para que podamos redibujarlo es, cuanto menos, una
inconveniencia. Adems, puede resultar molesto visualmente el que partes de la ventana se limpien para luego dibujarse
paso a paso. La solucin a este problema es el uso de un pixmap oculto. En vez de dibujar directamente en la pantalla se
dibuja en la imagen almacenada en la memoria del servidor (que no se muestra), y luego se copian las partes relevantes a
la pantalla.
Para crear un pixmap fuera de pantalla, se usa esta funcin:
pixmap = gtk.gdk.Pixmap(window, width, height, depth=-1)
El parmetro window especifica una ventana gtk.gdk.Window de la que este pixmap tomar algunas propiedades.
width (ancho) y height (alto) especifican el tamao del pixmap. depth especifica la profundidad de color, es
decir, el nmero de bits por pxel de la nueva ventana. Si depth es -1 o se omite, coincidir con la profundidad de la
ventana.
308
Creamos el pixmap en nuestro manejador del evento "configure_event". Este evento se genera cada vez que la ventana
cambia de tamao, incluso cuando se crea por primera vez.
32
33
34
35
36
37
38
39
40
41
Ya se ha visto cmo manetener la pantalla sincronizada con el pixmap de respaldo, pero cmo se pintan cosas
interesantes en el pixmap? Existe un gran nmero de llamadas de PyGTK para dibujar en objetos dibujables. Un objeto
dibujable es sencillamente algo en lo que se puede dibujar. Puede ser una ventana, un pixmap, o un bitmap (imagen en
blanco y negro). Ya se han visto dos de esas llamadas con anterioridad, draw_rectangle() y draw_pixmap(). La
lista completa es:
# dibuja punto
drawable.draw_point(gc, x, y)
# dibuja lnea
drawable.draw_line(gc, x1, y1, x2, y2)
# dibuja rectngulo
drawable.draw_rectangle(gc, fill, x, y, width, height)
# dibuja arco
drawable.draw_arc(gc, fill, x, y, width, height, angle1, angle2)
# dibuja polgono
drawable.draw_polygon(gc, fill, points)
# dibuja dibujable
drawable.draw_drawable(gc, src, xsrc, ysrc, xdest, ydest, width, height)
# dibuja puntos
drawable.draw_points(gc, points)
# dibuja lneas
309
drawable.draw_lines(gc, points)
# dibuja segmentos
drawable.draw_segments(gc, segments)
# dibuja imagen rgb
drawable.draw_rgb_image(gc, x, y, width, height, dither, buffer, rowstride)
# dibuja imagen rgb 32 bits
drawable.draw_rgb_32_image(gc, x, y, width, height, dither, buffer, rowstride)
# dibuja imagen escala grises
drawable.draw_gray_image(gc, x, y, width, height, dither, buffer, rowstride)
Los mtodos del rea de dibujo son los mismos que los mtodos de dibujo de los objetos dibujables por lo que se puede
consultar la seccin Mtodos de Dibujo para ms detalles sobre estas funciones. Todas estas funciones comparten los
primeros argumentos. El primer argumento es un contexto grfico (gc).
Un contexto grfico encapsula informacin sobre cuestiones como los colores de fondo y frente o el ancho de lnea.
PyGTK posee un conjunto completo de funciones para crear y modificar contextos grficos, pero para mantener las
cosas fciles aqu simplemente se usarn contextos grficos predefinidos. Consltese la seccin Contexto Grfico del
rea de Dibujo para obtener ms informacin sobre los contextos grficos. Cada control tiene un estilo asociado (que se
puede modificar en un fichero gtkrc, segn se indica en la seccin Ficheros rc de GTK+.) ste, entre otras cosas,
almacena diversos contextos grficos. Algunos ejemplos del acceso a estos contextos grficos son:
widget.get_style().white_gc
widget.get_style().black_gc
widget.get_style().fg_gc[STATE_NORMAL]
widget.get_style().bg_gc[STATE_PRELIGHT]
Los campos fg_gc, bg_gc, dark_gc, y light_gc se indexan con un parmetro que puede tomar los siguientes
valores:
STATE_NORMAL,
STATE_ACTIVE,
STATE_PRELIGHT,
STATE_SELECTED,
STATE_INSENSITIVE
#
#
#
#
#
El
El
El
El
El
Por ejemplo, para STATE_SELECTED el color de frente predeterminado es el blanco y el color de fondo
predeterminado es azul oscuro.
La funcin draw_brush() (pinta trazo) del ejemplo, que es la que realmente dibuja en el pixmap, es la siguiente:
50
51
52
53
54
55
310
que notifica a X que ese rea ha de ser actualizada. En algn momento X generar un evento de exposicin
(posiblemente combinando las reas que se le pasan en varias llamadas a draw()) que har que se llame al manejador
del evento de exposicin que se cre anteriormente que copiar las partes relevantes en la pantalla.
Y... ya se ha tratado todo el programa de dibujo, exceptuando algunos detalles mundanos como la creacin de la ventana
principal.
Captulo 25. Trucos para Escribir Aplicaciones PyGTK
Tabla de contenidos
25.1. El usario debera manejar la interfaz, no al contrario
25.2. Separa el modelo de datos de la interfaz
25.3. Cmo separar los Mtodos de Retrollamada de los Manejadores de Seal
25.3.1. Introduccin
25.3.2. Herencia
25.3.3. Herencia aplicada a PyGTK
Esta seccin es simplemente una recoleccin de conocimientos, guas de estilo generales y trucos para crear buenas
aplicaciones PyGTK. Actualmente esta seccin es muy corta, pero espero que se vaya haciendo ms larga en futuras
ediciones de este tutorial.
25.1. El usario debera manejar la interfaz, no al contrario
PyGTK, como otros conjuntos de herramientas, te proporciona maneras de invocar widgets, tales como la bandera
DIALOG_MODAL que se pasa a los dialogos, que requiere una respuesta desde el usuario antes de que el resto de la
aplicacin contine. En Python, como en otros lenguajes, es una buena prctica evitar el uso de elementos de interfaz
modales.
En cada interaccin modal la aplicacin fuerza un flujo particular en el usuario y, si bien esto es en algn momento
inevitable, como regla general debe evitarse, ya que la aplicacin debe tratar de adaptarse al flujo de trabajo preferido
por el usuario.
Un caso particularmente frecuente de esto es la solicitud de confirmacin. Cada una de ellas debera correponder a un
punto en el que se debera porporcionar la posibilidad de deshacer los cambios. GIMP, la aplicacin para la que se
desarroll inicialmente GTk+, evita muchas operaciones que necesitaran detenerse y solicitar confirmacin del usuario
mediante una instruccin de deshacer que permite deshacer cualquier operacin que ejecuta.
25.2. Separa el modelo de datos de la interfaz
El sistema de objetos flexible e implcito de Python reduce el coste de la toma de decisiones sobre la arquitectura de la
aplicacin en comparacin con otros lenguajes ms rgidos (s, estamos pensando en C++). Una de ellas es la cuidadosa
separacin del modelo de datos (las clases y estructuras de datos que representan el estado para los que la aplicacin est
diseada para manejar) del controlador (las clases que implementan la interfaz de usuario).
En Python, un patrn de uso habitual es el de tener una clase principal editora/controladora que encapsula la interfaz de
usuario (con, probablemente, pequeas clases para controles que mantengan su estado) y una clase de modelo principal
que encapsula el estado de la aplicacin (probablemente con algunos miembros que son en s mismos instancias de
pequeas clases para representacin de datos). La clase controladora (controlador) llama a mtodos de la clase modelo
(el modelo) para realizar su manipulacin de datos. A su vez, el modelo delega la representacin en pantalla y el
procesado de entrada de datos al controlador.
Reducir la interfaz entre el modelo y el controlador facilita evitar quedar atrapado en decisiones iniciales sobre la
relacin entre cualquiera de ambas partes. Tambin hace ms sencillo el mantenimiento de la aplicacin y el diagnstico
de fallos.
311
Luego creamos una clase derivada que hereda de esas dos clases base:
class derived(base1, base2):
var3 = 3
#
#
#
#
#
#
El objeto llamado x tiene la habilidad de acceder a las variables y mtodos de las clases base porque ha heredado su
funcionalidad. Ahora apliquemos este concepto a una aplicacin de PyGTK.
25.3.3. Herencia aplicada a PyGTK
Crea un archivo llamado gui.py, y luego copia este cdigo en l:
312
def __init__(self):
#
#
CDIGO DE CONSTRUCCIN DE GUI
#----------------------------------------------self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.show()
self.vbox1 = gtk.VBox(False, 25)
self.window.add(self.vbox1)
open_button = gtk.Button(label="Abre cosas")
open_button.set_use_stock(True)
self.vbox1.pack_start(open_button, False, False, 0)
open_button.show()
save_button = gtk.Button(label="Guarda Cosas")
self.vbox1.pack_start(save_button, False, False, 0)
save_button.show()
undo_button = gtk.Button(label="Deshacer")
self.vbox1.pack_start(undo_button, False, False, 0)
undo_button.show()
self.vbox1.show()
#
#
MANEJADORES DE SEAL
#-----------------------------------------------open_button.connect("clicked", self.open)
save_button.connect("clicked", self.save)
undo_button.connect("clicked", self.undo)
self.window.connect("destroy", self.destroy)
def main(self):
gtk.main()
if __name__ == "__main__":
gui_instance = gui()
gui_instance.main()
Si se ejecuta el programa se comprueba que se trata de una nica ventana con algunos botones. Tal como est
organizado ahora el programa, todo el cdigo est en un nico archivo. Pero en un momento se ver cmo dividir el
programa en mltiples archivos. La idea es retirar esos cuatro mtodos de retrollamada de la clase gui y ponerlos en sus
propias clases, en archivos independientes. En el caso de que se tuviesen cientos de mtodos de retrollamada se
313
procurara agruparlos de alguna forma lgica como, por ejemplo, situando todos los mtodos que se dedican a
entrada/salida en la misma clase, y de esa manera se haran otras clases para grupos de mtodos.
Lo primero que tenemos que hacer es construir algunas clases para los mtodos del archivo gui.py. Creamos tres nuevos
archivos de texto, llamados io.py, undo.py y destroy.py, y sita esos archivos en el mismo subdirectorio que el archivo
gui.py. Copia el cdigo siguiente en el archivo io.py:
class io:
def open(self, widget):
print "abre cosas"
def save(self, widget):
print "guarda cosas"
Estos son los dos mtodos de retrollamada, open y save, del programa gui.py. Copia el siguiente bloque de cdigo en el
archivo undo.py:
class undo:
def undo(self, widget):
print "deshace cosas"
Importante
En tus futuras aplicaciones querrs importar mdulos como gtk, pango, os ect... en tu clase derivada (la
que contiene todo el cdigo de inicializacin de GUI), pero recuerda que necesitars importar tambin
algunos de esos mdulos en las clases base. Podras tener que crear una instancia de un control de gtk en
el mtodo de una clase base, y en ese caso necesitaras importar gtk.
Este es solamente un ejemplo de una clase base en la que sera necesario importar gtk.
import gtk
class Font_io
def Font_Chooser(self,widget):
self.fontchooser = gtk.FontSelectionDialog("Elige Fuente")
self.fontchooser.show()
Hay que observar que define un control gtk: un dilogo de seleccin de fuente. Normalmente se
importara gtk en la clase principal (la clase derivada) y todo funcionara correctamente. Pero desde el
momento que se extrae este Fon_Chooser de la clase principal y se pone en su propia clase y luego se
intenta heredar de ella, nos encontraramos con un error. En este aso, ni siquiera se detectara el error
hasta el momento de ejecutar la aplicacin. Pero cuando se intente usar el Font_Chooser se encontrara
314
que gtk no est definido, aunque se haya importado en la clase derivada. As que se ha de recordar que,
cuando se crean clases base, es necesario aadir sus propios import.
Con esas tres clases en sus tres archivos py, es necesario cambiar el cdigo en gui.py de tres modos:
1. Importar las clases que se han creado.
2. Modificar la definicin de la clase.
3. Eliminar los mtodos de retrollamada.
El siguiente cdigo actualizado muestra cmo hacerlo:
#Un archivo llamado: gui.py
#(versin actualizada)
#(con herencia mltiple)
import pygtk
import gtk
from io import file_io
from undo import undo
from destroy import destroy
#
# 1. Importa tus clases
#
def __init__(self):
#
#
CDIGO DE CONSTRUCCIN DE GUI
#----------------------------------------------self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
self.window.show()
self.vbox1 = gtk.VBox(False, 25)
self.window.add(self.vbox1)
open_button = gtk.Button(label="Open Stuff")
open_button.set_use_stock(True)
self.vbox1.pack_start(open_button, False, False, 0)
open_button.show()
save_button = gtk.Button(label="Save Stuff")
self.vbox1.pack_start(save_button, False, False, 0)
save_button.show()
undo_button = gtk.Button(label="Undo")
self.vbox1.pack_start(undo_button, False, False, 0)
undo_button.show()
self.vbox1.show()
#
#
MANEJADORES DE SEAL
#-----------------------------------------------open_button.connect("clicked", self.open_method)
save_button.connect("clicked", self.save_method)
undo_button.connect("clicked", self.undo_method)
self.window.connect("destroy", self.destroy)
def main(self):
gtk.main()
if __name__ == "__main__":
gui_instance = gui()
gui_instance.main()
315
Los nombres de las clases base van entre parntesis en la definicin de la clase. Ahora la clase gui es una clase derivada
y es capaz de usar todos los atributos y mtodos definidos en sus clases base. Tambin, la case gui hereda de mltiples
clases (dos o ms); es lo que se conoce como herncia mltiple.
Ahora cambia el archivo gui.py a la versin actualizada y ejecuta la aplicacin de nuevo. Vers que funciona
exactamente del mismo modo, salvo que ahora todos los mtodos de retrollamada estn en clases independients, y son
heredadas por la clase gui.
Hay otro tema de inters que comentar. Mientras que tu aplicacin gui.py y tus archivos de clases base estn en el
mismo directorio todo funcionar correctamente. Pero si quieres crear otro directorio en el que situar los archivos con las
clases base, entonces es necesario aadir dos lneas ms de cdigo junto al resto de intrucciones import en gui.py. As:
import sys
sys.path.append("classes")
en donde "classes" es el nombre del directiro en el que se guardan las clases base. Esto permite que Python sepa dnde
localizarlas. Prubalo. Simplemente crea un directorio llamado classes en el directorio en el que est el programa gui.py.
Luego aade los archivos de las tres clases base al directorio classes. Aade las dos lneas de cdigo anteriores a la parte
superior del archivo gui.py. Y, listo!
Una nota final para quienes usan py2exe para compilar aplicaciones Python. Si se ponen las cases base en el directorio
de Python, entonces py2exe los incluir en la versin compilada de la aplicacin como con cualquier otro mdulo
Python.
Captulo 26. Contribuir
Este documento, como muchos programas que se encuentran por ah, fue escrito gratuitamente por voluntarias. Si
encuentra algn aspecto de PyGTK que no haya sido recogido en esta documentacin, por favor considere la
contribucin al mismo.
Si se decide a contribuir, enve por favor, mediante correo electrnico, su texto en ingls a John Finlay
([email protected]) o, si es en espaol, a Lorenzo Gil Snchez ([email protected]). Asimismo, tenga en cuenta que
la totalidad de este documento es libre y gratuita, y cualquier contribucin que se aporte debe ser en esas mismas
condiciones de libertad y gratuidad. De ese modo cualquiera podr tanto utilizar libremente fragmentos del tutorial en
sus programas como distribuir libremente copias de este documento...
Muchas gracias.
Captulo 27. Crditos
316
Tabla de contenidos
27.1. Crditos Original de GTK+
27.1. Crditos Original de GTK+
Los siguientes son los crditos originales de los Tutoriales GTK+ 1.2 y GTK+ 2.0 (de los que este tutorial es
prcticamente copia literal):
317
No existe garanta de que este documento cumpla con su intencin original. Se ofrece simplemente como un recurso
libre y gratuito. Como tal, los autores y mantenedores de la informacin proporcionada en el mismo no garantizan de
que la informacin sea incluso correcta.
Apndice A. Seales de GTK
Tabla de contenidos
A.1. gtk.Object
A.2. gtk.Widget
A.3. GtkData
A.4. gtk.Container
A.5. gtk.Calendar
A.6. gtk.Editable
A.7. gtk.Notebook
A.8. gtk.List
A.9. gtk.MenuShell
A.10. gtk.Toolbar
A.11. gtk.Button
A.12. gtk.Item
A.13. gtk.Window
A.14. gtk.HandleBox
A.15. gtk.ToggleButton
A.16. gtk.MenuItem
A.17. gtk.CheckMenuItem
A.18. gtk.InputDialog
A.19. gtk.ColorSelection
A.20. gtk.StatusBar
A.21. gtk.Curve
A.22. gtk.Adjustment
Como PyGTk es un conjunto de controles orientado a objetos, tiene una jerarqua de herencia. Este mecanismo de
herencia se aplica a las seales. Por tanto, se debe utilizar la jerarqua de controles cuando se usen las seales listadas en
esta seccin.
A.1. gtk.Object
destroy(object, data)
A.2. gtk.Widget
show(GtkWidget, data)
hide(widget, data)
map(widget, data)
unmap(widget, data)
realize(widget, data)
unrealize(widget, data)
draw(widget, area, data)
draw-focus(widget, data)
draw-default(widget, data)
318
319
320
A.6. gtk.Editable
changed(editable, data)
insert-text(editable, new_text, text_length, position, data)
delete-text(editable, start_pos, end_pos, data)
activate(editable, data)
set-editable(editable, is_editable, data)
move-cursor(editable, x, y, data)
move-word(editable, num_words, data)
move-page(editable, x, y, data)
move-to-row(editable, row, data)
move-to-column(editable, column, data)
kill-char(editable, direction, data)
kill-word(editable, drirection, data)
kill-line(editable, direction, data)
cut-clipboard(editable, data)
copy-clipboard(editable, data)
paste-clipboard(editable, data)
A.7. gtk.Notebook
switch-page(noteboook, page, page_num, data)
A.8. gtk.List
selection-changed(list, data)
select-child(list, widget, data)
unselect-child(list, widget, data)
A.9. gtk.MenuShell
deactivate(menu_shell, data)
selection-done(menu_shell, data)
move-current(menu_shell, direction, data)
activate-current(menu_shell, force_hide, data)
cancel(menu_shell, data)
321
A.10. gtk.Toolbar
orientation-changed(toolbar, orientation, data)
style-changed(toolbar, toolbar_style, data)
A.11. gtk.Button
pressed(button, data)
released(button, data)
clicked(button, data)
enter(button, data)
leave(button, data)
A.12. gtk.Item
select(item, data)
deselect(item, data)
toggle(item, data)
A.13. gtk.Window
set-focus(window, widget, data)
A.14. gtk.HandleBox
child-attached(handle_box, widget, data)
child-detached(handle_box, widget, data)
A.15. gtk.ToggleButton
toggled(toggle_button, data)
A.16. gtk.MenuItem
activate(menu_item, data)
activate-item(menu_item, data)
A.17. gtk.CheckMenuItem
toggled(check_menu_item, data)
A.18. gtk.InputDialog
enable-device(input_dialog, deviceid, data)
disable-device(input_dialog, deviceid, data)
A.19. gtk.ColorSelection
322
color-changed(color_selection, data)
A.20. gtk.StatusBar
text-pushed(statusbar, context_id, text, data)
text-popped(statusbar, context_id, text, data)
A.21. gtk.Curve
curve-type-changed(curve, data)
A.22. gtk.Adjustment
changed(adjustment, data)
value-changed(adjustment, data)
Apndice B. Ejemplos de Cdigo
Tabla de contenidos
B.1. scribblesimple.py
B.1. scribblesimple.py
1
#!/usr/bin/env python
2
3
# example scribblesimple.py
4
5
# GTK - The GIMP Toolkit
6
# Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh
MacDonald
7
# Copyright (C) 2001-2002 John Finlay
8
#
9
# This library is free software; you can redistribute it and/or
10
# modify it under the terms of the GNU Library General Public
11
# License as published by the Free Software Foundation; either
12
# version 2 of the License, or (at your option) any later version.
13
#
14
# This library is distributed in the hope that it will be useful,
15
# but WITHOUT ANY WARRANTY; without even the implied warranty of
16
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
# Library General Public License for more details.
18
#
19
# You should have received a copy of the GNU Library General Public
20
# License along with this library; if not, write to the
21
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22
# Boston, MA 02111-1307, USA.
23
24
25 import gtk
26
27 # Backing pixmap for drawing area
28 pixmap = None
29
30 # Create a new backing pixmap of the appropriate size
31 def configure_event(widget, event):
32
global pixmap
323
33
34
x, y, width, height = widget.get_allocation()
35
pixmap = gtk.gdk.Pixmap(widget.window, width, height)
36
pixmap.draw_rectangle(widget.get_style().white_gc,
37
gtk.TRUE, 0, 0, width, height)
38
39
return gtk.TRUE
40
41 # Redraw the screen from the backing pixmap
42 def expose_event(widget, event):
43
x , y, width, height = event.area
44
widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL],
45
pixmap, x, y, x, y, width, height)
46
return gtk.FALSE
47
48 # Draw a rectangle on the screen
49 def draw_brush(widget, x, y):
50
rect = (x - 5, y - 5, 10, 10)
51
pixmap.draw_rectangle(widget.get_style().black_gc, gtk.TRUE,
52
rect[0], rect[1], rect[2], rect[3])
53
widget.queue_draw_area(rect[0], rect[1], rect[2], rect[3])
54
55 def button_press_event(widget, event):
56
if event.button == 1 and pixmap != None:
57
draw_brush(widget, event.x, event.y)
58
return gtk.TRUE
59
60 def motion_notify_event(widget, event):
61
if event.is_hint:
62
x, y, state = event.window.get_pointer()
63
else:
64
x = event.x
65
y = event.y
66
state = event.state
67
68
if state & gtk.gdk.BUTTON1_MASK and pixmap != None:
69
draw_brush(widget, x, y)
70
71
return gtk.TRUE
72
73 def main():
74
window = gtk.Window(gtk.WINDOW_TOPLEVEL)
75
window.set_name ("Test Input")
76
77
vbox = gtk.VBox(gtk.FALSE, 0)
78
window.add(vbox)
79
vbox.show()
80
81
window.connect("destroy", gtk.mainquit)
82
83
# Create the drawing area
84
drawing_area = gtk.DrawingArea()
85
drawing_area.set_size_request(200, 200)
86
vbox.pack_start(drawing_area, gtk.TRUE, gtk.TRUE, 0)
87
88
drawing_area.show()
89
90
# Signals used to handle backing pixmap
91
drawing_area.connect("expose_event", expose_event)
324
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
drawing_area.connect("configure_event", configure_event)
# Event signals
drawing_area.connect("motion_notify_event", motion_notify_event)
drawing_area.connect("button_press_event", button_press_event)
drawing_area.set_events(gtk.gdk.EXPOSURE_MASK
| gtk.gdk.LEAVE_NOTIFY_MASK
| gtk.gdk.BUTTON_PRESS_MASK
| gtk.gdk.POINTER_MOTION_MASK
| gtk.gdk.POINTER_MOTION_HINT_MASK)
# .. And a quit button
button = gtk.Button("Quit")
vbox.pack_start(button, gtk.FALSE, gtk.FALSE, 0)
button.connect_object("clicked", lambda w: w.destroy(), window)
button.show()
window.show()
gtk.main()
return 0
if __name__ == "__main__":
main()
Apndice C. ChangeLog
2004-12-22
* Version 2.2
2004-12-17
2004-11-23
* NewWidgetsAndObject.xml Create.
* pygtk2-tut.xml Add NewWidgetsAndObject.xml file. Bump version
number and set date.
328
* Adjustments.xml AdvancedEventAndSignalHandling.xml
ButtonWidget.xml ChangeLog ContainerWidgets.xml
DragAndDrop.xml DrawingArea.xml GettingStarted.xml
ManagingSelections.xml MenuWidget.xml
MiscellaneousWidgets.xml MovingOn.xml
PackingWidgets.xml RangeWidgets.xml Scribble.xml
SettingWidgetAttributes.xml TextViewWidget.xml
TimeoutsIOAndIdleFunctions.xml WidgetOverview.xml
Update files with example programs.
2004-07-06 John Finlay <[email protected]>
* ContainerWidgets.xml
* RangeWidgets.xml
* WidgetOverview.xml Remove reference to testgtk.py since it doesn't
exist in PyGTK 2.0 (thanks to Steve Chaplin)
2003-10-07 John Finlay <[email protected]>
* ContainerWidgets.xml
* layout.py Use random module instead of whrandom in
331
332
9.5. Dilogos..................................................................................................................................................... 69
9.6. Imgenes .................................................................................................................................................... 69
9.6.1. Pixmaps .............................................................................................................................................. 72
9.7. Reglas ........................................................................................................................................................ 78
9.8. Barras de Estado ......................................................................................................................................... 81
9.9. Entradas de Texto (Entry) ............................................................................................................................ 83
9.10. Botones Aumentar/Disminuir..................................................................................................................... 86
9.11. Lista Desplegable (Combo) ....................................................................................................................... 92
Nota ............................................................................................................................................................. 92
9.12. Calendario ................................................................................................................................................ 93
9.13. Seleccin de Color .................................................................................................................................. 100
9.14. Selectores de Fichero .............................................................................................................................. 103
9.15. Dilogo de Seleccin de Fuentes .............................................................................................................. 105
Captulo 10. Controles Contenedores ................................................................................................................ 106
10.1. La Caja de Eventos (EventBox) ............................................................................................................... 107
10.2. El control Alineador ................................................................................................................................ 108
10.3. Contenedor Fijo (Fixed) .......................................................................................................................... 109
10.4. Contenedor de Disposicin (Layout) ........................................................................................................ 111
10.5. Marcos (Frame) ...................................................................................................................................... 113
10.6. Marcos Proporcionales (AspectFrame) ..................................................................................................... 115
10.7. Controles de Panel (HPaned y VPaned) .................................................................................................... 117
10.8. Vistas (Viewport).................................................................................................................................... 120
10.9. Ventanas de Desplazamiento (ScrolledWindow) ....................................................................................... 120
10.10. Cajas de Botones (ButtonBoxes) ............................................................................................................ 123
10.11. Barra de Herramientas (Toolbar) ............................................................................................................ 126
10.12. Fichas (Notebook) ................................................................................................................................. 131
Nota ........................................................................................................................................................... 132
10.13. Elementos incrustables y puntos de conexin (Plugs y Sockets) ............................................................... 135
10.13.1. Elementos incrustables (Plugs) ........................................................................................................ 135
10.13.2. Puntos de Conexin (Sockets) ......................................................................................................... 136
Nota ........................................................................................................................................................... 137
Captulo 11. Control Men............................................................................................................................... 138
11.1. Creacin Manual de Mens ..................................................................................................................... 139
11.2. Ejemplo de Men Manual........................................................................................................................ 141
11.3. Uso de la Factoria de Elementos .............................................................................................................. 143
11.4. Ejemplo de Factoria de Elementos - ItemFactory....................................................................................... 143
Captulo 12. rea de Dibujo............................................................................................................................. 146
Nota ........................................................................................................................................................... 146
12.1. Contexto Grfico .................................................................................................................................... 146
Nota ........................................................................................................................................................... 148
12.2. Mtodos de Dibujo .................................................................................................................................. 150
Nota ........................................................................................................................................................... 151
Captulo 13. Control de Vista de Texto ............................................................................................................. 157
13.1. Perspectiva general de la Vista de Texto ................................................................................................... 157
13.2. Vistas de Texto ....................................................................................................................................... 158
Nota ........................................................................................................................................................... 159
13.3. Buffers de Texto ..................................................................................................................................... 163
13.3.1. Informacin de estado de un Buffer de Texto ..................................................................................... 164
13.3.2. Creacin de Iteradores de Texto ........................................................................................................ 164
13.3.3. Insercin, Obtencin y Eliminacin de Texto ..................................................................................... 165
13.3.4. Marcas de Texto (TextMark)............................................................................................................. 166
13.3.5. Creacin y Uso de Etiquetas de Texto................................................................................................ 167
13.3.6. Insercin de Imgenes y Controles .................................................................................................... 168
13.4. Iteradores de Texto ................................................................................................................................. 169
334
335
336
337
338