Tutorial Pygtk

254
Tutorial de PyGTK 2.0 por John Finlay y Lorenzo Gil Sánchez

description

Tutorial para el uso de Pygtk

Transcript of Tutorial Pygtk

Page 1: Tutorial Pygtk

Tutorial de PyGTK 2.0

por John Finlay y Lorenzo Gil Sánchez

Page 2: Tutorial Pygtk

Tutorial de PyGTK 2.0por John Finlay y Lorenzo Gil Sánchez

Este tutorial describe el uso del módulo de Python PyGTK.

Page 3: Tutorial Pygtk

Tabla de contenidosI. Tutorial de PyGTK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21.1. Explorando PyGTK. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2. Primeros Pasos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.1. Hola Mundo en PyGTK. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72.2. Teoría de Señales y Retrollamadas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92.3. Eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102.4. Hola Mundo Paso a Paso. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3. Avanzando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163.1. Más sobre manejadores de señales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163.2. Un Hola Mundo Mejorado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16

4. Empaquetamiento de Controles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194.1. Teoría de Cajas Empaquetadoras. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194.2. Detalles de Cajas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194.3. Programa de Demostración de Empaquetamiento. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214.4. Uso de Tablas para Empaquetar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274.5. Ejemplo de Empaquetamiento con Tablas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

5. Perspectiva General de Controles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315.1. Jerarquía de Controles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315.2. Controles sin Ventana. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

6. El Control de Botón . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346.1. Botones Normales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346.2. Botones Biestado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376.3. Botones de Activación. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406.4. Botones de Exclusión Mútua. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42

7. Ajustes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467.1. Crear un Ajuste. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467.2. Usar los Ajustes de la Forma Fácil. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467.3. El Interior del Ajuste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

8. Controles de Rango. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498.1. Barras de Desplazamiento. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498.2. Controles de Escala. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

8.2.1. Crear un Control de Escala. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 498.2.2. Métodos y Señales (bueno, al menos métodos). . . . . . . . . . . . . . . . . . . . . . . . . . . . 50

8.3. Métodos Comunes de los Rangos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508.3.1. Establecer la Política de Actualización. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 508.3.2. Obtener y Cambiar Ajustes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

8.4. Atajos de Teclas y Ratón. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 518.5. Ejemplo de Control de Rango. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52

9. Miscelanea de Controles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589.1. Etiquetas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 589.2. Flechas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 619.3. El Objeto Pistas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 639.4. Barras de Progreso. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 659.5. Diálogos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 699.6. Imágenes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

9.6.1. Pixmaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 739.7. Reglas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 809.8. Barras de Estado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849.9. Entradas de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 869.10. Botones Aumentar/Disminuir. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89

iii

Page 4: Tutorial Pygtk

Tutorial de PyGTK2.0

9.11. Lista Desplegable. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 959.12. Calendario. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 979.13. Selección de Color. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1059.14. Selectores de Fichero. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1089.15. Diálogo de Selección de Fuentes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111

10. Controles Contenedores. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11410.1. La Caja de Eventos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11410.2. El control Alineador . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11510.3. Contenedor Fijo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11610.4. Contenedor de Disposición. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11810.5. Marcos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12110.6. Marcos Proporcionales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12410.7. Controles de Panel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12610.8. Puertos de Visión. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12910.9. Ventanas de Desplazamiento. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12910.10. Cajas de Botones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13210.11. Barra de Herramientas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13610.12. Fichas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

11. Control Menú . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14811.1. Creación de Menús Manual. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14811.2. Ejemplo de Menú Manual. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15011.3. Usando la Factoria de Elementos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15311.4. Ejemplo de Factoria de Elementos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153

12. Área de Dibujo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15612.1. Contexto Gráfico. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15612.2. Métodos de Dibujo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160

13. Control de Vista de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16713.1. Perspectiva general de la Vista de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16713.2. Vistas de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16713.3. Buffers de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173

13.3.1. Información de estado de un Buffer de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . 17413.3.2. Creación de Iteradores de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17413.3.3. Inserción, Obtención y Eliminación de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . 17513.3.4. Marcas de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17713.3.5. Creación y Uso de Etiquetas de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17813.3.6. Inserción de Imágenes y Controles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178

13.4. Iteradores de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18013.4.1. Atributos de los Iteradores de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18013.4.2. Atributos de Texto de un Iterador de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18013.4.3. Copiar un Iterador de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18113.4.4. Recuperar Texto y Objetos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18113.4.5. Comprobar Condiciones en un Iterador de Texto. . . . . . . . . . . . . . . . . . . . . . . . 18213.4.6. Comprobar la localización de un Iterador de Texto. . . . . . . . . . . . . . . . . . . . . . . 18313.4.7. Movimiento a través del Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18413.4.8. Moverse a una Posición Específica. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18513.4.9. Buscar en el Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185

13.5. Marcas de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18513.6. Etiquetas de Texto y Tablas de Etiquetas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186

13.6.1. Etiquetas de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18613.6.2. Tablas de Etiquetas de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190

13.7. Un ejemplo de Vista de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19014. Control de Vista de Árbol. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19315. Controles sin documentar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194

iv

Page 5: Tutorial Pygtk

Tutorial de PyGTK 2.0

15.1. Etiqueta de Aceleración. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19415.2. Menú de Opciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19415.3. Elementos de Menú. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194

15.3.1. Elemento de Menú de Activación. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19415.3.2. Elemento de Menú de Exclusión Mútua. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19415.3.3. Elemento de Menú de Separación. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19415.3.4. Elemento de Menú de Cascada. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194

15.4. Curvas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19415.5. Diálogo de Mensaje. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19415.6. Curva Gamma. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19415.7. Enchufes y Clavijas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194

16. Cambiar Atributos de Controles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19516.1. Métodos de Banderas de Controles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19516.2. Métodos de Visualización de Controles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19616.3. Atajos de Teclador de Controles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19716.4. Métodos para el Nombre de los Controles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19816.5. Estilos de Controles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198

17. Temporizadores, Entrada/Salida y Funciones de Inactividad. . . . . . . . . . . . . . . . . . . . . . . . . . . . 20117.1. Temporizadores. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20117.2. Monitorizar la Entrada/Salida. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20117.3. Funciones de Inactividad. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202

18. Procesamiento Avanzado de Eventos y Señales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20318.1. Métodos de Señales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203

18.1.1. Conectar y Desconectar Manejadores de Señales. . . . . . . . . . . . . . . . . . . . . . . . 20318.1.2. Bloquear y Desbloquear Manejadores de Señales. . . . . . . . . . . . . . . . . . . . . . . . 20318.1.3. Emitir y Parar Señales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204

18.2. Emisión y Propagación de Señales. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20419. Tratamiento de Selecciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205

19.1. Perspectiva General de la Selección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20519.2. Recuperar la Selección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20519.3. Proporcionar la Selección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209

20. Arrastrar y Soltar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21320.1. Perspectiva General de Arrastrar y Soltar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21320.2. Propiedades de Arrastrar y Soltar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21320.3. Métodos de Arrastrar y Soltar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214

20.3.1. Configuración del Control Orígen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21420.3.2. Señales en el Control Orígen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21520.3.3. Configuración de un Control Destino. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21520.3.4. Señales en el Control Destino. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216

21. Ficheros rc de GTK. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22021.1. Funciones para Ficheros rc. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22021.2. Formato de los Ficheros rc de GTK. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22021.3. Ejemplo de fichero rc. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222

22. Scribble, Un Ejemplo de Programa de Dibujo Simple. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22522.1. Perspectiva General de Scribble. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22522.2. Manejo de Eventos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225

22.2.1. Scribble - Manejo de Eventos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23022.3. El Control del Área de Dibujo, y Dibujar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232

23. Trucos para Escribir Aplicaciones PyGTK. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23624. Contribuir . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237

A. Señales GTK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238A.1. GtkObject . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238A.2. GtkWidget . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238

v

Page 6: Tutorial Pygtk

Tutorial de PyGTK 2.0

A.3. GtkData . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240A.4. GtkContainer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240A.5. GtkCalendar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240A.6. GtkEditable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240A.7. GtkNotebook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241A.8. GtkList . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241A.9. GtkMenuShell . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241A.10. GtkToolbar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241A.11. GtkButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242A.12. GtkItem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242A.13. GtkWindow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242A.14. GtkHandleBox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242A.15. GtkToggleButton . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242A.16. GtkMenuItem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242A.17. GtkCheckMenuItem. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242A.18. GtkInputDialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243A.19. GtkColorSelection. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243A.20. GtkStatusBar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243A.21. GtkCurve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243A.22. GtkAdjustment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243

B. Ejemplos de Código. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244B.1. scribblesimple.py. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244

vi

Page 7: Tutorial Pygtk

Lista de figuras2.1. Ventana Simple PyGTK. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.2. Programa de ejemplo Hola Mundo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83.1. Ejemplo de Hola Mundo mejorado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174.1. Empaquetamiento: Cinco variaciones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194.2. Empaquetando con Spacing y Padding. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214.3. Empaquetando con pack_end(). . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214.4. Empaquetamiento usando una Tabla. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286.1. Botón con Pixmap y Etiqueta. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356.2. Ejemplo de Botón Biestado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386.3. Ejemplo de Botón de Activación. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406.4. Ejemplo de Botones de Exclusión Mútua. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438.1. Ejemplo de Controles de Rango. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 529.1. Ejemplos de Etiquetas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 599.2. Ejemplos de Botones con Flechas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 629.3. Ejemplo de Pistas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 649.4. Ejemplo de Barra de Progreso. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 679.5. Ejemplo de Imágenes en Botones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 719.6. Ejemplo de Pixmap en un Botón. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 759.7. Ejemplo de Ventana con Forma. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 769.8. Ejemplo de Reglas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 819.9. Ejemplo de Barra de Estado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 849.10. Ejemplo de Entrada. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 879.11. Ejemplo de Botón Aumentar/Disminuir. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 929.12. Ejemplo de Calendario. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 999.13. Ejemplo de Diálogo de Selección de Color. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1069.14. Ejemplo de Selección de Ficheros. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1099.15. Diálogo de Selección de Fuentes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11110.1. Ejemplo de Caja de Eventos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11410.2. Ejemplo de Fijo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11610.3. Ejemplo de Disposición. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11910.4. Ejemplo de Marco. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12210.5. Ejemplo de Marco Proporcional. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12410.6. Ejemplo de Panel. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12610.7. Ejemplo de Ventana de Desplazamiento. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13010.8. Ejemplo de Barra de Herramientas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14110.9. Ejemplo de Fichas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14411.1. Ejemplo de Menú. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15111.2. Ejemplo de Factoria de Elementos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15312.1. Ejemplo de Área de Dibujo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16313.1. Ejemplo básico de Vista de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16913.2. Ejemplo de Vista de Texto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19019.1. Ejemplo de Obtención de la Selección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20719.2. Ejemplo de Fijar la Selección. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21020.1. Ejemplo de Arrastrar y Soltar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21622.1. Ejemplo de Programa de Dibujo Scribble. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22522.2. Ejemplo simple de Scribble. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231

vii

Page 8: Tutorial Pygtk

Lista de tablas

20.1. Señales del Control de Orígen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21520.2. Señales del Control Destino. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216

viii

Page 9: Tutorial Pygtk

Parte I. Tutorial de PyGTK

Page 10: Tutorial Pygtk

Capítulo 1. IntroducciónPyGTK 2.0 es un conjunto de módulos que componen una interfaz Python para GTK+ 2.0. A través del resto deeste documento PyGTK se refiere a la versión 2.0 de PyGTK y GTK y GTK+ se refieren a la versión 2.0 de GTK+.El principal sitio web de PyGTK es www.daa.com.au/~james/pygtk [http://www.daa.com.au/~james/pygtk]. Elprincipal autor de PyGTK es:

• James Henstridge [email protected] [mailto:[email protected]]

Python es un lenguaje de programación interpretado, ampliable y orientado a objectos que se distribuye con unamplio conjunto de módulos que soportan acceso a un gran número de servicios del sistema operativo, serviciosde internet (como HTML, XML, FTP, etc.), gráficos (incluyendo OpenGL, TK, etc.), funciones de manejo decadenas, servicios de correo (IMAP, SMTP, POP3, etc.), multimedia (audio, JPEG) y servicios de criptografía.Además hay otros muchos módulos disponibles por terceros que añaden otros muchos servicios. Python esdistribuido bajo términos similares a los de la licencia GPL y está disponible para los sistemas operativos Linux,Unix, Windows y Macintosh. Más informacion sobre Python está disponible en www.python.org. El principalautor de Python es:

• Guido van Rossum [email protected] [mailto:[email protected]]

GTK (GIMP Toolkit) es una librería para crear interfaces de usuario gráficas. Esta licenciada usando la licenciaLGPL, por lo que puedes desarrollar software abierto, software libre, o incluso software comercial no libre usandoGTK sin tener que pagar nada en licencias o derechos.

Se llama el toolkit de GIMP porque originariamente fue escrita para desarrollar el Programa de Manipulaciónde Imágenes GNU (GIMP), pero GTK se usa ahora en un amplio número de proyectos de software, incluyendoel proyecto de Entorno de Modelo de Objetos orientados a Red (GNOME). GTK está diseñada encima de GDK(Kit de Dibujo de GIMP) que básicamente es una abstracción de las funciones de bajo nivel para acceder a lasfunciones del sistema de ventanas (Xlib en el caso del sistema de ventanas X). Los principales autores de GTKson:

• Peter Mattis [email protected] [mailto:[email protected]]

• Spencer Kimball [email protected] [mailto:[email protected]]

• Josh MacDonald [email protected] [mailto:[email protected]]

Actualmente GTK es mantenida por:

• Owen Taylor [email protected] [mailto:[email protected]]

• Tim Janik [email protected] [mailto:[email protected]]

2

Page 11: Tutorial Pygtk

Capítulo 1. Introducción

Básicamente GTK es 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 afunción).

También hay un tercer componente, llamado Glib, que contiene unas cuantas funciones que reemplazan algunasllamadas estandard, así como funciones adicionales para manejar listas enlazadas, etc. Las funciones de reemplazose usan para aumentar la portabilidad de GTK ya que algunas de las funciones que implementa no están disponibleso no son estandarden otros unix tales comog_strerror (). Algunas también incluyen mejoras a las versiones delibc, tales comog_malloc que tiene utilidades de depuración mejoradas.

En la versión 2.0, GLib ha incluido el sistema de tipos que forma la base para la jerarquía de clases de GTK,el sistema de señales usado en GTK, una API de hebras que abstrae las diferentes APIs nativas de hebras de lasdiversas plataformas y una facilidad para cargar modulos.

Como último componente, GTK usa la librería Pango para la salida de texto internacionalizado.

Este tutorial describe el interfaz de Python a GTK+ y esta basado en el tutorial de GTK+ 2.0 escrito por Tony Galee Ian Main. Este tutorial intenta documentar todo lo posible PyGTK, pero en ningun caso es completo.

Este tutorial asume algún conocimiento previo de Python, y de cómo crear y ejecutar programas Python. Si no es-tas familiarizado con Python, por favor lee el Tutorial de Python [http://www.python.org/doc/current/tut/tut.html]primero. Este tutorial no asume ningun conocimiento previo de GTK; si estás aprendiendo PyGTK para aprenderGTK, por favor comenta cómo encuentras este tutorial, y con qué has tenido problemas. Este tutorial no describecómo compilar o instalar Python, GTK+ o PyGTK.

Este tutorial está basado en:

• GTK+ 2.0.6

• Python 2.2

• PyGTK 1.99

Los ejemplos fueron escritos y probados en una RedHat 7.2.

Este documento está "en obras". Por favor consulta www.moeraki.com [http://www.moeraki.com/pygtktutorial]para actualizaciones.

Me gustaría mucho escuchar los problemas que estás teniendo aprendiendo PyGTK a partir de este documento,y aprecio comentarios y formas de como puede ser mejorado. Por favor mira la sección Contribuyendo para másinformación.

1.1. Explorando PyGTKJohan Dahlin ha escrito un pequeño programa Python (pygtkconsole.py [examples/pygtkconsole.py]) que seejecuta en Linux y permite una exploración interactiva de PyGTK. El progama proporciona un interfaz deintérprete interactivo al estilo de Python que se comunica con un proceso hijo que ejecuta los comandosintroducidos. Los módulos PyGTK se cargan por defecto. Un ejemplo simple de sesión es:

moe: 96:1095$ pygtkconsole.py

Python 2.2.2, PyGTK 1.99.14 (Gtk+ 2.0.6)Interactive console to manipulate GTK+ widgets.>>> w=Window()>>> b=Button(’Hola’)

3

Page 12: Tutorial Pygtk

Capítulo 1. Introducción

>>> w.add(b)>>> def hola(b):... print "¡Hola Mundo!"...>>> b.connect(’clicked’, hola)5>>> w.show_all()>>> ¡Hola Mundo!¡Hola Mundo!¡Hola Mundo!

>>> b.set_label("Hola a todos")>>>

Esto crea una ventana que contiene un botón que imprime un mensaje (’¡Hola Mundo!’) cuando se hace clic. Esteprograma permite probar fácilmente varios controles GTK y sus interfaces PyGTK.

4

Page 13: Tutorial Pygtk

Capítulo 2. Primeros PasosPara empezar nuestra introducción a PyGTK, comenzaremos con el programa más simple posible. Este programa(base.py[examples/base.py]) creará una ventana de 200x200 píxeles y no hay forma de salir excepto matándolodesde la consola.

1 #!/usr/bin/env python23 # example base.py45 import gtk67 class Base:8 def __init__(self):9 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)

10 self.window.show()1112 def main(self):13 gtk.main()1415 if __name__ == "__main__":16 base = Base()17 base.main()

Puedes ejecutar el programa anterior usando:

python base.py

Si base.py[examples/base.py] es hecho ejecutable y se puede encontrar en la variablePATH, puede ser ejecutadousando:

base.py

La línea 1 le pedirá a python que ejecutebase.py[examples/base.py] en este caso. Las líneas 15-17 compruebansi la variable__name__ es"__main__" , lo cual indica que el programa esta siendo ejecutado directamente porpython y no está siendo importado en un intérprete python. En este caso el programa crea una nueva instancia dela clase Base y guarda una referencia a ella en la variable base. Después invoca la funciónmain () para empezarel bucle de procesamiento de eventos de GTK.

Una ventana similar a Figura 2.1. Ventana Simple PyGTK debería aparecer en tu pantalla.

5

Page 14: Tutorial Pygtk

Capítulo 2. Primeros Pasos

Figura 2.1. Ventana Simple PyGTK

La primera línea permite al programabase.py[examples/base.py] ser invocado desde una consola Linux o Unixasumiendo quepython se encuentre en tuPATH. Esta línea será la primera línea en todos los programas de ejemplo.

La línea 5 importa el módulo PyGTK e inicializa el entorno GTK+. El módulo PyGTK define los interfacesPython para las funciones GTK+ que se usarán en el programa. Para aquellos que estén familiarizados con GTK+,la inicialización incluye la llamada a la funcióngtk_init (). Este configura unas cuantas cosas por nosotros comoel visual por defecto y el mapa de colores, manejadores de señales predeterminados, y comprueba los argumentosque se le pasan a tu programa desde la línea de comandos, buscando alguno de los siguientes:

• --gtk-module

• --g-fatal-warnings

• --gtk-debug

• --gtk-no-debug

• --gdk-debug

• --gdk-no-debug

• --display

• --sync

• --name

• --class

6

Page 15: Tutorial Pygtk

Capítulo 2. Primeros Pasos

Borra estos argumentos de la lista de argumentos, dejando todo lo que no reconoce para que tu programa lo proceseo ignore. Este conjunto de argumentos estandard son los que aceptan todas los programas GTK+.

Las líneas 7-10 definen una clase de python llamadaBase que define un método de inicialización de instancia__init__ (). La función __init__ () crea una ventana de nivel superior (línea 9) y ordena a GTK+ que lamuestre (línea 10). La GtkWindow se crea en la línea 9 con el argumentogtk.WINDOW_TOPLEVELque especificaque queremos una ventana sometida a las decoraciones y posicionamiento del manejador de ventanas. En vez decrear una ventana de tamaño 0x0, una ventana sin hijos tiene un tamaño de 200x200 por defecto para que puedasmanipularla.

Las líneas 12-13 definen el métodomain () que llama a la función PyGTKmain (), la cual, invoca el bucle principalde procesamiento de eventos de GTK+ para manejar eventos de ratón y de teclado, así como eventos de ventana.

Las líneas 15-17 permiten al programa que comience automáticamente si es llamado directamente o pasado comoargumento al intérprete de python; en estos casos el nombre de programa que hay en la variable__name__ serála cadena"__main__" y el código entre las líneas 16-17 será ejecutado. Si el programa se carga en un intérpretede python que se esté ejecutando, las líneas 16-17 no serán ejecutadas.

La línea 16 crea una instancia de la claseBase llamada base. UnaGtkWindow es creada y mostrada comoresultado.

La línea 17 llama al métodomain () de la claseBase , la cual comienza el bucle de procesamiento de eventos deGTK+. Cuando el control llega a este punto, GTK se dormirá esperando que ocurran eventos X (como pulsacionesde teclas o botones), alarmas, o notificaciones de entrada/salida de ficheros. En nuestro ejemplo, sin embargo, loseventos son ignorados.

2.1. Hola Mundo en PyGTKAhora haremos un programa con un control (un botón). Es la versión PyGTK del clásico programa hola mundo(helloworld.py [examples/helloworld.py] ).

1 #!/usr/bin/env python23 # example helloworld.py45 import gtk67 class HelloWorld:89 # This is a callback function. The data arguments are ignored

10 # in this example. More on callbacks below.11 def hello(self, widget, data=None):12 print "Hello World"1314 def delete_event(self, widget, event, data=None):15 # If you return FALSE in the "delete_event" signal handler,16 # GTK will emit the "destroy" signal. Returning TRUE means17 # you don’t want the window to be destroyed.18 # This is useful for popping up ’are you sure you want to quit?’19 # type dialogs.20 print "delete event occurred"2122 # Change TRUE to FALSE and the main window will be destroyed with23 # a "delete_event".24 return gtk.FALSE25

7

Page 16: Tutorial Pygtk

Capítulo 2. Primeros Pasos

26 # Another callback27 def destroy(self, widget, data=None):28 gtk.main_quit()2930 def __init__(self):31 # create a new window32 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)3334 # When the window is given the "delete_event" signal (this is given35 # by the window manager, usually by the "close" option, or on the36 # titlebar), we ask it to call the delete_event () function37 # as defined above. The data passed to the callback38 # function is NULL and is ignored in the callback function.39 self.window.connect("delete_event", self.delete_event)4041 # Here we connect the "destroy" event to a signal handler.42 # This event occurs when we call gtk_widget_destroy() on the window,43 # or if we return FALSE in the "delete_event" callback.44 self.window.connect("destroy", self.destroy)4546 # Sets the border width of the window.47 self.window.set_border_width(10)4849 # Creates a new button with the label "Hello World".50 self.button = gtk.Button("Hello World")5152 # When the button receives the "clicked" signal, it will call the53 # function hello() passing it None as its argument. The hello()54 # function is defined above.55 self.button.connect("clicked", self.hello, None)5657 # This will cause the window to be destroyed by calling58 # gtk_widget_destroy(window) when "clicked". Again, the destroy59 # signal could come from here, or the window manager.60 self.button.connect_object("clicked", gtk.Widget.destroy, self.window)6162 # This packs the button into the window (a GTK container).63 self.window.add(self.button)6465 # The final step is to display this newly created widget.66 self.button.show()6768 # and the window69 self.window.show()7071 def main(self):72 # All PyGTK applications must have a gtk.main(). Control ends here73 # and waits for an event to occur (like a key press or mouse event).74 gtk.main()7576 # If the program is run directly or passed as an argument to the python77 # interpreter then create a HelloWorld instance and show it78 if __name__ == "__main__":79 hello = HelloWorld()80 hello.main()

Figura 2.2. Programa de ejemplo Hola Mundo muestra la ventana creada porhelloworld.py [exam-ples/helloworld.py].

8

Page 17: Tutorial Pygtk

Capítulo 2. Primeros Pasos

Figura 2.2. Programa de ejemplo Hola Mundo

Las variables y funciones que se definen en el módulo PyGTK se llaman de la formagtk.* . Por ejemplo, elprogramahelloworld.py [examples/helloworld.py] usa:

gtk.FALSEgtk.mainquit()gtk.Window()gtk.Button()

del módulo PyGTK. En futuras secciones no especificaré el prefijo del módulo gtk, pero se asumirá. Por supuesto,los programas de ejemplo usarán los prefijos del módulo.

2.2. Teoría de Señales y Retrollamadas

Nota

En la versión 2.0 de GTK+, el sistema de señales se ha movido de GTK a GLib. No entraremos endetalles sobre las extensiones que GLib 2.0 tiene en relación con el sistema de señales de GTK 1.2. Lasdiferecias no deberían notarse entre los usuarios de PyGTK.

Antes de que entremos en detalle enhelloworld.py [examples/helloworld.py], discutiremos las señales y lasretrollamadas. GTK es una libreria orientada a eventos, lo que significa que se dormirá en la funcióngtk.main ()hasta que un evento ocurra y el control pase a la función apropiada.

Esta delegación del control se realiza usando la idea de "señales". (Nótese que estas señales no son las mismasque las señales de los sistemas Unix, y no se implementan usando estas, aunque la terminología es casi idéntica)Cuando un evento ocurre, como cuando presionamos un botón del ratón, la señal apropiada será "emitida" por elel control que fué presionado. Asi es cómo GTK hace la mayoría de su trabajo útil. Hay señales que todos loscontroles heredan, como "destroy", y hay señales que son específicas a cada control, como "toggled" en un botónde activación.

Para hacer que un botón realice una acción, tenemos que configurar un manejador de señales que capture estasseñales y llame a la función apropiada. Esto se hace usando un método deGtkWidget (heredado de la claseGObject ) como por ejemplo:

handler_id = object.connect(name, func, func_data)

donde object es la instancia deGtkWidget que estará emitiendo la señal, y el primer argumentoname es unacadena que contiene el nombre de la señal que deseas capturar. El segundo argumento,func , es la función quequieres que sea llamada cuando es capturado, y el tercer argumento,func_data , los datos que quieres pasar aesta función. El método devuelve un handler_id que puede usarse para desconectar o bloquear el manejador.

La función especificada en el tercer argumento se llama "función de retrollamada", y generalmente es de la forma:

9

Page 18: Tutorial Pygtk

Capítulo 2. Primeros Pasos

def callback_func(widget, callback_data):

donde el primer argumento será una referencia alwidget (control) que emitió la señal, y el segundo(callback_data ) una referencia a los datos dados como último argumento en el métodoconnect () mostradoantes.

Si la función de retrollamada es un método de un objeto entonces tendrá la forma general siguiente:

def callback_meth(self, widget, callback_data):

donde self es la instancia del objeto que invoca este método. Esta es la forma usada en el programa de ejemplohelloworld.py [examples/helloworld.py].

Nota

La forma anterior de declaración de una función de retrollamada a señales es sólo una guía general, yaque señales específicas de controles generan diferentes parámetros de llamada.

Otra llamada que se usa en el ejemplohelloworld.py [examples/helloworld.py] es:

handler_id = object.connect_object(name, func, slot_object)

connect_object () es lo mismo queconnect () excepto que una función de retrollamada sólo usa un argumentoy un método de retrollamada, dos argumentos:

def callback_func(object)def callback_meth(self, object)

dondeobject normalmente es un control.connect_object () permite usar los métodos de controles PyGTKqué solo admiten un argumento (self) como manejadores de señales.

2.3. EventosAdemás del mecanismo de señales descrito anteriormente, hay un conjunto de eventos que reflejan el mecanismode eventos de X. Las retrollamadas también se pueden conectar a estos eventos. Estos eventos son:

eventbutton_press_eventbutton_release_eventscroll_eventmotion_notify_eventdelete_eventdestroy_eventexpose_eventkey_press_eventkey_release_evententer_notify_eventleave_notify_eventconfigure_event

10

Page 19: Tutorial Pygtk

Capítulo 2. Primeros Pasos

focus_in_eventfocus_out_eventmap_eventunmap_eventproperty_notify_eventselection_clear_eventselection_request_eventselection_notify_eventproximity_in_eventproximity_out_eventvisibility_notify_eventclient_eventno_expose_eventwindow_state_event

Para conectar una función de retrollamada a uno de estos eventos se usa el métodoconnect (), como se ha dichoanteriormente, usando uno del nombres de eventos anteriores en el parámetroname. La función (o método) deretrollamada para eventos es ligeramente diferente de la usada para señales:

def callback_func(widget, event, callback_data ):

def callback_meth(self, widget, event, callback_data ):

GdkEvent es un tipo de objetos python cuyos atributos de tipo indicarán cuál de los eventos anteriores ha ocurrido.Los otros atributos del evento dependerán del tipo de evento. Valores posibles para los tipos son:

NOTHINGDELETEDESTROYEXPOSEMOTION_NOTIFYBUTTON_PRESS_2BUTTON_PRESS_3BUTTON_PRESSBUTTON_RELEASEKEY_PRESSKEY_RELEASEENTER_NOTIFYLEAVE_NOTIFYFOCUS_CHANGECONFIGUREMAPUNMAPPROPERTY_NOTIFYSELECTION_CLEARSELECTION_REQUESTSELECTION_NOTIFYPROXIMITY_INPROXIMITY_OUTDRAG_ENTERDRAG_LEAVEDRAG_MOTIONDRAG_STATUSDROP_STARTDROP_FINISHED

11

Page 20: Tutorial Pygtk

Capítulo 2. Primeros Pasos

CLIENT_EVENTVISIBILITY_NOTIFYNO_EXPOSESCROLLWINDOW_STATESETTING

Para acceder a estos valores se le pone el prefijo gtk.gdk. al tipo evento. Por ejemplogtk.gdk.DRAG_ENTER .

Por tanto, para conectar una función de retrollamada a uno de estos eventos usaríamos algo como:

button.connect("button_press_event", button_press_callback)

Esto asume que button es un controlGtkButton . Entonces, cuando el ratón este sobre el botón y un botón delratón se pulse, se llamará a la funciónbutton_press_callback . Esta función puede ser definida así:

def button_press_callback(widget, event, data ):

El valor que devuelve esta función indica si el evento debe ser propagado por el sistema de manejo de eventos GTK.Devolviendogtk.TRUE indicamos que el evento ha sido procesado, y que no debe ser propagado. Devolviendogtk.FALSE se continua el procesamiento normal del evento. Consulta la sección Procesamiento Avanzado deEventos y Señales para más detalles sobre el sistema de propagación.

Las APIs de selección y arrastrar y soltar de GDK también emiten unos cuantos eventos que se reflejan en GTKpor medio de señales. Consulta Señales en el Control de Orígen y Señales en el Control de Destino para detallessobre la sintáxis de las funciones de retrollamada para estas señales:

selection_receivedselection_getdrag_begin_eventdrag_end_eventdrag_data_deletedrag_motiondrag_dropdrag_data_getdrag_data_received

2.4. Hola Mundo Paso a PasoAhora que ya conocemos la teoría que hay detrás de esto, vamos a aclarar el programa de ejemplohelloworld.py[examples/helloworld.py] paso a paso.

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

Las líneas 11-12 definen el método de retrollamadahello () que se llamará cuando el botón sea pulsado. Cuandose llama a este método, se imprime "Hello World" a la consola. En este ejemplo ignoramos los parámetros de lainstancia del objeto, el control y los datos, pero la mayoría de las retrollamadas los usan. El parámetrodata sedefine con un valor por defecto deNone porque PyGTK no pasará ningún valor para los datos si no son incluidosen la llamada aconnect (); esto lanzaría un error ya que la retrollamada espera tres argumentos y solo recibe dos.

12

Page 21: Tutorial Pygtk

Capítulo 2. Primeros Pasos

Definir un valor por defecto None permite llamar a la retrollamada con dos o tres parámetros sin ningún error. Eneste caso el parámetro de datos podría haberse omitido ya que el métodohello () siempre será llamado con sólodos parámetros (nunca se usan los datos de usuario). En el siguiente ejemplo usaremos el argumento de datos parasaber que botón fue pulsado.

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

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

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

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

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

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

Las líneas 30-69 definen el método de inicialización de instancia__init__ () del objetoHelloWorld , el cualcrea la ventana y los controles que se usan en el programa.

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

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

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

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

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

13

Page 22: Tutorial Pygtk

Capítulo 2. Primeros Pasos

self.window.set_border_width(10)

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

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

En la línea 55 conectamos un manejador de señal al botón para que cuando emita la señal "clicked", nuestromanejador de retrollamadahello () se llame. No estamos pasandole ningún dato ahello () asi que simplementele pasamosNone como dato. Obviamente la señal "clicked" se emite cuando hacemos clic en el botón con el cursordel ratón. El valor del parámetro de los datosNone no es imprescindible y podría ser omitido. La retrollamada sellamará con un parámetro menos.

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

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

Como queremos usar el métododestroy () de la claseGtkWidget que acepta un argumento (el control que seva a destruir - en este casowindow ), utilizamos el métodoconnect_object () y le pasamos la referencia a laventana. El métodoconnect_object () organiza el primer argumento de la retrollamada para que seawindow

en vez de el botón.

Cuando se llama el métododestroy () de la claseGtkWidget esto provoca que se emita la señal "destroy" desdela ventana que a su vez provocará que se llame el métododestroy () de la claseHelloWorld para terminar elprograma.

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

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

self.window.add(self.button)

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

self.button.show()

14

Page 23: Tutorial Pygtk

Capítulo 2. Primeros Pasos

self.window.show()

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

def main(self):gtk.main()

Las líneas 78-80 permiten al programa ejecutarse automáticamente si es llamado directamente o como unargumento al intérprete de python. La línea 79 crea una instancia de la claseHelloWorld y guarda una referenciaa ella en la variablehello . La línea 80 llama al métodomain () de la claseHelloWorld para empezar el buclede procesamiento de eventos GTK.

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

Ahora, cuando hagamos clic con el botón del ratón en el botón GTK, el control emitirá una señal "clicked". Parapoder usar esta información, nuestro programa configura un manejador de señal para que capture esta señal, lacual llama a la función que decidamos. En nuestro ejemplo, cuando el botón que hemos creado es pulsado, sellama el métodohello () con un argumentoNone, y después se llama el siguiente manejador para esta señal. Elsiguiente manejador llama a la funcióndestroy () del control con la ventana como su argumento y de esta maneracausa a la ventana que emita la señal "destroy", que es capturada, y llama a nuestro métododestroy () de la claseHelloWorld

Otra función de los eventos es usar el manejador de ventanas para matar la ventana, lo cual causará que se emita"delete_event". Esto llamará nuestro manejador de "delete_event". Si devolvemosTRUEaqui, la ventana sequedará como si nada hubiera pasado. DevolviendoFALSEhará que GTK emita la señal "destroy" que llama a laretrollamada "destroy" de la claseHelloWorld cerrando GTK.

15

Page 24: Tutorial Pygtk

Capítulo 3. Avanzando3.1. Más sobre manejadores de señalesVeamos otra vez la llamada a connect() .

object.connect(name, func, func_data)

El valor de retorno de una llamada aconnect () es un número entero que identifica tu retrollamada. Como ya seha dicho, se pueden tener tantas retrollamadas por señal como necesites, y cada una se ejecutará por turnos, en elorden en el que fueron conectadas.

Este identificador permite eliminar la retrollamada de la lista mediante:

object.disconnect(id)

Así, pasándole el identificador que devuelven los métodos de conexión, se puede desconectar un manejador deseñal.

También puedes deshabilitar temporalmente los manejadores de señal con los métodossignal_handler_block ()y signal_handler_unblock () .

object.signal_handler_block(handler_id)

object.signal_handler_unblock(handler_id)

3.2. Un Hola Mundo Mejorado

1 #!/usr/bin/env python23 # example helloworld2.py45 import gtk67 class HelloWorld2:89 # Our new improved callback. The data passed to this method

10 # is printed to stdout.11 def callback(self, widget, data):12 print "Hello again - %s was pressed" % data1314 # another callback15 def delete_event(self, widget, event, data=None):16 gtk.main_quit()17 return gtk.FALSE1819 def __init__(self):20 # Create a new window21 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)22

16

Page 25: Tutorial Pygtk

Capítulo 3. Avanzando

23 # This is a new call, which just sets the title of our24 # new window to "Hello Buttons!"25 self.window.set_title("Hello Buttons!")2627 # Here we just set a handler for delete_event that immediately28 # exits GTK.29 self.window.connect("delete_event", self.delete_event)3031 # Sets the border width of the window.32 self.window.set_border_width(10)3334 # We create a box to pack widgets into. This is described in detail35 # in the "packing" section. The box is not really visible, it36 # is just used as a tool to arrange widgets.37 self.box1 = gtk.HBox(gtk.FALSE, 0)3839 # Put the box into the main window.40 self.window.add(self.box1)4142 # Creates a new button with the label "Button 1".43 self.button1 = gtk.Button("Button 1")4445 # Now when the button is clicked, we call the "callback" method46 # with a pointer to "button 1" as its argument47 self.button1.connect("clicked", self.callback, "button 1")4849 # Instead of add(), we pack this button into the invisible50 # box, which has been packed into the window.51 self.box1.pack_start(self.button1, gtk.TRUE, gtk.TRUE, 0)5253 # Always remember this step, this tells GTK that our preparation for54 # this button is complete, and it can now be displayed.55 self.button1.show()5657 # Do these same steps again to create a second button58 self.button2 = gtk.Button("Button 2")5960 # Call the same callback method with a different argument,61 # passing a pointer to "button 2" instead.62 self.button2.connect("clicked", self.callback, "button 2")6364 self.box1.pack_start(self.button2, gtk.TRUE, gtk.TRUE, 0)6566 # The order of showing the buttons is not really important, but I67 # recommend showing the window last, so it all pops up at once.68 self.button2.show()69 self.box1.show()70 self.window.show()7172 def main():73 gtk.main()7475 if __name__ == "__main__":76 hello = HelloWorld2()77 main()

Al ejecutarhelloworld2.py [examples/helloworld2.py] se genera la ventana que se ve en la Figura 3.1. Ejemplode Hola Mundo mejorado.

17

Page 26: Tutorial Pygtk

Capítulo 3. Avanzando

Figura 3.1. Ejemplo de Hola Mundo mejorado

Esta vez verás que no hay una forma fácil de salir del programa, tienes que usar tu manejador de ventanas o lalínea de comandos para matarlo. Un buen ejercicio para el lector sería insertar un tercer botón "Salir" que cerrarael programa. Puedes intentar jugar con las opciones de pack_start() mientras lees la siguiente sección. Prueba acambiar de tamaño la ventana, y observa lo que pasa.

Sólo como una nota, hay otra constante útil paraGtkWindow () - WINDOW_DIALOG. Esto interacciona con elmanejador de ventanas de una forma un poco diferente y debe usarse para ventanas temporales.

A continuación se describen en orden las pequeñas diferencias en el código con respecto al primer programahelloworld.

Como ya se ha dicho no hay manejador para el evento "destroy" en la versión mejorada de helloworld.

Las líneas 11-12 definen un método de retrollamada que es similar a la retrollamadahello () en el primerhelloworld. La diferencia está en que la retrollamada imprime un mensaje incluyendo los datos que se le pasan.

La línea 25 le pone un título en la barra de título de la ventana (mira la Figura 3.1. Ejemplo de Hola Mundomejorado).

La línea 37 crea una caja horizontal (GtkHBox ) para almacenar los dos botones que se crean en las líneas 43 y 58.La línea 40 añade la caja horizontal al contenedor de la ventana.

Las líneas 47 y 62 conectan el métodocallback () a la señal "clicked" de los botones. Cada botón establece unacadena diferente para que se le pase al métodocallback () cuando sea invocado.

Las líneas 51 y 64 empaquetan los botones en la caja horizontal. Las líneas 55 y 68 le dicen a GTK que muestrenlos botones.

Las líneas 69-70 le piden a GTK que muestre la caja y la ventana respectivamente.

18

Page 27: Tutorial Pygtk

Capítulo 4. Empaquetamiento deControlesCuando crees un programa, querrás poner más de un control en una ventana. Nuestro primer ejemplo holamundosolo usaba un control para que pudieramos usar simplemente el métodoadd () de la claseGtkContainer para"empaquetar" el control en la ventana. Pero cuando quieres poner más de un control en la ventana, ¿cómo controlasel sitio donde ese control se coloca? Aqui es donde la colocación entra en juego.

4.1. Teoría de Cajas EmpaquetadorasLa mayoría del empaquetamiento se realiza utilizando cajas. Estos contenedores invisibles de controles puedenser de dos tipos, una caja horizontal, y una caja vertical. Cuando empaquetamos controles en una caja horizontal,los objetos se insertan horizontalmente de izquierda a derecha o de derecha a izquierda dependiendo de la llamadaque se use. En una caja vertical, los controles se empaquetan de arriba a abajo o viceversa. Puedes usar unacombinación de cajas dentro de cajas para obtener el efecto deseado.

Para crear una nueva caja horizontal, usamos una llamada agtk.HBox (), y para cajas verticales,gtk.VBox (). Los métodospack_start () y pack_end () se utilizan para colocar objetos dentro de estos contenedores. Elmétodopack_start () empezará en la parte de arriba e irá bajando en una vbox, y de izquierda a derecha enuna hbox. El métodopack_end () hará lo contrario, empaquetará de abajo a arriba en una vbox, y de derecha aizquierda en una hbox. Usando estos métodos, podemos alinear a la derecha o a la izquierda nuestros controles yse pueden mezclar de la forma necesaria para obtener el efecto deseado. Usaremospack_start () en la mayoríade nuestros ejemplos. Un objeto puede ser otro contenedor o un control. De hecho, muchos controles son enrealidad contenedores por ellos mismos, incluyendo el botón, pero normalmente sólo usamos una etiqueta dentrode un botón.

Usando estas llamadas, GTK sabe donde quieres colocar tus controles y asi puede cambiar el tamaño automáti-camente y otras cosas interesantes. Como puedes imaginar, este método nos da bastante flexibilidad al colocar ycrear controles.

4.2. Detalles de CajasA causa de esta flexibilidad, el empaquetamiento de cajas puede ser confuso al principio. Hay muchas opciones,y no es obvio al principio cómo enacajan todas ellas. Al final, de cualquier forma, hay básicamente cinco estilos.La Figura 4.1. Empaquetamiento: Cinco variaciones muestra el resultado de ejecutar el programapackbox.py[examples/packbox.py] con un argumento de 1:

19

Page 28: Tutorial Pygtk

Capítulo 4. Empaquetamiento deControles

Figura 4.1. Empaquetamiento: Cinco variaciones

Cada línea contiene una caja horizontal (hbox) con varios botones. La llamada a pack es una copia de la llamadaa pack en cada uno de los botones de la hbox. Cada botón es empaquetado en la hbox de la misma manera (porejemplo, con los mismos argumentos al métodopack_start ().

Esto es un ejemplo del método pack_start() .

box.pack_start(child, expand, fill, padding)

box es la caja donde estas empaquetando el objeto; el primer argumento,child , es el objeto que se va aempaquetar. Los objetos serán botones por ahora, por lo que estaremos empaquetando botones dentro de cajas.

El argumentoexpand depack_start () y pack_end () controla si los controles se disponen para ocupar todo elespacio extra de la caja y de esta manera la caja se expande hasta ocupar todo el área reservada para ella (TRUE); osi la caja se encoge para ocupar el espacio justo de los controles (FALSE). Poniendo expand a FALSE te permitirájustificar a la derecha y a la izquierda tus controles. Si no, se expandirán para llenar la caja, y el mismo efectopodría obtenerse usando sólo opack_start () o pack_end ().

El argumntofill controla si el espacio extra se utiliza en los propios objetos (TRUE), como expacio extra en lacaja alrededor de los objetos (FALSE). Sólo tiene efecto si el argumento expand también es TRUE.

Python permite definir un método o función con valores de argumento por defecto y argumentos con palabrasclave. A través de este tutorial mostraré la definición de las funciones y métodos con valores por defecto ypalabras clave cuando sea aplicable. Por ejemplo, el método 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)

20

Page 29: Tutorial Pygtk

Capítulo 4. Empaquetamiento deControles

child , expand , fill y padding son palabras clave. Los argumentosexpand , fill y padding tienen losvalores por defecto mostrados arriba. El argumentochild debe especificarse.

Al crear una caja nueva, la función es la siguiente:

hbox = gtk.HBox( homogeneous =gtk.FALSE, spacing =0)

vbox = gtk.VBox( homogeneous =gtk.FALSE, spacing =0)

El argumentohomogeneous de gtk.HBox () y gtk.VBox () controla si cada objeto en la caja tiene el mismotamaño (por ejemplo el mismo ancho en una hbox, or el mismo alto en una vbox). Si se usa, las rutinas deempaquetado funcionan basicamente como si el argumento expand estuviera siempre activado.

¿Qué diferencia hay entrespacing (se pone cuando la caja se crea) ypadding (se pone cuando los elementosse empaquetan)? El spacing se añade entre objetos, y el padding se añade a cada lado de un objeto. LaFigura 4.2. Empaquetando con Spacing y Padding ilustra la diferencia; pasa un argumento de 2 apackbox.py[examples/packbox.py] :

Figura 4.2. Empaquetando con Spacing y Padding

La Figura 4.3. Empaquetando con pack_end() ilustra el uso del métodopack_end () (pasa un argumento de 3 apackbox.py [examples/packbox.py]). La etiqueta "end" se empaqueta con el métodopack_end (). Se mantendráen el borde derecho de la ventana cuando esta sea redimensionada.

21

Page 30: Tutorial Pygtk

Capítulo 4. Empaquetamiento deControles

Figura 4.3. Empaquetando con pack_end()

4.3. Programa de Demostración de EmpaquetamientoAqui [examples/packbox.py] está el código usado para crear la imágen de arriba. Esta bastante comentado asi queespero que no tengas ningún problema siguiendolo. Ejecútalo y juega con él.

1 #!/usr/bin/env python23 # example packbox.py45 import gtk6 import sys, string78 # Helper function that makes a new hbox filled with button-labels. Arguments9 # for the variables we’re interested are passed in to this function. We do

10 # not show the box, but do show everything inside.1112 def make_box(homogeneous, spacing, expand, fill, padding):1314 # Create a new hbox with the appropriate homogeneous15 # and spacing settings16 box = gtk.HBox(homogeneous, spacing)1718 # Create a series of buttons with the appropriate settings19 button = gtk.Button("box.pack")20 box.pack_start(button, expand, fill, padding)21 button.show()2223 button = gtk.Button("(button,")24 box.pack_start(button, expand, fill, padding)25 button.show()2627 # Create a button with the label depending on the value of28 # expand.29 if expand == gtk.TRUE:30 button = gtk.Button("TRUE,")31 else:32 button = gtk.Button("FALSE,")3334 box.pack_start(button, expand, fill, padding)35 button.show()3637 # This is the same as the button creation for "expand"38 # above, but uses the shorthand form.

22

Page 31: Tutorial Pygtk

Capítulo 4. Empaquetamiento deControles

39 button = gtk.Button(("FALSE,", "TRUE,")[fill==gtk.TRUE])40 box.pack_start(button, expand, fill, padding)41 button.show()4243 padstr = "%d)" % padding4445 button = gtk.Button(padstr)46 box.pack_start(button, expand, fill, padding)47 button.show()48 return box4950 class PackBox1:51 def delete_event(self, widget, event, data=None):52 gtk.main_quit()53 return gtk.FALSE5455 def __init__(self, which):5657 # Create our window58 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)5960 # You should always remember to connect the delete_event signal61 # to the main window. This is very important for proper intuitive62 # behavior63 self.window.connect("delete_event", self.delete_event)64 self.window.set_border_width(10)6566 # We create a vertical box (vbox) to pack the horizontal boxes into.67 # This allows us to stack the horizontal boxes filled with buttons one68 # on top of the other in this vbox.69 box1 = gtk.VBox(gtk.FALSE, 0)7071 # which example to show. These correspond to the pictures above.72 if which == 1:73 # create a new label.74 label = gtk.Label("HBox(FALSE, 0)")7576 # Align the label to the left side. We’ll discuss this method77 # and others in the section on Widget Attributes.78 label.set_alignment(0, 0)7980 # Pack the label into the vertical box (vbox box1). Remember that81 # widgets added to a vbox will be packed one on top of the other in82 # order.83 box1.pack_start(label, gtk.FALSE, gtk.FALSE, 0)8485 # Show the label86 label.show()8788 # Call our make box function - homogeneous = FALSE, spacing = 0,89 # expand = FALSE, fill = FALSE, padding = 090 box2 = make_box(gtk.FALSE, 0, gtk.FALSE, gtk.FALSE, 0)91 box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)92 box2.show()9394 # Call our make box function - homogeneous = FALSE, spacing = 0,95 # expand = TRUE, fill = FALSE, padding = 096 box2 = make_box(gtk.FALSE, 0, gtk.TRUE, gtk.FALSE, 0)97 box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)

23

Page 32: Tutorial Pygtk

Capítulo 4. Empaquetamiento deControles

98 box2.show()99

100 # Args are: homogeneous, spacing, expand, fill, padding101 box2 = make_box(gtk.FALSE, 0, gtk.TRUE, gtk.TRUE, 0)102 box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)103 box2.show()104105 # Creates a separator, we’ll learn more about these later,106 # but they are quite simple.107 separator = gtk.HSeparator()108109 # Pack the separator into the vbox. Remember each of these110 # widgets is being packed into a vbox, so they’ll be stacked111 # vertically.112 box1.pack_start(separator, gtk.FALSE, gtk.TRUE, 5)113 separator.show()114115 # Create another new label, and show it.116 label = gtk.Label("HBox(TRUE, 0)")117 label.set_alignment(0, 0)118 box1.pack_start(label, gtk.FALSE, gtk.FALSE, 0)119 label.show()120121 # Args are: homogeneous, spacing, expand, fill, padding122 box2 = make_box(gtk.TRUE, 0, gtk.TRUE, gtk.FALSE, 0)123 box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)124 box2.show()125126 # Args are: homogeneous, spacing, expand, fill, padding127 box2 = make_box(gtk.TRUE, 0, gtk.TRUE, gtk.TRUE, 0)128 box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)129 box2.show()130131 # Another new separator.132 separator = gtk.HSeparator()133 # The last 3 arguments to pack_start are:134 # expand, fill, padding.135 box1.pack_start(separator, gtk.FALSE, gtk.TRUE, 5)136 separator.show()137 elif which == 2:138 # Create a new label, remember box1 is a vbox as created139 # near the beginning of __init__()140 label = gtk.Label("HBox(FALSE, 10)")141 label.set_alignment( 0, 0)142 box1.pack_start(label, gtk.FALSE, gtk.FALSE, 0)143 label.show()144145 # Args are: homogeneous, spacing, expand, fill, padding146 box2 = make_box(gtk.FALSE, 10, gtk.TRUE, gtk.FALSE, 0)147 box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)148 box2.show()149150 # Args are: homogeneous, spacing, expand, fill, padding151 box2 = make_box(gtk.FALSE, 10, gtk.TRUE, gtk.TRUE, 0)152 box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)153 box2.show()154155 separator = gtk.HSeparator()156 # The last 3 arguments to pack_start are:

24

Page 33: Tutorial Pygtk

Capítulo 4. Empaquetamiento deControles

157 # expand, fill, padding.158 box1.pack_start(separator, gtk.FALSE, gtk.TRUE, 5)159 separator.show()160161 label = gtk.Label("HBox(FALSE, 0)")162 label.set_alignment(0, 0)163 box1.pack_start(label, gtk.FALSE, gtk.FALSE, 0)164 label.show()165166 # Args are: homogeneous, spacing, expand, fill, padding167 box2 = make_box(gtk.FALSE, 0, gtk.TRUE, gtk.FALSE, 10)168 box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)169 box2.show()170171 # Args are: homogeneous, spacing, expand, fill, padding172 box2 = make_box(gtk.FALSE, 0, gtk.TRUE, gtk.TRUE, 10)173 box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)174 box2.show()175176 separator = gtk.HSeparator()177 # The last 3 arguments to pack_start are:178 # expand, fill, padding.179 box1.pack_start(separator, gtk.FALSE, gtk.TRUE, 5)180 separator.show()181182 elif which == 3:183184 # This demonstrates the ability to use pack_end() to185 # right justify widgets. First, we create a new box as before.186 box2 = make_box(gtk.FALSE, 0, gtk.FALSE, gtk.FALSE, 0)187188 # Create the label that will be put at the end.189 label = gtk.Label("end")190 # Pack it using pack_end(), so it is put on the right191 # side of the hbox created in the make_box() call.192 box2.pack_end(label, gtk.FALSE, gtk.FALSE, 0)193 # Show the label.194 label.show()195196 # Pack box2 into box1197 box1.pack_start(box2, gtk.FALSE, gtk.FALSE, 0)198 box2.show()199200 # A separator for the bottom.201 separator = gtk.HSeparator()202203 # This explicitly sets the separator to 400 pixels wide by 5204 # pixels high. This is so the hbox we created will also be 400205 # pixels wide, and the "end" label will be separated from the206 # other labels in the hbox. Otherwise, all the widgets in the207 # hbox would be packed as close together as possible.208 separator.set_usize(400, 5)209 # pack the separator into the vbox (box1) created near the start210 # of __init__()211 box1.pack_start(separator, gtk.FALSE, gtk.TRUE, 5)212 separator.show()213214 # Create another new hbox.. remember we can use as many as we need!215 quitbox = gtk.HBox(gtk.FALSE, 0)

25

Page 34: Tutorial Pygtk

Capítulo 4. Empaquetamiento deControles

216217 # Our quit button.218 button = gtk.Button("Quit")219220 # Setup the signal to terminate the program when the button is clicked221 button.connect_object("clicked", gtk.mainquit, self.window)222 # Pack the button into the quitbox.223 # The last 3 arguments to pack_start are:224 # expand, fill, padding.225 quitbox.pack_start(button, gtk.TRUE, gtk.FALSE, 0)226 # pack the quitbox into the vbox (box1)227 box1.pack_start(quitbox, gtk.FALSE, gtk.FALSE, 0)228229 # Pack the vbox (box1) which now contains all our widgets, into the230 # main window.231 self.window.add(box1)232233 # And show everything left234 button.show()235 quitbox.show()236237 box1.show()238 # Showing the window last so everything pops up at once.239 self.window.show()240241 def main():242 # And of course, our main loop.243 gtk.main()244 # Control returns here when main_quit() is called245 return 0246247 if __name__ =="__main__":248 if len(sys.argv) != 2:249 sys.stderr.write("usage: packbox.py num, where num is 1, 2, or 3.\n")250 sys.exit(1)251 PackBox1(string.atoi(sys.argv[1]))252 main()

El pequeño tour por el códigopackbox.py [examples/packbox.py] empieza por las lineas 12-48 que definen unafunción auxiliar que crea una caja horizontal y la rellena con botones según los parámetros específicados. Devuelveuna referencia a la caja horizontal.

Las lineas 55-239 definen el método de inicialización__init__ () de la clasePackBox1 que crea una ventanay una caja vertical dentro de ella que se rellena con una configuración de controles dependiendo del argumentoque se le pase. Si se le pasa un 1, las lineas 73-136 crean una ventana mostrando las cinco únicas posibilidadesque hay cuando variamos los parámetros homogeneous, expand y fill. Si se le pasa un 2, las lineas 138-180 creanuna ventana mostrando las diferentes combinaciones de fill con spacing y padding. Finalmente, si le pasamos un3, las lineas 186-212 crean una ventana mostrando el uso del métodopack_start () para justificar los botones ala izquierda y el métodopack_end () para justificar una etiqueta a la derecha. Las lineas 215-235 crean una cajahorizontal que contiene un botón que se empaqueta dentro de la caja vertical. La señal ’clicked’ del botón estáconectada a la función PyGTKmain_quit () para terminar el programa.

Las lineas 248-250 comprueba los argumentos de la linea de comandos y terminan el programa usando la funciónsys.exit () si no hay exactamente un argumento. La linea 251 crea una instancia de PackBox1. La linea 252llama a la funciónmain () para empezar el bucle de procesamiento de eventos GTK.

26

Page 35: Tutorial Pygtk

Capítulo 4. Empaquetamiento deControles

En este programa de ejemplo, las referencias a los controles (excepto a la ventana) no se guardan en los atributosde instancia del objeto porque no se necesitan después.

4.4. Uso de Tablas para EmpaquetarVeamos otra manera de empaquetar - Tablas. Pueden ser extremadamente útiles en determinadas situaciones.

Al usar tablas, creamos una rejilla donde podemos colocar los controles. Los controles puede ocupar tantosespacios como especifiquemos.

Lo primero que hay que mirar es obviamente la funcióngtk.Table () :

table = gtk.Table( rows =1, columns =1, homogeneous =FALSE)

El primer argumento es el número de filas de la tabla, mientras que el segundo, obviamente, es el número decolumnas.

El argumentohomogeneous tiene que ver en el tamaño de las celdas de la tabla. Sihomogeneous es TRUE, lasceldas de la tabla tienen el tamaño del mayor control en la tabla. Sihomogeneous es FALSE, el tamaño de lasceldas viene dado por el control más alto en su misma fila, y el control más ancho en su columna.

Las filas y las columnas se disponen de 0 a n, donde n es el número que se especificó en la llamada agtk.Table ().Por tanto, si especificas rows (filas) = 2 y columns (columnas) = 2, la dispoisición quedaría así:

0 1 20+----------+----------+| | |

1+----------+----------+| | |

2+----------+----------+

Fijate que el sistema de coordenadas empieza en la esquina superior izquierda. Para meter un control en una caja,usa el siguiente método:

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 creaste congtk.Table (). El primer parámetro ("child") es el control que quieresmeter en la tabla.

Los argumentosleft_attach , right_attach , top_attach y bottom_attach especifican donde colocar elcontrol, y cuantas cajas usar. Si quieres un botón en la esquina inferior derecha de una tabla 2x2, y quieres queocupe SÓLO ese espacio,left_attach sería = 1,right_attach = 2, top_attach = 1, bottom_attach = 2.

Ahora, si quieres que un control ocupe la fila entera de nuestra tabla 2x2, pondríasleft_attach = 0,right_attach = 2, top_attach = 0, bottom_attach = 1.

Los argumentosxoptions y yoptions se usan para especificar opciones de colocación y pueden ser unidasmediante la operación OR permitiendo así múltiples opciones.

Estas opciones son:

27

Page 36: Tutorial Pygtk

Capítulo 4. Empaquetamiento deControles

FILL Si la caja es más grande que el control, y especificas FILL, el control se expandirá hastausar todo el espacio disponible.

SHRINK Si se le asigna menos espacio a la tabla del que solicitó (normalmente porque el usuario haredimensionado la ventana), entonces los controles normalmente sería empujados a la parteinferior de la ventana y desaparecerían. Si especificas SHRINK, los controles se encojerancon la tabla.

EXPAND Esto hará que la tabla se expanda para usar el espacio sobrante en la ventana.

El Padding es igual que en las cajas, ya que crea un espacio vacío especificado en pixeles alrededor del control.

También tenemos los métodosset_row_spacing () y set_col_spacing () . Añaden espacio entre las filas enla columna o fila especificada.

table.set_row_spacing( row , spacing )

y

table.set_col_spacing( column , spacing )

Fijate que para las columnas, el espacio va a la derecha de la columna, y para las filas, el espacio va debajo de lafila.

También puedes poner un espacio consistente para todas las filas y/o columnas con:

table.set_row_spacings( spacing )

y,

table.set_col_spacings( spacing )

Fijate que con estas funciones, la última fila y la última columna no obtienen ningún espacio.

4.5. Ejemplo de Empaquetamiento con TablasEl programa de ejemplotable.py [examples/table.py] hace una ventana con tres botones en una tabla 2x2. Losprimeros dos botones se colocarán en la fila superior. Un tercer botón, de salir, se coloca en la fila inferior,ocupando las dos columnas. La Figura 4.4. Empaquetamiento usando una Tabla ilustra la ventana resultante:

28

Page 37: Tutorial Pygtk

Capítulo 4. Empaquetamiento deControles

Figura 4.4. Empaquetamiento usando una Tabla

Aqui va el código fuente:

1 #!/usr/bin/env python23 # example table.py45 import gtk67 class Table:8 # Our callback.9 # The data passed to this method is printed to stdout

10 def callback(self, widget, data=None):11 print "Hello again - %s was pressed" % data1213 # This callback quits the program14 def delete_event(self, widget, event, data=None):15 gtk.main_quit()16 return gtk.FALSE1718 def __init__(self):19 # Create a new window20 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)2122 # Set the window title23 self.window.set_title("Table")2425 # Set a handler for delete_event that immediately26 # exits GTK.27 self.window.connect("delete_event", self.delete_event)2829 # Sets the border width of the window.30 self.window.set_border_width(20)3132 # Create a 2x2 table33 table = gtk.Table(2, 2, gtk.TRUE)3435 # Put the table in the main window36 self.window.add(table)3738 # Create first button39 button = gtk.Button("button 1")4041 # When the button is clicked, we call the "callback" method42 # with a pointer to "button 1" as its argument43 button.connect("clicked", self.callback, "button 1")

29

Page 38: Tutorial Pygtk

Capítulo 4. Empaquetamiento deControles

444546 # Insert button 1 into the upper left quadrant of the table47 table.attach(button, 0, 1, 0, 1)4849 button.show()5051 # Create second button5253 button = gtk.Button("button 2")5455 # When the button is clicked, we call the "callback" method56 # with a pointer to "button 2" as its argument57 button.connect("clicked", self.callback, "button 2")58 # Insert button 2 into the upper right quadrant of the table59 table.attach(button, 1, 2, 0, 1)6061 button.show()6263 # Create "Quit" button64 button = gtk.Button("Quit")6566 # When the button is clicked, we call the mainquit function67 # and the program exits68 button.connect("clicked", gtk.mainquit)6970 # Insert the quit button into the both lower quadrants of the table71 table.attach(button, 0, 2, 1, 2)7273 button.show()7475 table.show()76 self.window.show()7778 def main():79 gtk.main()80 return 08182 if __name__ =="__main__":83 Table()84 main()

La claseTable se define entre las lineas 7-76. Las lineas 10-11 definen el métodocallback () que se llamacuando se hace "click" en dos de los botones. La retrollamada sólo imprime un mensaje en la consola indicandoqué botón fue pulsado usando los datos que se le pasan.

Las lineas 14-16 definen el métododelete_event () que se llama cuando el manejador de ventanas le pide a laventana que se elimine.

Las lineas 18-76 definen el constructor de la claseTable __init__ () . Crea una ventana (linea 20), le pone eltítulo (linea 23), conecta la retrollamadadelete_event () a la señal "delete_event" (linea 27), y le pone el anchoal borde (linea 30). Se crea unagtk.Table en la linea 33 y se añade a la ventana en la linea 36.

Los dos botones superiores se crean (lineas 39 y 53), sus señales "clicked" se conectan al métodocallback ()(lineas 43 y 57), y se añaden a la tabla en la primera fila (lineas 47 y 59). Las lineas 64-71 crean el botón "Quit",conectan su señal "clicked" a la funciónmainquit () y lo añaden a la tabla ocupando la fila inferior completa.

30

Page 39: Tutorial Pygtk

Capítulo 5. Perspectiva General deControlesLos pasos generales para usar un control (widget) en PyGTK son:

• Llama a gtk.* - una de las múltiples funciones para crear un nuevo control. Estas funciones se detallan en estasección.

• Conecta todas la señales y eventos que queramos usar a los manejadores apropiados.

• Establece los atributos del control.

• Empaqueta el control dentro de un contenedor usando una llamada como gtk.Container.add() ogtk.Box.pack_start().

• Llama a gtk.Widget.show() en el control.

show() le permite saber a GTK que hemos terminado de configurar los atributos del control, y esta listo para sermostrado. También puedes usargtk.Widget.hide () para que desaparezca otra vez. El orden en el que muestraslos controles no es importante, pero te sugiero que muestres la ventana al final para que la ventana entera aparezcade una vez en vez de ver como los controles individuales van apareciendo en la ventana conforme se van formando.Los hijos de un control (una ventana también es un control) no se mostrarán hasta que la propia ventana se muestreusando el métodoshow() .

5.1. Jerarquía de ControlesAqui tienes el árbol de la jerarquía utilizada para implementar los controles para tu referencia. (Los controlesobsoletos y las clases auxiliares se han omitido.)

gobject.GObject|gtk.Object (Objeto)+gtk.Widget (Control)| +gtk.Misc (Miscélaneo)| | +gtk.Label (Etiqueta)| | | ‘gtk.AccelLabel (EtiquetaAceleradora)| | +gtk.Arrow (Flecha)| | ‘gtk.Image (Imagen)| +gtk.Container (Contenedor)| | +gtk.Bin (Bin?)| | | +gtk.Alignment (Alineador)| | | +gtk.Frame (Marco)| | | | ‘gtk.AspectFrame (Marco Proporcional)| | | +gtk.Button (Botón)| | | | +gtk.ToggleButton (Botón Biestado)| | | | | ‘gtk.CheckButton (Botón Activación)| | | | | ‘gtk.RadioButton (Botón Exclusión Mútua)| | | | ‘gtk.OptionMenu (Menú Opciones)| | | +gtk.Item (Elemento)| | | | +gtk.MenuItem (Elemento de Menú)| | | | +gtk.CheckMenuItem (Elemento Activable de Menú)| | | | | ‘gtk.RadioMenuItem (Elemento de Exclusión Mútua de Menú)| | | | +gtk.ImageMenuItem (Elemento de Imagen de Menú)

31

Page 40: Tutorial Pygtk

Capítulo 5. Perspectiva General deControles

| | | | ‘gtk.TearoffMenuItem (Menú Desprendible)| | | +gtk.Window (Ventana)| | | | +gtk.Dialog (Diálogo)| | | | | +gtk.ColorSelectionDialog (Diálogo de Selección de Colores)| | | | | +gtk.FileSelection (Selector de Ficheros)| | | | | +gtk.FontSelectionDialog (Diálogo de Selección de Tipos de Letra)| | | | | +gtk.InputDialog (Diálogo de Entrada de Datos)| | | | | ‘gtk.MessageDialog (Diálogo de Mensaje)| | | | ‘gtk.Plug (Conectable)| | | +gtk.EventBox (Caja de Eventos)| | | +gtk.HandleBox (Manejador de Caja)| | | +gtk.ScrolledWindow (Ventana de Desplazamiento)| | | ‘gtk.Viewport (Puerto)| | +gtk.Box (Caja)| | | +gtk.ButtonBox (Caja de Botones)| | | | +gtk.HButtonBox (Caja de Botones Horizontal)| | | | ‘gtk.VButtonBox (Caja de Botones Vertical)| | | +gtk.VBox (Caja Vertical)| | | | +gtk.ColorSelection (Selector de Colores)| | | | +gtk.FontSelection (Selector de Tipos de Letra)| | | | ‘gtk.GammaCurve (Curva Gamma)| | | ‘gtk.HBox (Caja Horizontal)| | | +gtk.Combo (Lista Desplegable)| | | ‘gtk.Statusbar (Barra de Estado)| | +gtk.Fixed (Fijo)| | +gtk.Paned (Panel)| | | +gtk.HPaned (Panel Horizontal)| | | ‘gtk.VPaned (Panel Vertical)| | +gtk.Layout (Disposición)| | +gtk.MenuShell (Consola de Menú)| | | +gtk.MenuBar (Barra de Menú)| | | ‘gtk.Menu (Menú)| | +gtk.Notebook (Cuaderno de Fichas)| | +gtk.Socket (Socket)| | +gtk.Table (Tabla)| | +gtk.TextView (Vista de Texto)| | +gtk.Toolbar (Barra de Herramientas)| | ‘gtk.TreeView (Vista de Árbol)| +gtk.Calendar (Calendario)| +gtk.DrawingArea (Área de Dibujo)| | ‘gtk.Curve (Curva)| +gtk.Editable (Editable)| | +gtk.Entry (Entrada de Texto)| | | ‘gtk.SpinButton (Botón Aumentar/Disminuir)| +gtk.Ruler (Regla)| | +gtk.HRuler (Regla Horizontal)| | ‘gtk.VRuler (Regla Vertical)| +gtk.Range (Rango)| | +gtk.Scale (Escala)| | | +gtk.HScale (Escala Horizontal)| | | ‘gtk.VScale (Escala Vertical)| | ‘gtk.Scrollbar (Barra de Desplazamiento)| | +gtk.HScrollbar (Barra de Desplazamiento Horizontal)| | ‘gtk.VScrollbar (Barra de Desplazamiento Vertical)| +gtk.Separator (Separador)| | +gtk.HSeparator (Separador Horizontal)| | ‘gtk.VSeparator (Separador Vertical)| +gtk.Invisible (Invisible)| +gtk.Preview (Previsualización)

32

Page 41: Tutorial Pygtk

Capítulo 5. Perspectiva General deControles

| ‘gtk.ProgressBar (Barra de Progreso)| +gtk.Adjustment (Ajuste)+gtk.CellRenderer (Visualizador de Celda)| +gtk.CellRendererPixbuf (Visualizador de Imágen de Celda)| +gtk.CellRendererText (Visualizador de Texto de Celda)| +gtk.CellRendererToggle (Visualizador de Activación de Celda)+gtk.ItemFactory (Fábrica de Elementos)+gtk.Tooltips (Pistas)‘gtk.TreeViewColumn (Columna de Vista de Árbol)

5.2. Controles sin VentanaLos siguientes controles no tienen ninguna ventana asociada. Si quieres caputar eventos, tendrás que usarEventBox . Mira la sección del controlEventBox .

gtk.Alignment (Alineador)gtk.Arrow (Flecha)gtk.Bin (Bin)gtk.Box (Caja)gtk.Button (Botón)gtk.CheckButton (Botón de Activación)gtk.Fixed (Fijo)gtk.Image (Imágen)gtk.Label (Etiqueta)gtk.MenuItem (Elemento de Menú)gtk.Notebook (Cuaderno de Fichas)gtk.Paned (Panel)gtk.RadioButton (Botón de Exclusión Mútua)gtk.Range (Rango)gtk.ScrolledWindow (Ventana de Desplazamiento)gtk.Separator (Separador)gtk.Table (Tabla)gtk.Toolbar (Barra de Herramientas)gtk.AspectFrame (Marco de Aspecto)gtk.Frame (Marco)gtk.VBox (Caja Vertical)gtk.HBox (Caja Horizontal)gtk.VSeparator (Separador Vertical)gtk.HSeparator (Separador Horizontal)

Seguiremos nuestra exploración de PyGTK examinando cada control, creando programas de ejemplo simplesque los muestren. Otra buena fuente es el programatestgtk.py que viene con PyGTK. Se puede encontrar enexamples/testgtk/testgtk.py dentro del directorio con las fuentes de PyGTK.

33

Page 42: Tutorial Pygtk

Capítulo 6. El Control de Botón6.1. Botones NormalesYa hemos visto casi todo lo que hay que ver sobre el control de botón. Es bastante sencillo. Puedes usar la funcióngtk.Button () para crear un botón con una etiqueta pasándole un parámetro de cadena. Después depende de ti elempaquetar una etiqueta o un pixmap en este nuevo botón. Para hacer esto, crea una nueva caja, y después colocatus objetos en esta caja usando el típico pack_start(), y después usaadd () para colocar la caja dentro del botón.

La función para crear un botón es:

button = gtk.Button ( label =None, stock =None)

si se especifica una etiqueta esta se usa como texto del botón.Si se especifica stock este se usa para elegir un iconode stock y una etiqueta para el botón. Los elementos de stock son:

STOCK_DIALOG_INFOSTOCK_DIALOG_WARNINGSTOCK_DIALOG_ERRORSTOCK_DIALOG_QUESTIONSTOCK_DNDSTOCK_DND_MULTIPLESTOCK_ADDSTOCK_APPLYSTOCK_BOLDSTOCK_CANCELSTOCK_CDROMSTOCK_CLEARSTOCK_CLOSESTOCK_CONVERTSTOCK_COPYSTOCK_CUTSTOCK_DELETESTOCK_EXECUTESTOCK_FINDSTOCK_FIND_AND_REPLACESTOCK_FLOPPYSTOCK_GOTO_BOTTOMSTOCK_GOTO_FIRSTSTOCK_GOTO_LASTSTOCK_GOTO_TOPSTOCK_GO_BACKSTOCK_GO_DOWNSTOCK_GO_FORWARDSTOCK_GO_UPSTOCK_HELPSTOCK_HOMESTOCK_INDEXSTOCK_ITALICSTOCK_JUMP_TOSTOCK_JUSTIFY_CENTERSTOCK_JUSTIFY_FILLSTOCK_JUSTIFY_LEFTSTOCK_JUSTIFY_RIGHT

34

Page 43: Tutorial Pygtk

Capítulo 6. El Control de Botón

STOCK_MISSING_IMAGESTOCK_NEWSTOCK_NOSTOCK_OKSTOCK_OPENSTOCK_PASTESTOCK_PREFERENCESSTOCK_PRINTSTOCK_PRINT_PREVIEWSTOCK_PROPERTIESSTOCK_QUITSTOCK_REDOSTOCK_REFRESHSTOCK_REMOVESTOCK_REVERT_TO_SAVEDSTOCK_SAVESTOCK_SAVE_ASSTOCK_SELECT_COLORSTOCK_SELECT_FONTSTOCK_SORT_ASCENDINGSTOCK_SORT_DESCENDINGSTOCK_SPELL_CHECKSTOCK_STOPSTOCK_STRIKETHROUGHSTOCK_UNDELETESTOCK_UNDERLINESTOCK_UNDOSTOCK_YESSTOCK_ZOOM_100STOCK_ZOOM_FITSTOCK_ZOOM_INSTOCK_ZOOM_OUT

El programa de ejemplobuttons.py [examples/buttons.py] proporciona un ejemplo del uso degtk.Button ()para crear un botón con una imagen y una etiqueta dentro de él. He separado el código para crear una caja delresto para que puedas usarlo en tus programas. Hay más ejemplos del uso de imágenes más tarde en el tutorial.La figura Figura 6.1. Botón con Pixmap y Etiqueta muestra la ventana conteniendo un botón con un imágen y unaetiqueta:

Figura 6.1. Botón con Pixmap y Etiqueta

El código fuente del programabuttons.py [examples/buttons.py] es:

1 #!/usr/bin/env python23 # example-start buttons buttons.c45 import gtk

35

Page 44: Tutorial Pygtk

Capítulo 6. El Control de Botón

67 # Create a new hbox with an image and a label packed into it8 # and return the box.9

10 def xpm_label_box(parent, xpm_filename, label_text):11 # Create box for xpm and label12 box1 = gtk.HBox(gtk.FALSE, 0)13 box1.set_border_width(2)1415 # Get the style of the button to get the16 # background color.17 style = parent.get_style()1819 # Now on to the image stuff20 image = gtk.Image()21 image.set_from_file(xpm_filename)2223 # Create a label for the button24 label = gtk.Label(label_text)2526 # Pack the pixmap and label into the box27 box1.pack_start(image, gtk.FALSE, gtk.FALSE, 3)28 box1.pack_start(label, gtk.FALSE, gtk.FALSE, 3)2930 image.show()31 label.show()32 return box13334 class Buttons:35 # Our usual callback method36 def callback(self, widget, data=None):37 print "Hello again - %s was pressed" % data3839 def __init__(self):40 # Create a new window41 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)4243 self.window.set_title("Image’d Buttons!")4445 # It’s a good idea to do this for all windows.46 self.window.connect("destroy", lambda wid: gtk.main_quit())47 self.window.connect("delete_event", lambda a1,a2:gtk.main_quit())4849 # Sets the border width of the window.50 self.window.set_border_width(10)5152 # Create a new button53 button = gtk.Button()5455 # Connect the "clicked" signal of the button to our callback56 button.connect("clicked", self.callback, "cool button")5758 # This calls our box creating function59 box1 = xpm_label_box(self.window, "info.xpm", "cool button")6061 # Pack and show all our widgets62 button.add(box1)6364 box1.show()

36

Page 45: Tutorial Pygtk

Capítulo 6. El Control de Botón

65 button.show()6667 self.window.add(button)68 self.window.show()6970 def main():71 gtk.main()72 return 07374 if __name__ == "__main__":75 Buttons()76 main()

Las lineas 10-32 definen la función de ayudaxpm_label_box () que crea una caja horizontal con un borde deancho 2 (lineas 12-13) y le pone una imagen (lineas 20-21) y una etiqueta (linea 24).

Las lineas 34-68 definen la claseButtons Las lineas 39-68 definen el método de inicialización de instancia quecrea una ventana (linea 41), le pone el título (linea 43), le conecta las señales "delete_event" y "destroy" (lineas46-47). La linea 53 crea el botón sin etiqueta. Su señal "clicked" se conecta al métodocallback () en la linea56. La funciónxpm_label_box () se llama en la linea 59 para crear la imagen y la etiqueta que se pondrán en elbotón en la linea 62.

La funciónxpm_label_box () podría usarse para colocar archivos xpm y etiquetas en cualquier control que puedaser un contenedor.

El control Botón tiene las siguientes señales:

pressed - se emite cuando el botón del puntero se presiona en el control Botón

released - se emite cuando el botón del puntero se suelta en el control Botón

clicked - se emite cuando el botón del puntero se presiona y luego sesuelta sobre el control Botón

enter - se emite cuando el puntero entra en el control Botón

leave - se emite cuando el puntero sale del control Botón

6.2. Botones BiestadoLos Botones Biestado se derivan de los botones normales y son muy similares, excepto que siempre están en unode dos estados, que cambia con un clic. Puedan estar presionados, y cuando vuelvas a hacer clic, volverán a suestado inicial, levantados. Haz clic otra vez, y volverán a estar presionados.

Los botones Biestado son la base para los botones de activación y los botones de exclusión mútua, y por eso,muchas de las llamadas usadas con los botones biestado son heredados por los botones de activación y los botonesde exclusión mútua. Volveré a destacar este hecho cuando tratemos dichos botones.

Creando un nuevo botón biestado:

toggle_button = gtk.ToggleButton( label =None)

37

Page 46: Tutorial Pygtk

Capítulo 6. El Control de Botón

Como puedes imaginar, estas llamadas funcionan igual que las llamadas al control de botón normal. Si no seespecifica etiqueta el botón estará vacio. El texto de la etiqueta se analiza para comprobar si contiene caracteresmemotécnicos con prefijo ’_’

Para obtener el estado de un botón biestado, incluyendo los botones de exclusión mútua y los botones de activación,utilizamos el mecanismo del ejemplo anterior. Asi comprobamos el estado del biestado, llamando al métodoget_active () del objeto botón biestado. La señal que nos interesa que emiten los botones biestado (el botónbiestado, el botón de activación y el botón de exclusión mútua) es la señal ’toggled’. Para comprobar el estado deestos botones, configura un manejador de señales para capturar la señal toggled, y accede a los atributos del objetopara determinar su estado. La retrollamada será parecida a:

def toggle_button_callback(widget, data):if widget.get_active():

# Si estamos aqui, el botón biestado está pulsadoelse:

# Si estamos aqui, el botón biestado está levantado

Para forzar el estado de un botón biestado, y sus hijos, el botón de exclusión mútua y el botón de activación, utilizaeste método:

toggle_button.set_active( is_active )

El método anterior puede usarse para forzar el estado del botón biestado, y sus hijos los botones de activación yde exclusión mútua. Especificando un argumento TRUE o FALSE para el parámetrois_active indicamos si elbotón debería estar pulsado o levantado. Cuando el botón biesado se crea su valor predeterminado es levantado oFALSE.

Fíjate que cuando usas el métodoset_active (), y cambias el estado realmente, esto causa que las señales"clicked" y "toggled" se emitan desde el botón.

toggle_button.get_active()

Este método devuelve el estado actual del botón biestado como un valor booleano TRUE/FALSE.

El programatogglebutton.py [examples/togglebutton.py] proporciona un ejemplo simple del uso de botonesbiestado. La figura Figura 6.2. Ejemplo de Botón Biestado ilustra la ventana resultante con el segundo botónbiestado activo:

Figura 6.2. Ejemplo de Botón Biestado

38

Page 47: Tutorial Pygtk

Capítulo 6. El Control de Botón

El código fuente del programa es:

1 #!/usr/bin/env python23 # example togglebutton.py45 import gtk67 class ToggleButton:8 # Our callback.9 # The data passed to this method is printed to stdout

10 def callback(self, widget, data=None):11 print "%s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()])1213 # This callback quits the program14 def delete_event(self, widget, event, data=None):15 gtk.main_quit()16 return gtk.FALSE1718 def __init__(self):19 # Create a new window20 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)2122 # Set the window title23 self.window.set_title("Toggle Button")2425 # Set a handler for delete_event that immediately26 # exits GTK.27 self.window.connect("delete_event", self.delete_event)2829 # Sets the border width of the window.30 self.window.set_border_width(20)3132 # Create a vertical box33 vbox = gtk.VBox(gtk.TRUE, 2)3435 # Put the vbox in the main window36 self.window.add(vbox)3738 # Create first button39 button = gtk.ToggleButton("toggle button 1")4041 # When the button is toggled, we call the "callback" method42 # with a pointer to "button" as its argument43 button.connect("toggled", self.callback, "toggle button 1")444546 # Insert button 1 into the upper left quadrant of the table47 vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 2)4849 button.show()5051 # Create second button5253 button = gtk.ToggleButton("toggle button 2")5455 # When the button is toggled, we call the "callback" method56 # with a pointer to "button 2" as its argument

39

Page 48: Tutorial Pygtk

Capítulo 6. El Control de Botón

57 button.connect("toggled", self.callback, "toggle button 2")58 # Insert button 2 into the upper right quadrant of the table59 vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 2)6061 button.show()6263 # Create "Quit" button64 button = gtk.Button("Quit")6566 # When the button is clicked, we call the mainquit function67 # and the program exits68 button.connect("clicked", lambda wid: gtk.main_quit())6970 # Insert the quit button into the both lower quadrants of the table71 vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 2)7273 button.show()74 vbox.show()75 self.window.show()7677 def main():78 gtk.main()79 return 08081 if __name__ == "__main__":82 ToggleButton()83 main()

Las lineas interesantes son la 10-11, que definen el métodocallback () que imprime la etiqueta del botón biestadoy su estado cuando es activado. Las lineas 43 y 57 conectan la señal "toggled" de los botones biestado al métodocallback ().

6.3. Botones de ActivaciónLos botones de activación heredan muchas propiedades y métodos de los botones biestado vistos anteriormente,pero su apariencia es un poco diferente. En vez de ser botones con texto dentro de ellos, son pequeñas cajas conun texto a su derecha. Normalmente se utilizan para opciones que pueden estar activadas o desactivadas en lasaplicaciones.

El método de creación es similar que el de los botones normales.

check_button = gtk.CheckButton( label =None)

Si el argumentolabel se especifica el método crea un botón de activación con una etiqueta a su lado. El texto dela etiqueta se analiza para comprobar si contiene caracteres memotécnicos con prefijo ’_’

Ver y modificar el estado de un botón de activación es idéntico que en un botón biestado.

El programacheckbutton.py [examples/checkbutton.py] proporciona un ejemplo del uso de los botones deactivación. La figura Figura 6.3. Ejemplo de Botón de Activación ilustra la ventana resultante:

40

Page 49: Tutorial Pygtk

Capítulo 6. El Control de Botón

Figura 6.3. Ejemplo de Botón de Activación

El código fuente del programacheckbutton.py [examples/checkbutton.py] es:

1 #!/usr/bin/env python23 # example checkbutton.py45 import gtk67 class CheckButton:8 # Our callback.9 # The data passed to this methods is printed to stdout

10 def callback(self, widget, data=None):11 print "%s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()]) 1213 # This callback quits the program14 def delete_event(self, widget, event, data=None):15 gtk.main_quit()16 return gtk.FALSE1718 def __init__(self):19 # Create a new window20 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)2122 # Set the window title23 self.window.set_title("Check Button")2425 # Set a handler for delete_event that immediately26 # exits GTK.27 self.window.connect("delete_event", self.delete_event)2829 # Sets the border width of the window.30 self.window.set_border_width(20)3132 # Create a vertical box33 vbox = gtk.VBox(gtk.TRUE, 2)3435 # Put the vbox in the main window36 self.window.add(vbox)3738 # Create first button39 button = gtk.CheckButton("check button 1")40

41

Page 50: Tutorial Pygtk

Capítulo 6. El Control de Botón

41 # When the button is toggled, we call the "callback" method42 # with a pointer to "button" as its argument43 button.connect("toggled", self.callback, "check button 1")444546 # Insert button 1 into the upper left quadrant of the table47 vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 2)4849 button.show()5051 # Create second button5253 button = gtk.CheckButton("check button 2")5455 # When the button is toggled, we call the "callback" method56 # with a pointer to "button 2" as its argument57 button.connect("toggled", self.callback, "check button 2")58 # Insert button 2 into the upper right quadrant of the table59 vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 2)6061 button.show()6263 # Create "Quit" button64 button = gtk.Button("Quit")6566 # When the button is clicked, we call the mainquit function67 # and the program exits68 button.connect("clicked", lambda wid: gtk.main_quit())6970 # Insert the quit button into the both lower quadrants of the table71 vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 2)7273 button.show()74 vbox.show()75 self.window.show()7677 def main():78 gtk.main()79 return 08081 if __name__ == "__main__":82 CheckButton()83 main()

6.4. Botones de Exclusión MútuaLos botones de exclusión mútua son similares a los botones de activación excepto que se agrupan de tal forma quesólo uno puede estar seleccionado/pulsado en un momento dado. Esto es bueno para sitios en los que tu aplicaciónnecesita seleccionar un valor de una pequeña lista de opciones.

Crear un nuevo botón de exclusión mútua se hace con la siguiente llamada:

radio_button = gtk.RadioButton( group =None, label =None)

42

Page 51: Tutorial Pygtk

Capítulo 6. El Control de Botón

Te habrás dado cuenta del argumento adicional de esta llamada. Los botones de exclusión mútua requieren ungrupo para funcionar correctamente.La primera llamada agtk.RadioButton () debe pasarleNone en el primerargumento y entonces se creará un nuevo grupo de botones de exclusión mútua con el nuevo botón de exclusiónmútua como su único miembro.

Para añadir más botones de exclusión mútua a un grupo, pasa una referencia a un botón de exclusión mútua en elgrupo en las llamadas posteriores agtk.RadioButton ().

Si se especifica un argumentolabel dicho texto se analizará para comprobar si contiene caracteres memotécnicoscon prefijo ’_’

También es una buena idea especificar explícitamente que botón será el que esté activado por defecto mediante:

radio_button.set_active( is_active )

Esto se describe en la sección de los botones biestado, y funciona exactamente de la misma forma. Una vez quelos botones de exclusión mútua se agrupan juntos, sólo uno del grupo puede estar activo al mismo tiempo. Si elusuario hace clic en un botón de exclusión mútua, y luego en otro, el primer botón de exclusión mútua emitirá unaseñal "toggled" (para informar de que va a estar activo), y luego el segundo botón emitirá su señal "toggled" (parainformar de que va a estar activo).

El programa de ejemploradiobuttons.py [examples/radiobuttons.py] crea un grupo de botones de exclusiónmútua con tres botones. La figura Figura 6.4. Ejemplo de Botones de Exclusión Mútua ilustra la ventanaresultante:

Figura 6.4. Ejemplo de Botones de Exclusión Mútua

The source code for the example program is:

1 #!/usr/bin/env python23 # example radiobuttons.py45 import gtk67 class RadioButtons:8 def callback(self, widget, data=None):

43

Page 52: Tutorial Pygtk

Capítulo 6. El Control de Botón

9 print "%s was toggled %s" % (data, ("OFF", "ON")[widget.get_active()])1011 def close_application(self, widget, event, data=None):12 gtk.main_quit()13 return gtk.FALSE1415 def __init__(self):16 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)1718 self.window.connect("delete_event", self.close_application)1920 self.window.set_title("radio buttons")21 self.window.set_border_width(0)2223 box1 = gtk.VBox(gtk.FALSE, 0)24 self.window.add(box1)25 box1.show()2627 box2 = gtk.VBox(gtk.FALSE, 10)28 box2.set_border_width(10)29 box1.pack_start(box2, gtk.TRUE, gtk.TRUE, 0)30 box2.show()3132 button = gtk.RadioButton(None, "radio button1")33 button.connect("toggled", self.callback, "radio button 1")34 box2.pack_start(button, gtk.TRUE, gtk.TRUE, 0)35 button.show()3637 button = gtk.RadioButton(button, "radio button2")38 button.connect("toggled", self.callback, "radio button 2")39 button.set_active(gtk.TRUE)40 box2.pack_start(button, gtk.TRUE, gtk.TRUE, 0)41 button.show()4243 button = gtk.RadioButton(button, "radio button3")44 button.connect("toggled", self.callback, "radio button 3")45 box2.pack_start(button, gtk.TRUE, gtk.TRUE, 0)46 button.show()4748 separator = gtk.HSeparator()49 box1.pack_start(separator, gtk.FALSE, gtk.TRUE, 0)50 separator.show()5152 box2 = gtk.VBox(gtk.FALSE, 10)53 box2.set_border_width(10)54 box1.pack_start(box2, gtk.FALSE, gtk.TRUE, 0)55 box2.show()5657 button = gtk.Button("close")58 button.connect_object("clicked", self.close_application, self.window,59 None)60 box2.pack_start(button, gtk.TRUE, gtk.TRUE, 0)61 button.set_flags(gtk.CAN_DEFAULT)62 button.grab_default()63 button.show()64 self.window.show()6566 def main():67 gtk.main()

44

Page 53: Tutorial Pygtk

Capítulo 6. El Control de Botón

68 return 06970 if __name__ == "__main__":71 RadioButtons()72 main()

El código es bastante simple de seguir. Las lineas 61-62 hacen que el botón "close" sea el control por defecto paraque al pulsar la tecla "Enter" cuando la ventana está activa el botón "close" emitirá la señal "clicked".

45

Page 54: Tutorial Pygtk

Capítulo 7. AjustesGTK tiene varios controles que pueden ser ajustados visualmente por el usuario usando el ratón o el teclado, talescomo los controles de rango, descritos en la sección Controles de Rango. También hay unos cuantos controles quevisualizan una parte ajustable de un área de datos mayor, tales como el control de texto y el control de puerto.

Obviamente, una aplicación necesita ser capaz de reaccionar ante los cambios que el usuario realiza en loscontroles de rango. Una forma de hacer esto sería que cada control emitiera su propio tipo de señal cuandosu ajuste cambiara y, o bien pasa el nuevo valor al manejador de señal, o requiere que se mire dentro de laestructura de datos del control para ver el nuevo valor. Pero puede que también quieras conectar los ajustes devarios controles juntos, para que ajustando uno se ajusten los otros. El ejemplo más obvio de esto es conectar unabarra de desplazamiento a un puerto o a un área de texto desplazable. Si cada control tuviera su propia manerade manipular el valor del ajuste, entonces el programador tendría que escribir sus propios manejadores de señalespara traducir entre la salida de la señal de un control y la entrada del método de ajuste de otro control.

GTK arregla este problema usando el objetoAdjustment , que no es un control sino una manera de que loscontroles almacenen y pasen la información de ajuste de una forma abstracta y flexible. El uso más obvio deAdjustment es almacenar los parámetros de configuración y los valores de los controles de rango como lasbarras de desplazamiento y los controles de escala. Sin embargo, como la claseAdjustments deriva deObject ,también tiene unas características especiales más alla de ser estructuras de datos normales. La más importantees que pueden emitir señales, como los controles, y estas señales no sólo pueden ser usadas para permitir a tusprogramas reaccionar a la entrada de usuario en controles ajustables, sino que pueden propagar valores de ajustede una forma transparente entre controles ajustables.

Verás como los ajustes encajan entre sí cuando veas otros controles que los incorporan: Barras de Progreso,Puertos, Ventanas de Desplazamiento, y otros.

7.1. Crear un AjusteMuchos de los controles que usan ajustes lo hacen automáticamente, pero más tarde se mostrarán casos en los quepuedes necesitar crearlos por ti mismo. Puedes crear un ajuste usando:

adjustment = gtk.Adjustment( value =0, lower =0, upper =0, step_incr =0, page_incr =0,←↩page_size =0)

El argumentovalue es el valor inicial que quieres darle al ajuste, normalmente corresponde a la posición superioro la posición más a la izquierda de un control ajustable. El argumentolower especifica el valor más bajo quepuede tomar el ajuste. El argumentostep_incr especifica el incremento más pequeño de los dos incrementospor los que el usuario puede cambiar el valor, mientras que el argumentopage_incr es el más grande de losdos. El argumentopage_size normalmente se corresponde de alguna manera con el área visible de un controldesplazable. El argumentoupper se usa para representar la coordenada inferior o la más a la derecha en el hijode un control desplazable. Por tanto no es siempre el número más grande que el valor puede tomar, ya que elpage_size de tales controles normalmente es distinto de cero.

7.2. Usar los Ajustes de la Forma FácilLos controles ajustables pueden dividirse más o menos en aquellos que usan y requieren unidades específicas paraestos valores, y aquellos que los tratan como número arbitrarios. El grupo que trata los valores como númerosarbitrarios incluye los controles de rango (barras de desplazamiento y escalas, la barra de progreso y los botonesde aumentar/disminuir). Todos estos controles normalmente se ajustan directamente por el usuario con el ratón oel teclado. Tratarán los valores inferior y superior de un ajuste como un rango dentro del cual el usuario puedemanipular el valor del ajuste. Por defecto, solo modificarán el valor de un ajuste.

46

Page 55: Tutorial Pygtk

Capítulo 7. Ajustes

El otro grupo incluye el control de texto, el control de puerto, el control de lista compuesta y el control deventana de desplazamiento. Todos estos controles usan valores de píxeles para sus ajustes. Todos estos controlesnormalmente se ajustan indirectamente usando barras de desplazamiento. Aunque todos los controles que usanajustes pueden crear sus propios ajustes o usar los que les proporciones, normalmente querrás dejarles a ellosla tarea de crear sus propios ajustes. Normalmente, sobreescribirán todos los valores de los ajustes que lesproporciones, excepto el propio valor, pero los resultado son, en general, impredecibles (lo que significa quetendrás que leer el código fuente para descubrirlo, y puede ser diferente entre los controles).

Ahora, probablemente estes pensando, ya que los controles de texto y los puertos insisten en establecer todos losparámetros de sus ajustes excepto el valor, mientras que las barras de desplazamiento solo tocan el valor del ajuste,si compartes un objeto ajuste entre una barra de desplazamiento y un control de texto, al manipular la barra dedesplazamiento, ¿se ajustará automágicamente el control de texto? ¡Por supuesto que lo hará! Tal y como esto:

# crea sus propios ajustesviewport = gtk.Viewport()# usa los ajustes recién creados para la barra de desplazamiento tambiénvscrollbar = gtk.VScrollbar(viewport.get_vadjustment())

7.3. El Interior del AjusteVale, dirás, eso está bien, pero ¿qué pasa si quiero crear mis propios manejadores que respondan cuando elusuario ajuste un control de rango o un botón aumentar/disminuir, y cómo obtengo el valor de un ajuste en estosmanejadores? Para contestar a estas preguntas y a otras más, vamos a empezar mirando los atributos de la propiaclasegtk.Adjustment :

loweruppervaluestep_incrementpage_incrementpage_size

Seaadj una instancia de la clasegtk.Adjustment , cada uno de los atributos se obtienen o modifican usandoadj.lower , adj.value , etc.

Ya que, cuando pones el valor de un ajuste, generalmente quieres que el cambio afecte a todos los controles queusan este ajuste, PyGTK proporciona un método para hacer esto:

adjustment.set_value( value )

Como se ha dicho antes,Adjustment es una subclase deObject igual que los demás controles, y por tanto,es capaz de emitir señales. Esto es la causa, claro está, de por qué las actualizaciones ocurren automágicamentecuando compartes un objeto ajuste entre una barra de desplazamiento y otro control ajustable; todos los controlesajustables conectan manejadores de señales a la señal "value_changed" de sus ajustes, como podría hacerlo tuprograma. Aqui tienes la definición de la retrollamada de esta señal:

def value_changed(adjustment):

47

Page 56: Tutorial Pygtk

Capítulo 7. Ajustes

Los diversos controles que usan el objetoAdjustment emitirán esta señal en un ajuste siempre que cambiensu valor. Esto ocurre tanto cuando el usuario hace que el deslizador se mueva en un control de rango, comocuando el programa explícitamente cambia el valor con el métodoset_value (). Así, por ejemplo, si tienes uncontrol de escala, y quieres que cambie la rotación de una imagen siempre que su valor cambie, podrías crear unaretrollamada 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 camposupper (superior) o lower (inferior) de su ajuste,tal y como cuando un usario añade más texto al control de texto? En este caso, se emite la señal "changed", quees así:

def changed(adjustment):

Los controlesRange normalmente conectan un manejador para esta señal, el cual cambia su apariencia parareflejar el cambio - por ejemplo, el tamaño del deslizador de una barra de desplazamiento crecerá o encojerá enproporción inversa a la diferencia entre el valor superior e inferior de su ajuste.

Probablemente nunca tendrás que conectar un manejador a esta señal, a menos que estes escribiendo un nuevotipo de control de rango. En cualquier caso, si cambias alguno de los valores de unAdjustment directamente,deberías emitir esta señal para reconfigurar los controles que lo están usando, tal que así:

adjustment.emit("changed")

¡Ahora adelante y ajusta!

48

Page 57: Tutorial Pygtk

Capítulo 8. Controles de RangoLa categoría de los controles de rango incluye el famoso control de barra de desplazamiento y el menos comúncontrol de escala. Aunque estos dos tipos de controles se usan generalmente para propósitos diferentes, sonbastante similares en función e implementación. Todos los controles de rango comparten un conjunto de elementosgráficos, cada uno de los cuales tiene su propia ventana X y recibe eventos. Todos ellos contienen un canal y undeslizador (lo que a veces se ha llamado un "thumbwheel" (????) en otros entornos GUI). Arrastrar el deslizadorcon el puntero del ratón hace que se mueve hacia alante y hacia atrás dentro del canal, mientras que haciendo clicen el canal avanza el deslizador hacia la localización del clic, ya sea completamente, o con una cantidad designada,dependiendo del botón del ratón que se use.

Como se mencionó en Adjustments más arriba, todos los controles de rango están asociados con un objeto ajuste,a partir del cual se calcula la longitud del deslizador y su posición con respecto al canal. Cuando el usuariomanipula el deslizador, el control de rango cambiará el valor del ajuste.

8.1. Barras de DesplazamientoEstas son tus barras de desplazamiento estandares . Deberían usarse sólo para desplazar algún otro control, talcomo una lista, una caja de texto, o un puerto (y generalmente es más fácil de usar la ventana de desplazamientoen la mayoría de los casos). Para otros propósitos, deberías usar los controles de escala, ya que son más amigablesy tienen más características.

Hay tipos separados para ls barras de desplazamiento horizontales y verticales. No hay demasiado que decir sobreellos. Los puedes crear con los siguientes métodos:

hscrollbar = gtk.HSscrollbar( adjustment =None)

vscrollbar = gtk.VSscrollbar( adjustment =None)

y eso es todo. El argumentoadjustment puede ser o una referencia a unAdjustment existente, o nada, encuyo caso se creará uno. Especificar nada puede ser útil en el caso, en el que quieras pasar el ajuste recién creadoal constructor de algún otro control que será configurado por ti, tal como una caja de texto.

8.2. Controles de EscalaLos controlesScale (Escala) se usan para permitir al usuario seleccionar y manipular visualmente un valor dentrode un rango específico. Puedes querer usar un control de escala, por ejemplo, para ajustar el nivel de zoom en unaprevisualización de una imagen, o para controlar el brillo de un color, o para especificar el número de minutos deinactividad antes de que el protector de pantalla se active.

8.2.1. Crear un Control de Escala

Al igual que con las barras de desplazamiento, hay controles separados para los controles de escala horizontalesy verticales. (La mayoría de los programadres suelen usar los controles de escala horizontales.) Ya queesencialmente funcionan de la misma manera, no hay necesidad de tratarlos por separado aqui. Los siguientesmétodos crean controles de escala verticales y horizontales, respectivamente:

vscale = gtk.VScale( adjustment =None)

hscale = gtk.HScale( adjustment =None)

49

Page 58: Tutorial Pygtk

Capítulo 8. Controles de Rango

El argumentoadjustment puede ser o un ajuste que ya haya sido creado congtk.Adjustment () , o nada, encuyo caso se crea unAdjustment anónimo con todos sus valores puestos a 0.0 (lo cual no es muy útil en estecaso). Para evitarte confusiones, probablemente quieras crear tu ajuste con un valor depage_size de 0.0 paraque su valorupper realmente corresponda con el valor más alto que el usuario puede seleccionar. (Si ya estásrealmente confundido, lee la sección sobre Ajustes otra vez para una explicación de lo que hacen exactamente losajustes y como crearlos y manipularlos.)

8.2.2. Métodos y Señales (bueno, al menos métodos)

Los controles de escala pueden visualizar su valor como un número al lado del canal. El comportamiento pordefecto es mostrar el valor, pero puedes cambiarlo con este método:

scale.set_draw_value( draw_value )

Como habrás imaginado,draw_value es o TRUE o FALSE, con consecuencias predecibles para cualquiera delos dos.

El valor que muestra un control de escala se redondea a un valor decimal por defecto, tal y como se hace con elcampo valor en suAdjustment (Ajuste). Puedes cambiar esto con:

scale.set_digits( digits )

dondedigits es el número de posiciones decimales que quieres. Puedes poner el número que quieras, pero nose veran más de 13 posiciones en la pantalla.

Finalmente, el valor se puede mostrar en diferentes posiciones relativas al canal:

scale.set_value_pos( pos )

El argumentopos puede tomar uno de los siguientes valores:

POS_LEFTPOS_RIGHTPOS_TOPPOS_BOTTOM

Si pones el valor en un "lado" del canal (por ejemplo, en la parte de arriba o abajo de un control de escalahorizontal), entonces seguirá al deslizador arriba y abajo del canal.

8.3. Métodos Comunes de los RangosLa claseRange es bastante complicada internamente, pero, como todas las clases base de los controles, la mayoríade su complejidad solo te interesa si quieres trastear con ella. Además, la mayoría de los métodos y señales quedefine sólo son útiles al escribir controles derivados. Hay, en cualquier caso, unos cuantos métodos útiles quefuncionarán con todos los controles de rango.

8.3.1. Establecer la Política de Actualización

50

Page 59: Tutorial Pygtk

Capítulo 8. Controles de Rango

La "política de actualización" de un control de rango define en qué puntos de la interacción con el usuario secambiará el campo de valor de suAdjustment y emitirá la señal "value_changed" en esteAdjustment . Laspolíticas de actualización son:

UPDATE_CONTINUOUS Es es el valor predeterminado. La señal "value_changed" se emite contínuamente,por ejemplo, cada vez qeu el deslizador se mueve incluso en las cantidades masminúsculas.

UPDATE_DISCONTINUOUS La señal "value_changed" sólo se mite una vez que el deslizador ha parado demoverse y el usuario ha soltado el botón del ratón.

UPDATE_DELAYED La señal "value_changed" se emite cuando el usuario suelta el botón del ratón, o siel deslizador para de moverse durante un corto período de tiempo.

La política de actualización de un control de rango puede cambiarse con este método:

range.set_update_policy( policy )

8.3.2. Obtener y Cambiar Ajustes

Obtener y cambiar el ajuste de un control de rango sobre la marcha se puede hacer, predeciblemente, con:

adjustment = range.get_adjustment()

range.set_adjustment( adjustment )

El métodoget_adjustment () devuelve una referencia aladjustment que está conectado al rango.

El métodoset_adjustment () no hace absolutamente nada si le pasas eladjustment que elrange ya estéutilizando, da igual que le hayas cambiado alguno de sus cambios o no. Si le pasas un nuevoAdjustment , seperderá la referencia al antiguo si existía (posiblemente se destruirá), se conectarán las señales apropiadas al nuevo,y se recalculará el tamaño y/o posición del deslizador y se repintará si es necesario. Como se ha mencionado en lasección de ajustes, si deseas reutilizar el mismoAdjustment , cuando modifiques sus valores directamente, debesemitir la señal "changed" en él, como por ejemplo:

adjustment.emit("changed")

8.4. Atajos de Teclas y RatónTodos los controles de rango de GTK reaccionan a clics de ratón más o menos de la misma forma. Haciendo cliccon el botón-1 en el canal hace que el valorpage_increment del ajuste se añada o se reste a suvalue , y queel deslizador se mueva de forma acorde. Haciendo clic con el botón-2 en el canal hará que el deslizador salte alpunto donde se ha hecho clic. Haciendo clic con cualquier botón en las flechas de una barra de desplazamientoharán que su ajuste cambie tanto como diga su valorstep_increment de una vez.

Las barras de desplazamiento no pueden recibir el foco, por lo que no tienen atajos de teclado. Los atajos deteclado de otros controles de rango (que por supuesto sólo están ativos cuando el control tiene el foco) no sediferencian para los controles de rango horizontales y verticales.

51

Page 60: Tutorial Pygtk

Capítulo 8. Controles de Rango

Todos los controles de rango pueden manejarse con las teclas de flecha izquierda, flecha derecha, flecha arriba yflecha abajo , así como con las teclasPágina Anterior y Página Siguienteç . Las flechas mueven el deslizadoren cantidades iguales astep_increment , mientras quePágina Anterior y Página Siguientelo mueven encantidades depage_increment .

El usuario también puede mover el deslizador directamente a un extremo o al otro del canal usando el teclado.Esto se hace con las teclasInicio y Fin .

8.5. Ejemplo de Control de RangoEl programa de ejemplo (rangewidgets.py [examples/rangewidgets.py]) es una versión modificada del test"controles de rango" detestgtk.py. Básicamente muestra una ventana con tres controles de rango todos conectadosal mismo ajuste, y un par de controles para ajustar algunos de los parámetros mencionados más arriba y en lasección de ajustes, por lo que puedes ver como afectan a la manera en la que estos controles funcionan para elusuario. 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

52

Page 61: Tutorial Pygtk

Capítulo 8. Controles de Rango

El código fuente derangewidgets.py[examples/rangewidgets.py] es:

1 #!/usr/bin/env python23 # example rangewidgets.py45 import gtk67 # Convenience functions89 def make_menu_item(name, callback, data=None):

10 item = gtk.MenuItem(name)11 item.connect("activate", callback, data)12 item.show()13 return item1415 def scale_set_default_values(scale):16 scale.set_update_policy(gtk.UPDATE_CONTINUOUS)17 scale.set_digits(1)18 scale.set_value_pos(gtk.POS_TOP)19 scale.set_draw_value(gtk.TRUE)2021 class RangeWidgets:22 def cb_pos_menu_select(self, item, pos):23 # Set the value position on both scale widgets24 self.hscale.set_value_pos(pos)25 self.vscale.set_value_pos(pos)2627 def cb_update_menu_select(self, item, policy):28 # Set the update policy for both scale widgets29 self.hscale.set_update_policy(policy)30 self.vscale.set_update_policy(policy)3132 def cb_digits_scale(self, adj):33 # Set the number of decimal places to which adj->value is rounded34 self.hscale.set_digits(adj.value)35 self.vscale.set_digits(adj.value)3637 def cb_page_size(self, get, set):38 # Set the page size and page increment size of the sample39 # adjustment to the value specified by the "Page Size" scale40 set.page_size = get.value41 set.page_incr = get.value42 # Now emit the "changed" signal to reconfigure all the widgets that43 # are attached to this adjustment44 set.emit("changed")4546 def cb_draw_value(self, button):47 # Turn the value display on the scale widgets off or on depending48 # on the state of the checkbutton49 self.hscale.set_draw_value(button.get_active())50 self.vscale.set_draw_value(button.get_active())5152 # makes the sample window5354 def __init__(self):55 # Standard window-creating stuff56 self.window = gtk.Window (gtk.WINDOW_TOPLEVEL)

53

Page 62: Tutorial Pygtk

Capítulo 8. Controles de Rango

57 self.window.connect("destroy", gtk.mainquit)58 self.window.set_title("range controls")5960 box1 = gtk.VBox(gtk.FALSE, 0)61 self.window.add(box1)62 box1.show()6364 box2 = gtk.HBox(gtk.FALSE, 10)65 box2.set_border_width(10)66 box1.pack_start(box2, gtk.TRUE, gtk.TRUE, 0)67 box2.show()6869 # value, lower, upper, step_increment, page_increment, page_size70 # Note that the page_size value only makes a difference for71 # scrollbar widgets, and the highest value you’ll get is actually72 # (upper - page_size).73 adj1 = gtk.Adjustment(0.0, 0.0, 101.0, 0.1, 1.0, 1.0)7475 self.vscale = gtk.VScale(adj1)76 scale_set_default_values(self.vscale)77 box2.pack_start(self.vscale, gtk.TRUE, gtk.TRUE, 0)78 self.vscale.show()7980 box3 = gtk.VBox(gtk.FALSE, 10)81 box2.pack_start(box3, gtk.TRUE, gtk.TRUE, 0)82 box3.show()8384 # Reuse the same adjustment85 self.hscale = gtk.HScale(adj1)86 self.hscale.set_size_request(200, 30)87 scale_set_default_values(self.hscale)88 box3.pack_start(self.hscale, gtk.TRUE, gtk.TRUE, 0)89 self.hscale.show()9091 # Reuse the same adjustment again92 scrollbar = gtk.HScrollbar(adj1)93 # Notice how this causes the scales to always be updated94 # continuously when the scrollbar is moved95 scrollbar.set_update_policy(gtk.UPDATE_CONTINUOUS)96 box3.pack_start(scrollbar, gtk.TRUE, gtk.TRUE, 0)97 scrollbar.show()9899 box2 = gtk.HBox(gtk.FALSE, 10)

100 box2.set_border_width(10)101 box1.pack_start(box2, gtk.TRUE, gtk.TRUE, 0)102 box2.show()103104 # A checkbutton to control whether the value is displayed or not105 button = gtk.CheckButton("Display value on scale widgets")106 button.set_active(gtk.TRUE)107 button.connect("toggled", self.cb_draw_value)108 box2.pack_start(button, gtk.TRUE, gtk.TRUE, 0)109 button.show()110111 box2 = gtk.HBox(gtk.FALSE, 10)112 box2.set_border_width(10)113114 # An option menu to change the position of the value115 label = gtk.Label("Scale Value Position:")

54

Page 63: Tutorial Pygtk

Capítulo 8. Controles de Rango

116 box2.pack_start(label, gtk.FALSE, gtk.FALSE, 0)117 label.show()118119 opt = gtk.OptionMenu()120 menu = gtk.Menu()121122 item = make_menu_item ("Top", self.cb_pos_menu_select, gtk.POS_TOP)123 menu.append(item)124125 item = make_menu_item ("Bottom", self.cb_pos_menu_select,126 gtk.POS_BOTTOM)127 menu.append(item)128129 item = make_menu_item ("Left", self.cb_pos_menu_select, gtk.POS_LEFT)130 menu.append(item)131132 item = make_menu_item ("Right", self.cb_pos_menu_select, gtk.POS_RIGHT)133 menu.append(item)134135 opt.set_menu(menu)136 box2.pack_start(opt, gtk.TRUE, gtk.TRUE, 0)137 opt.show()138139 box1.pack_start(box2, gtk.TRUE, gtk.TRUE, 0)140 box2.show()141142 box2 = gtk.HBox(gtk.FALSE, 10)143 box2.set_border_width(10)144145 # Yet another option menu, this time for the update policy of the146 # scale widgets147 label = gtk.Label("Scale Update Policy:")148 box2.pack_start(label, gtk.FALSE, gtk.FALSE, 0)149 label.show()150151 opt = gtk.OptionMenu()152 menu = gtk.Menu()153154 item = make_menu_item("Continuous", self.cb_update_menu_select,155 gtk.UPDATE_CONTINUOUS)156 menu.append(item)157158 item = make_menu_item ("Discontinuous", self.cb_update_menu_select,159 gtk.UPDATE_DISCONTINUOUS)160 menu.append(item)161162 item = make_menu_item ("Delayed", self.cb_update_menu_select,163 gtk.UPDATE_DELAYED)164 menu.append(item)165166 opt.set_menu(menu)167 box2.pack_start(opt, gtk.TRUE, gtk.TRUE, 0)168 opt.show()169170 box1.pack_start(box2, gtk.TRUE, gtk.TRUE, 0)171 box2.show()172173 box2 = gtk.HBox(gtk.FALSE, 10)174 box2.set_border_width(10)

55

Page 64: Tutorial Pygtk

Capítulo 8. Controles de Rango

175176 # An HScale widget for adjusting the number of digits on the177 # sample scales.178 label = gtk.Label("Scale Digits:")179 box2.pack_start(label, gtk.FALSE, gtk.FALSE, 0)180 label.show()181182 adj2 = gtk.Adjustment(1.0, 0.0, 5.0, 1.0, 1.0, 0.0)183 adj2.connect("value_changed", self.cb_digits_scale)184 scale = gtk.HScale(adj2)185 scale.set_digits(0)186 box2.pack_start(scale, gtk.TRUE, gtk.TRUE, 0)187 scale.show()188189 box1.pack_start(box2, gtk.TRUE, gtk.TRUE, 0)190 box2.show()191192 box2 = gtk.HBox(gtk.FALSE, 10)193 box2.set_border_width(10)194195 # And, one last HScale widget for adjusting the page size of the196 # scrollbar.197 label = gtk.Label("Scrollbar Page Size:")198 box2.pack_start(label, gtk.FALSE, gtk.FALSE, 0)199 label.show()200201 adj2 = gtk.Adjustment(1.0, 1.0, 101.0, 1.0, 1.0, 0.0)202 adj2.connect("value_changed", self.cb_page_size, adj1)203 scale = gtk.HScale(adj2)204 scale.set_digits(0)205 box2.pack_start(scale, gtk.TRUE, gtk.TRUE, 0)206 scale.show()207208 box1.pack_start(box2, gtk.TRUE, gtk.TRUE, 0)209 box2.show()210211 separator = gtk.HSeparator()212 box1.pack_start(separator, gtk.FALSE, gtk.TRUE, 0)213 separator.show()214215 box2 = gtk.VBox(gtk.FALSE, 10)216 box2.set_border_width(10)217 box1.pack_start(box2, gtk.FALSE, gtk.TRUE, 0)218 box2.show()219220 button = gtk.Button("Quit")221 button.connect_object("clicked", gtk.mainquit, self.window)222 box2.pack_start(button, gtk.TRUE, gtk.TRUE, 0)223 button.set_flags(gtk.CAN_DEFAULT)224 button.grab_default()225 button.show()226 self.window.show()227228 def main():229 gtk.main()230 return 0231232 if __name__ == "__main__":233 RangeWidgets()

56

Page 65: Tutorial Pygtk

Capítulo 8. Controles de Rango

234 main()

Notarás que el programa no llama al métodoconnect () para el evento "delete_event", si sólo con la señaldestroy. Esto seguirá realizando la acción deseada porque un evento "delete_event" sin tratar resultará en unaseñal "destroy" mandada a la ventana.

57

Page 66: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles9.1. EtiquetasLas Labels (Etiquetas) se usan un montón en GTK, y son relativamente simples. LasLabels (Etiquetas) noemiten señales ya que no tienen ninguna ventana X asociada. Si necesitas capturar señales, o hacer recorte, ponlasdentro de un controlEventBox (Caja de Eventos) o de un controlButton (Botón).

Para crear una nueva etiqueta, usa:

label = gtk.Label( str )

El único argumento es la cadena de texto que quieres que la etiqueta visualice. Para cambiar el texto de la etiquetadespués de la creación, usa el método:

label.set_text( str )

label es la etiqueta que has creado previamente, ystr es la nueva cadena. El espacio que necesite la nuevacadena se ajustará automáticamente si es necesario. Puedes hacer etiquetas multilinea poniendo saltos de linea enla cadena de la etiqueta.

Para obtener la cadena actual, usa:

str = label.get_text()

label es la etiqueta que has creado, ystr es la cadena que devuelve. El texto de una etiqueta se puede justificarusando:

label.set_justify( jtype )

Los valores parajtype son:

JUSTIFY_LEFTJUSTIFY_RIGHTJUSTIFY_CENTER # valor predeterminadoJUSTIFY_FILL

El control de etiqueta también es capaz de partir el texto automáticamente. Esto se puede activar usando:

label.set_line_wrap( wrap )

El argumentowrap toma un valor TRUE o FALSE.

Si quieres la etiqueta subrayada, entonces puedes ponerle un patrón a la etiqueta:

58

Page 67: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

label.set_pattern( pattern )

El argumentopattern (patrón) indica cómo se verá el subrayadao. Consiste en una cadena de signos desubrayado y caracteres de espacio. Un signo de subrayado indica que el caracter correspondiente en la etiquetadebe estar subrayado. Por ejemplo, la cadena"__ __" subrayaría los primeros dos caracteres y loscaracteres octavo y noveno. Si sólo quieres un atajo subrayado ("mnemónico") en tu etiqueta, deberías usarset_text_with_mnemonic (str) , noset_pattern ().

El programa de ejemplolabel.py [examples/label.py] es un ejemplo corto para ilustrar estos métodos. Esteejemplo hace uso del controlFrame (Marco) para demostrar mejor los estilos de etiqueta. Puedes ignorar esto porahora ya que el controlFrame (Marco) se explica después.

En GTK+ 2.0, el texto de la etiqueta puede contener marcas para el tipo de letra y otros atributos del texto, y lasetiquetas pueden ser seleccionables (para copiar y pegar). Estas características 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

59

Page 68: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

El código fuente delabel.py [examples/label.py] es:

1 #!/usr/bin/env python23 # example label.py45 import gtk67 class Labels:8 def __init__(self):9 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)

10 self.window.connect("destroy", gtk.mainquit)1112 self.window.set_title("Label")13 vbox = gtk.VBox(gtk.FALSE, 5)14 hbox = gtk.HBox(gtk.FALSE, 5)15 self.window.add(hbox)16 hbox.pack_start(vbox, gtk.FALSE, gtk.FALSE, 0)17 self.window.set_border_width(5)1819 frame = gtk.Frame("Normal Label")20 label = gtk.Label("This is a Normal label")21 frame.add(label)22 vbox.pack_start(frame, gtk.FALSE, gtk.FALSE, 0)2324 frame = gtk.Frame("Multi-line Label")25 label = gtk.Label("This is a Multi-line label.\nSecond line\n"26 "Third line")27 frame.add(label)28 vbox.pack_start(frame, gtk.FALSE, gtk.FALSE, 0)2930 frame = gtk.Frame("Left Justified Label")31 label = gtk.Label("This is a Left-Justified\n"32 "Multi-line label.\nThird line")33 label.set_justify(gtk.JUSTIFY_LEFT)34 frame.add(label)35 vbox.pack_start(frame, gtk.FALSE, gtk.FALSE, 0)3637 frame = gtk.Frame("Right Justified Label")38 label = gtk.Label("This is a Right-Justified\nMulti-line label.\n"39 "Fourth line, (j/k)")40 label.set_justify(gtk.JUSTIFY_RIGHT)41 frame.add(label)42 vbox.pack_start(frame, gtk.FALSE, gtk.FALSE, 0)4344 vbox = gtk.VBox(gtk.FALSE, 5)45 hbox.pack_start(vbox, gtk.FALSE, gtk.FALSE, 0)46 frame = gtk.Frame("Line wrapped label")47 label = gtk.Label("This is an example of a line-wrapped label. It "48 "should not be taking up the entire "49 "width allocated to it, but automatically "50 "wraps the words to fit. "51 "The time has come, for all good men, to come to "52 "the aid of their party. "53 "The sixth sheik’s six sheep’s sick.\n"54 " It supports multiple paragraphs correctly, "55 "and correctly adds "56 "many extra spaces. ")

60

Page 69: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

57 label.set_line_wrap(gtk.TRUE)58 frame.add(label)59 vbox.pack_start(frame, gtk.FALSE, gtk.FALSE, 0)6061 frame = gtk.Frame("Filled, wrapped label")62 label = gtk.Label("This is an example of a line-wrapped, filled label. "63 "It should be taking "64 "up the entire width allocated to it. "65 "Here is a sentence to prove "66 "my point. Here is another sentence. "67 "Here comes the sun, do de do de do.\n"68 " This is a new paragraph.\n"69 " This is another newer, longer, better "70 "paragraph. It is coming to an end, "71 "unfortunately.")72 label.set_justify(gtk.JUSTIFY_FILL)73 label.set_line_wrap(gtk.TRUE)74 frame.add(label)75 vbox.pack_start(frame, gtk.FALSE, gtk.FALSE, 0)7677 frame = gtk.Frame("Underlined label")78 label = gtk.Label("This label is underlined!\n"79 "This one is underlined in quite a funky fashion")80 label.set_justify(gtk.JUSTIFY_LEFT)81 label.set_pattern(82 "_________________________ _ _________ _ ______ __ _______ ___")83 frame.add(label)84 vbox.pack_start(frame, gtk.FALSE, gtk.FALSE, 0)85 self.window.show_all ()8687 def main():88 gtk.main()89 return 09091 if __name__ == "__main__":92 Labels()93 main()

9.2. FlechasEl controlArrow (Flecha) dibuja la cabeza de una flecha, apuntando a un número de direcciones posibles y conun número de estilos posibles. Puede ser muy útil en un botón en muchas aplicaciones. Al igual que con el controlLabel (Etiqueta), no emite ninguna señal.

Sólo hay dos llamadas para manipular un controlArrow :

arrow = gtk.Arrow( arrow_type , shadow_type )

arrow.set( arrow_type , shadow_type )

La primera crea un control flechacon el tipo y apariencia indicados. La segunda permite cambiar estos valoresrespectivamente. El argumentoarrow_type puede tomar uno de lo siguientes valores:

ARROW_UP #(Arriba)

61

Page 70: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

ARROW_DOWN #(Abajo)ARROW_LEFT #(Izquierda)ARROW_RIGHT #(Derecha)

Estos valores obviamente indican la dirección hacia la que apunta la flecha. El argumentoshadow_type puedetomar uno de los siguientes valores:

SHADOW_INSHADOW_OUT # valor predeterminadoSHADOW_ETCHED_INSHADOW_ETCHED_OUT

El programa de ejemploarrow.py [examples/arrow.py] ilustra brevemente su uso. La figura Figura 9.2. Ejemplosde Botones con Flechas muestra el resultado de ejecutar el programa:

Figura 9.2. Ejemplos de Botones con Flechas

El código fuente del programaarrow.py [examples/arrow.py] es:

1 #!/usr/bin/env python23 # example arrow.py45 import gtk67 # Create an Arrow widget with the specified parameters8 # and pack it into a button9 def create_arrow_button(arrow_type, shadow_type):

10 button = gtk.Button();11 arrow = gtk.Arrow(arrow_type, shadow_type);12 button.add(arrow)13 button.show()14 arrow.show()15 return button1617 class Arrows:18 def __init__(self):19 # Create a new window20 window = gtk.Window(gtk.WINDOW_TOPLEVEL)2122 window.set_title("Arrow Buttons")2324 # It’s a good idea to do this for all windows.25 window.connect("destroy", gtk.mainquit)2627 # Sets the border width of the window.28 window.set_border_width(10)

62

Page 71: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

2930 # Create a box to hold the arrows/buttons31 box = gtk.HBox(gtk.FALSE, 0)32 box.set_border_width(2)33 window.add(box)3435 # Pack and show all our widgets36 box.show()3738 button = create_arrow_button(gtk.ARROW_UP, gtk.SHADOW_IN)39 box.pack_start(button, gtk.FALSE, gtk.FALSE, 3)4041 button = create_arrow_button(gtk.ARROW_DOWN, gtk.SHADOW_OUT)42 box.pack_start(button, gtk.FALSE, gtk.FALSE, 3)4344 button = create_arrow_button(gtk.ARROW_LEFT, gtk.SHADOW_ETCHED_IN)45 box.pack_start(button, gtk.FALSE, gtk.FALSE, 3)4647 button = create_arrow_button(gtk.ARROW_RIGHT, gtk.SHADOW_ETCHED_OUT)48 box.pack_start(button, gtk.FALSE, gtk.FALSE, 3)4950 window.show()5152 def main():53 gtk.main()54 return 05556 if __name__ == "__main__":57 Arrows()58 main()

9.3. El Objeto PistasLasTooltips (Pistas) son pequeñas cadenas de texto que aparecen cuando dejas el cursor sobre un botón u otrocontrol durante unos segundos.

Los controles que no reciben eventos (controles que no tienen su propia ventana) no funcionarán con las pistas.

La primera llamada que usarás creará una nueva pista. Sólo tienes que hacer esto una vez ya que el objeto quedevuelvegtk.Tooltips puede usarse para crear múltiples pistas.

tooltips = gtk.Tooltips()

Una vez que has creado una nueva pista, y el control que quieres que la use está preparado, simplemente usa estallamada para asociarlos:

tooltips.set_tip( widget , tip_text , tip_private =None)

El objeto tooltips es la pista que acabas de crear. El primero argumento (widget ) es el control que quieresque muestre la pista; el segundo argumento (tip_text ), el texto que quieres visualizar. El último argumento(tip_private ) es una cadena de texto que puede usarse como identificador.

63

Page 72: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

El programa de ejemplotooltip.py [examples/tooltip.py] modifica el programaarrow.py [examples/arrow.py]para añadir una pista a cada botón. La figura Figura 9.3. Ejemplo de Pistas ilustra la ventana resultante con lapista del segundo botón flecha activada:

Figura 9.3. Ejemplo de Pistas

El código fuente del programatooltip.py [examples/tooltip.py] es:

1 #!/usr/bin/env python23 # example tooltip.py45 import gtk67 # Create an Arrow widget with the specified parameters8 # and pack it into a button9 def create_arrow_button(arrow_type, shadow_type):

10 button = gtk.Button();11 arrow = gtk.Arrow(arrow_type, shadow_type);12 button.add(arrow)13 button.show()14 arrow.show()15 return button1617 class Tooltips:18 def __init__(self):19 # Create a new window20 window = gtk.Window(gtk.WINDOW_TOPLEVEL)2122 window.set_title("Tooltips")2324 # It’s a good idea to do this for all windows.25 window.connect("destroy", gtk.mainquit)2627 # Sets the border width of the window.28 window.set_border_width(10)2930 # Create a box to hold the arrows/buttons31 box = gtk.HBox(gtk.FALSE, 0)32 box.set_border_width(2)33 window.add(box)3435 # create a tooltips object36 self.tooltips = gtk.Tooltips()3738 # Pack and show all our widgets39 box.show()

64

Page 73: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

4041 button = create_arrow_button(gtk.ARROW_UP, gtk.SHADOW_IN)42 box.pack_start(button, gtk.FALSE, gtk.FALSE, 3)43 self.tooltips.set_tip(button, "SHADOW_IN")4445 button = create_arrow_button(gtk.ARROW_DOWN, gtk.SHADOW_OUT)46 box.pack_start(button, gtk.FALSE, gtk.FALSE, 3)47 self.tooltips.set_tip(button, "SHADOW_OUT")4849 button = create_arrow_button(gtk.ARROW_LEFT, gtk.SHADOW_ETCHED_IN)50 box.pack_start(button, gtk.FALSE, gtk.FALSE, 3)51 self.tooltips.set_tip(button, "SHADOW_ETCHED_IN")5253 button = create_arrow_button(gtk.ARROW_RIGHT, gtk.SHADOW_ETCHED_OUT)54 box.pack_start(button, gtk.FALSE, gtk.FALSE, 3)55 self.tooltips.set_tip(button, "SHADOW_ETCHED_OUT")5657 window.show()5859 def main():60 gtk.main()61 return 06263 if __name__ == "__main__":64 tt = Tooltips()65 main()

Hay otros métodos que pueden usarse con las pistas. Simplemente los listaré con una breve descripción sobre loque hacen.

tooltips.enable()

Activa un conjunto de pistas desactivadas.

tooltips.disable()

Desactiva un conjunto de pistas activadas.

tooltips.set_delay( delay )

Dice cuantos milisegundos tienes que mantener el puntero sobre el control antes de que la pista aparezca. El valorpredefinido es de 500 milisegundos (medio segundo).

Y esos son todos los métodos asociados con las pistas. Más de lo que siempre quisiste saber :-)

9.4. Barras de ProgresoLas barras de progreso se usan para mostrar el estado de una operación. Son bastante fáciles de usar, como verásen el código que sigue. Pero primero empecemos con una llamada para crar una nueva barra de progreso.

65

Page 74: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

progressbar = gtk.ProgressBar( adjustment =None)

El argumentoadjustment (ajuste) especifica un ajusta para usarlo con la barra de progreso. Si no se especificase creará un ajuste. Ahora la barra de progreso está creada y podemos usarla.

progressbar.set_fraction( fraction )

El objetoprogressbar es la barra de progreso con la que queremos operar, y el argumento (fraction ) es lacantidad "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 método como un número real entre 0 y 1.

Una barra de progreso puede orientarse de diversas formas usando el mmétodo:

progressbar.set_orientation( orientation )

El argumentoorientation puede tomar uno de los siguientes valores para indicar la dirección en la que la barrade progreso se mueve:

PROGRESS_LEFT_TO_RIGHT # izquierda a derechaPROGRESS_RIGHT_TO_LEFT # derecha a izquierdaPROGRESS_BOTTOM_TO_TOP # abajo a arribaPROGRESS_TOP_TO_BOTTOM # arriba a abajo

Además de indicar la cantidad de progreso que se ha completado, la barra de progreso también puede usarse paraindicar que ha habido alguna actividad. Esto puede ser útil en situaciones donde el progreso no se puede medircon un rango de valores. La siguiente función indica que se ha hecho algún progreso.

progressbar.pulse()

El tamaño de paso de un indicador de actividad se establece usando la siguiente función, donde la fracción es unnúmero entre 0.0 y 1.0.

progressbar.set_pulse_step( fraction )

Cuando no está en el modo actividad, la barra de progreso también puede mostrar una cadena de texto en su canal,usando el siguiente método:

progressbar.set_text( text )

Nota

Ten en cuenta queset_text () no soporta el formateo de texto al estiloprintf () como lo hacía la barrade progreso de GTK+ 1.2.

Puedes desactivar la visualización de la cadena llamando aset_text () otra vez sin argumentos.

66

Page 75: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

La cadena de texto actual de la barra de progreso se puede obtener con el siguiente método:

text = progressbar.get_text()

Normalmente las Barras de Progreso usan cronómetros u otras funciones parecidas (mira la sección sobreCronómetros, E/S y Funciones de Inactividad) para dar la ilusión de multitarea. Todas usarán los métodosset_fraction () o pulse () de la misma forma.

El programaprogressbar.py[examples/progressbar.py] proporciona un ejemplo de barra de progreso, actualizadausando cronómetros. Este código también 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

El código fuente del programaprogressbar.py[examples/progressbar.py] es:

1 #!/usr/bin/env python23 # example progressbar.py45 import gtk67 # Update the value of the progress bar so that we get8 # some movement9 def progress_timeout(pbobj):

10 if pbobj.activity_check.get_active():11 pbobj.pbar.pulse()12 else:13 # Calculate the value of the progress bar using the14 # value range set in the adjustment object15 new_val = pbobj.pbar.get_fraction() + 0.0116 if new_val > 1.0:17 new_val = 0.018 # Set the new value19 pbobj.pbar.set_fraction(new_val)

67

Page 76: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

2021 # As this is a timeout function, return TRUE so that it22 # continues to get called23 return gtk.TRUE2425 class ProgressBar:26 # Callback that toggles the text display within the progress27 # bar trough28 def toggle_show_text(self, widget, data=None):29 if widget.get_active():30 self.pbar.set_text("some text")31 else:32 self.pbar.set_text("")3334 # Callback that toggles the activity mode of the progress35 # bar36 def toggle_activity_mode(self, widget, data=None):37 if widget.get_active():38 self.pbar.pulse()39 else:40 self.pbar.set_fraction(0.0)4142 # Callback that toggles the orientation of the progress bar43 def toggle_orientation(self, widget, data=None):44 if self.pbar.get_orientation() == gtk.PROGRESS_LEFT_TO_RIGHT:45 self.pbar.set_orientation(gtk.PROGRESS_RIGHT_TO_LEFT)46 elif self.pbar.get_orientation() == gtk.PROGRESS_RIGHT_TO_LEFT:47 self.pbar.set_orientation(gtk.PROGRESS_LEFT_TO_RIGHT)4849 # Clean up allocated memory and remove the timer50 def destroy_progress(self, widget, data=None):51 gtk.timeout_remove(self.timer)52 self.timer = 053 gtk.mainquit()5455 def __init__(self):56 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)57 self.window.set_resizable(gtk.TRUE)5859 self.window.connect("destroy", self.destroy_progress)60 self.window.set_title("ProgressBar")61 self.window.set_border_width(0)6263 vbox = gtk.VBox(gtk.FALSE, 5)64 vbox.set_border_width(10)65 self.window.add(vbox)66 vbox.show()6768 # Create a centering alignment object69 align = gtk.Alignment(0.5, 0.5, 0, 0)70 vbox.pack_start(align, gtk.FALSE, gtk.FALSE, 5)71 align.show()7273 # Create the ProgressBar using the adjustment74 self.pbar = gtk.ProgressBar()7576 align.add(self.pbar)77 self.pbar.show()78

68

Page 77: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

79 # Add a timer callback to update the value of the progress bar80 self.timer = gtk.timeout_add (100, progress_timeout, self)8182 separator = gtk.HSeparator()83 vbox.pack_start(separator, gtk.FALSE, gtk.FALSE, 0)84 separator.show()8586 # rows, columns, homogeneous87 table = gtk.Table(2, 2, gtk.FALSE)88 vbox.pack_start(table, gtk.FALSE, gtk.TRUE, 0)89 table.show()9091 # Add a check button to select displaying of the trough text92 check = gtk.CheckButton("Show text")93 table.attach(check, 0, 1, 0, 1,94 gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL,95 5, 5)96 check.connect("clicked", self.toggle_show_text)97 check.show()9899 # Add a check button to toggle activity mode

100 self.activity_check = check = gtk.CheckButton("Activity mode")101 table.attach(check, 0, 1, 1, 2,102 gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL,103 5, 5)104 check.connect("clicked", self.toggle_activity_mode)105 check.show()106107 # Add a check button to toggle orientation108 check = gtk.CheckButton("Right to Left")109 table.attach(check, 0, 1, 2, 3,110 gtk.EXPAND | gtk.FILL, gtk.EXPAND | gtk.FILL,111 5, 5)112 check.connect("clicked", self.toggle_orientation)113 check.show()114115 # Add a button to exit the program116 button = gtk.Button("close")117 button.connect("clicked", self.destroy_progress)118 vbox.pack_start(button, gtk.FALSE, gtk.FALSE, 0)119120 # This makes it so the button is the default.121 button.set_flags(gtk.CAN_DEFAULT)122123 # This grabs this button to be the default button. Simply hitting124 # the "Enter" key will cause this button to activate.125 button.grab_default ()126 button.show()127128 self.window.show()129130 def main():131 gtk.main()132 return 0133134 if __name__ == "__main__":135 ProgressBar()136 main()

69

Page 78: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

9.5. DiálogosEl control Dialog (Diálogo) es muy simple, y realmente es sólo una ventana con unas cuantas cosas yaempaquetadas. Simplemente crea una ventana, y luego empaqueta unaVBox en ella, que contiene un separador yluego unaHBox llamada "action_area" ("área de acción").

El controlDialog (Diálogo) se puede usar para mensajes emergentes para el usuario, y otras tareas similares. Esrealmente básico, y sólo hay una función para la caja de diálogo, que es:

dialog = gtk.Dialog( title =None, parent =None, flags =0, buttons =None)

dondetitle (título) es el texto usado en la barra de título,parent (padre) es la ventana principal de la aplicacióny flags establecen varios modos de operación para el diálogo:

DIALOG_MODAL - hace el diálogo modalDIALOG_DESTROY_WITH_PARENT - destruye el diálogo cuando su padre transient (???) ←↩

sea destruidoDIALOG_NO_SEPARATOR - omite el separador entre la vbox y el área de acción

El argumentobuttons (botones) es una tupla de pares texto de botón y respuesta. Todos los argumentos tienenvalores predeterminados y pueden especificarse usando palabras clave.

Esto creará la caja de diálogo, y ahora depende de ti el usarla. Podrías empaquetar un botón en el área de acción:

button = ...dialog.action_area.pack_start(button, TRUE, TRUE, 0)button.show()

Y podrías añadir, por ejemplo una etiqueta, al áreavbox usando el empaqeutamiento, intenta algo así:

label = gtk.Label("Los diálogos molan")dialog.vbox.pack_start(label, TRUE, TRUE, 0)label.show()

Como ejemplo del uso de una caja de diálogo, podrías poner dos botones en el área de acción, un botón de Cancelary un botón de Aceptar, y una etiqueta en el áreavbox , preguntando al usuario una pregunta, o informando de unerror, etc. Luego podrías conectar diferentes señales a cada botón y realizar la operación que el usuario seleccione.

Si la funcionalidad básica que proporcionan las cajas verticales y horizontales predeterminadas no te dan elsuficiente control para tu aplicación, entonces puedes simplemente empaquetar otro control dentro de las cajasproporcionadas. Por ejemplo, podrías empaquetar una tabla dentro de la caja vertical.

9.6. ImágenesLas Images (Imágenes) son estructuras de datos que contienen dibujos. Estos dibujos se pueden usar en variossitios.

Las Images (Imágenes) se pueden crear a partir dePixbufs , Pixmaps , archivos que contengan información deimagen (por ejemplo. XPM, PNG, JPEG, TIFF, etc.), incluso ficheros de animación.

70

Page 79: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

Las Images (Imágenes) se crean usando la función:

image = gtk.Image()

Después se carga la imágen usando alguno de los siguientes métodos:

image.set_from_pixbuf( pixbuf )image.set_from_pixmap( pixmap , mask)image.set_from_image( image )image.set_from_file( filename )image.set_from_stock (stock_id , size )image.set_from_icon_set( icon_set , size )image.set_from_animation( animation )

Dondepixbuf es unGdkPixbuf ; pixmap y mask sonGdkPixmaps ; image es unaGdkImage ; stock_id es elnombre de unGtkStockItem ; icon_set es unGtkIconSet ; y, animation es unaGdkPixbufAnimation . elargumentosize (tamaño) es uno de:

ICON_SIZE_MENUICON_SIZE_SMALL_TOOLBARICON_SIZE_LARGE_TOOLBARICON_SIZE_BUTTONICON_SIZE_DNDICON_SIZE_DIALOG

La forma más fácil de crear una imagen es usar el métodoset_from_file () que automáticamente determina eltipo de imagen y la carga.

El programa images.py [examples/images.py] muestra cómo cargar varios tipos de imagen (goalie.gif

[examples/goalie.gif], apple-red.png [examples/apple-red.png], chaos.jpg [examples/chaos.jpg],important.tif [examples/important.tif], soccerball.gif [examples/soccerball.gif]) en imagenes quese colocan dentro de botones:

Figura 9.5. Ejemplo de Imágenes en Botones

71

Page 80: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

El código fuente es:

1 #!/usr/bin/env python23 # example images.py45 import gtk67 class ImagesExample:8 # when invoked (via signal delete_event), terminates the application.9 def close_application(self, widget, event, data=None):

10 gtk.mainquit()11 return gtk.FALSE1213 # is invoked when the button is clicked. It just prints a message.14 def button_clicked(self, widget, data=None):15 print "button %s clicked" % data1617 def __init__(self):18 # create the main window, and attach delete_event signal to terminating19 # the application20 window = gtk.Window(gtk.WINDOW_TOPLEVEL)21 window.connect("delete_event", self.close_application)22 window.set_border_width(10)23 window.show()2425 # a horizontal box to hold the buttons26 hbox = gtk.HBox()27 hbox.show()28 window.add(hbox)2930 pixbufanim = gtk.gdk.PixbufAnimation("goalie.gif")31 image = gtk.Image()32 image.set_from_animation(pixbufanim)33 image.show()34 # a button to contain the image widget35 button = gtk.Button()36 button.add(image)37 button.show()38 hbox.pack_start(button)39 button.connect("clicked", self.button_clicked, "1")4041 # create several images with data from files and load images into42 # buttons43 image = gtk.Image()44 image.set_from_file("apple-red.png")45 image.show()46 # a button to contain the image widget47 button = gtk.Button()48 button.add(image)49 button.show()50 hbox.pack_start(button)51 button.connect("clicked", self.button_clicked, "2")5253 image = gtk.Image()54 image.set_from_file("chaos.jpg")55 image.show()56 # a button to contain the image widget

72

Page 81: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

57 button = gtk.Button()58 button.add(image)59 button.show()60 hbox.pack_start(button)61 button.connect("clicked", self.button_clicked, "3")6263 image = gtk.Image()64 image.set_from_file("important.tif")65 image.show()66 # a button to contain the image widget67 button = gtk.Button()68 button.add(image)69 button.show()70 hbox.pack_start(button)71 button.connect("clicked", self.button_clicked, "4")7273 image = gtk.Image()74 image.set_from_file("soccerball.gif")75 image.show()76 # a button to contain the image widget77 button = gtk.Button()78 button.add(image)79 button.show()80 hbox.pack_start(button)81 button.connect("clicked", self.button_clicked, "5")828384 def main():85 gtk.main()86 return 08788 if __name__ == "__main__":89 ImagesExample()90 main()

9.6.1. Pixmaps

Los Pixmaps son estructuras de datos que contienen dibujos. Estos dibujos se pueden usar en varios sitios, perolo más común es usarlos como iconos en un escritorio X, o como cursores.

Un pixmap con sólo 2 colores se llama bitmap, y hay unas cuantas rutinas adicionales para trabajar con este casoespecial.

Para entender los pixmaps, es de ayuda entender cómo funciona el sistema X Window. En X, las aplicaciones nonecesitan ejecutarse en el mismo ordenador que interactua con el usuario. En cambio, estas aplicaciones, llamadas"clientes", se comunican con un programa que muestra los gráficos y maneja el teclado y el ratón. Este programaque interactua directamente con el usuario se llama un "servidor de visualización" o "servidor X". Ya que lacomunicación puede tener lugar sobre una red, es importante mantener alguna información en el servidor X. LosPixmaps , por ejemplo, se almacenan en la memoria del servidor X. Esto significa que una vez que los valoresde un pixmap se establecen, no hay que seguir transmitiendolos por la red; en lugar de eso, se envía un comandopara "mostrar el pixmap número XYZ aqui." Incluso si no estás usando X con GTK simultáneamente, usandoconstrucciones comoPixmaps hará que tus programas funcionen en X.

Para usar pixmaps en PyGTK, primero debemos construir unGdkPixmap usando las funciones de gtk.gdk enPyGTK. LosPixmaps se pueden crear a partir de datos en memoria, o a partir de datos leidos de un fichero.Veamos cada una de las llamadas usadas para crear un pixmap.

73

Page 82: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

pixmap = gtk.gdk.pixmap_create_from_data( window , data , width , height , fg , bg)

Esta rutina se usa para crear unpixmap con la profundidad de color dada por el argumentowindow a partir delos datosdata en memoria. Cada pixel usa un número de bits de datos para representar el color que es igual a laprofundidad de color. Elwidth (ancho) y elheight (alto) son en pixeles. El argumentowindow (ventana) debereferirse a unaGdkWindow realizada, ya que los recursos de un pixmap sólo tienen sentido en el contexto de lapantalla donde se va a visualizar.fg y bg son los colores de frente y fondo del pixmap.

Pixmaps can be created from XPM files using:

pixmap, mask = gtk.gdk.pixmap_create_from_xpm( window , transparent_color , filename )

El formato XPM es una representación legible de pixmap para el Sistema de Ventanas X. Es usado amplia-mente y hay muchas utilidades disponibles para crear ficheros de imágenes en este formato. En la funciónpixmap_create_from_xpm () el primer argumento es unGdkWindow . (La mayoría de los controles GTK tienenunaGdkWindow subyacente que se puede obtener usando el atributo window (ventana) del control.) El fichero seespecificar confilename que debe contener una imagen en formato XPM el cual se carga en la estructura delpixmap . La mask (máscara) es un bitmap que especifica que bits delpixmap son opacos; se crea con la función.Todos los demás pixeles se colorean con el color especificado portransparent_color . Un ejemplo del uso deesta función a continuación:

Los Pixmaps también puede crearse a partir de datos en memoria usando la función:

pixmap, mask = gtk.gdk.pixmap_create_from_xpm_d( window , transparent_color , data )

Imagenes pequeñas se pueden incorporar a un programa como datos en el formato XPM usando la función anterior.Un pixmap se crea usando estos datos, en lugar de leerlo de un fichero. Un ejemplo de este tipo de datos es:

xpm_data = ["16 16 3 1"," c None",". c #000000000000","X c #FFFFFFFFFFFF"," "," ...... "," .XXX.X. "," .XXX.XX. "," .XXX.XXX. "," .XXX..... "," .XXXXXXX. "," .XXXXXXX. "," .XXXXXXX. "," .XXXXXXX. "," .XXXXXXX. "," .XXXXXXX. "," .XXXXXXX. "," ......... "," "," "]

74

Page 83: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

La última forma para crear un pixmap en blanco disponible para operaciones de dibujo es:

pixmap = gtk.gdk.Pixmap( window , width , height , depth =-1)

window es o unaGdkWindow o None. Siwindow es unaGdkWindow entoncesdepth puede ser -1 para indicarque la profundidad se obtiene de la ventana. Siwindow es None entoncesdepth debe especificarse.

El programapixmap.py [examples/pixmap.py] es un ejemplo del uso de un pixmap en un botón. La figura Figura9.6. Ejemplo de Pixmap en un Botón muestra el resultado:

Figura 9.6. Ejemplo de Pixmap en un Botón

El código fuente es:

1 #!/usr/bin/env python23 # example pixmap.py45 import gtk67 # XPM data of Open-File icon8 xpm_data = [9 "16 16 3 1",

10 " c None",11 ". c #000000000000",12 "X c #FFFFFFFFFFFF",13 " ",14 " ...... ",15 " .XXX.X. ",16 " .XXX.XX. ",17 " .XXX.XXX. ",18 " .XXX..... ",19 " .XXXXXXX. ",20 " .XXXXXXX. ",21 " .XXXXXXX. ",22 " .XXXXXXX. ",23 " .XXXXXXX. ",24 " .XXXXXXX. ",25 " .XXXXXXX. ",26 " ......... ",27 " ",28 " "29 ]3031 class PixmapExample:32 # when invoked (via signal delete_event), terminates the application.33 def close_application(self, widget, event, data=None):34 gtk.mainquit()

75

Page 84: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

35 return gtk.FALSE3637 # is invoked when the button is clicked. It just prints a message.38 def button_clicked(self, widget, data=None):39 print "button clicked"4041 def __init__(self):42 # create the main window, and attach delete_event signal to terminating43 # the application44 window = gtk.Window(gtk.WINDOW_TOPLEVEL)45 window.connect("delete_event", self.close_application)46 window.set_border_width(10)47 window.show()4849 # now for the pixmap from XPM data50 pixmap, mask = gtk.gdk.pixmap_create_from_xpm_d(window.window,51 None,52 xpm_data)5354 # an image widget to contain the pixmap55 image = gtk.Image()56 image.set_from_pixmap(pixmap, mask)57 image.show()5859 # a button to contain the image widget60 button = gtk.Button()61 button.add(image)62 window.add(button)63 button.show()6465 button.connect("clicked", self.button_clicked)6667 def main():68 gtk.main()69 return 07071 if __name__ == "__main__":72 PixmapExample()73 main()

Una desventaja de usar pixmaps es que que el objeto mostrado siempre es rectangular, da igual la imagen. Nosgustaría crear escritorios y aplicaciones con iconos que tengan formas más naturales. Por ejemplo, para el interfazde un juego, nos gustaría 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 imágen de fondo se colorea, no la sobreescribimos con un borde rectangular y que no encaja, de nuestroicono. El programa de ejemplowheelbarrow.p [examples/wheelbarrow.p] muestra una imagen completa en elescritorio. La figura Figura 9.7. Ejemplo de Ventana con Forma muestra la imagen sobre una ventana de terminal:

76

Page 85: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

Figura 9.7. Ejemplo de Ventana con Forma

The source code for wheelbarrow.py is:

1 #!/usr/bin/env python23 # example wheelbarrow.py45 import gtk67 # XPM8 WheelbarrowFull_xpm = [9 "48 48 64 1",

10 " c None",11 ". c #DF7DCF3CC71B",12 "X c #965875D669A6",13 "o c #71C671C671C6",14 "O c #A699A289A699",15 "+ c #965892489658",16 "@ c #8E38410330C2",17 "# c #D75C7DF769A6",18 "$ c #F7DECF3CC71B",19 "% c #96588A288E38",20 "& c #A69992489E79",21 "* c #8E3886178E38",22 "= c #104008200820",23 "- c #596510401040",24 "; c #C71B30C230C2",25 ": c #C71B9A699658",26 "> c #618561856185",27 ", c #20811C712081",28 "< c #104000000000",29 "1 c #861720812081",30 "2 c #DF7D4D344103",31 "3 c #79E769A671C6",32 "4 c #861782078617",33 "5 c #41033CF34103",34 "6 c #000000000000",35 "7 c #49241C711040",36 "8 c #492445144924",37 "9 c #082008200820",38 "0 c #69A618611861",39 "q c #B6DA71C65144",40 "w c #410330C238E3",

77

Page 86: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

41 "e c #CF3CBAEAB6DA",42 "r c #71C6451430C2",43 "t c #EFBEDB6CD75C",44 "y c #28A208200820",45 "u c #186110401040",46 "i c #596528A21861",47 "p c #71C661855965",48 "a c #A69996589658",49 "s c #30C228A230C2",50 "d c #BEFBA289AEBA",51 "f c #596545145144",52 "g c #30C230C230C2",53 "h c #8E3882078617",54 "j c #208118612081",55 "k c #38E30C300820",56 "l c #30C2208128A2",57 "z c #38E328A238E3",58 "x c #514438E34924",59 "c c #618555555965",60 "v c #30C2208130C2",61 "b c #38E328A230C2",62 "n c #28A228A228A2",63 "m c #41032CB228A2",64 "M c #104010401040",65 "N c #492438E34103",66 "B c #28A2208128A2",67 "V c #A699596538E3",68 "C c #30C21C711040",69 "Z c #30C218611040",70 "A c #965865955965",71 "S c #618534D32081",72 "D c #38E31C711040",73 "F c #082000000820",74 " ",75 " .XoO ",76 " +@#$%o& ",77 " *=-;#::o+ ",78 " >,<12#:34 ",79 " 45671#:X3 ",80 " +89<02qwo ",81 "e* >,67;ro ",82 "ty> 459@>+&& ",83 "$2u+ ><ipas8* ",84 "%$;=* *3:.Xa.dfg> ",85 "Oh$;ya *3d.a8j,Xe.d3g8+ ",86 " Oh$;ka *3d$a8lz„xxc:.e3g54 ",87 " Oh$;kO *pd$%svbzz,sxxxxfX..&wn> ",88 " Oh$@mO *3dthwlsslszjzxxxxxxx3:td8M4 ",89 " Oh$@g& *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B* ",90 " Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5& ",91 " Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM* ",92 " OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ",93 " 2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ",94 " :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo",95 " +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g",96 " *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&en",97 " p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>",98 " OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ",99 " 3206Bwxxszx%et.eaAp77m77mmmf3&eeeg* ",

78

Page 87: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

100 " @26MvzxNzvlbwfpdettttttttttt.c,n& ",101 " *;16=lsNwwNwgsvslbwwvccc3pcfu<o ",102 " p;<69BvwwsszslllbBlllllllu<5+ ",103 " OS0y6FBlvvvzvzss,u=Blllj=54 ",104 " c1-699Blvlllllu7k96MMMg4 ",105 " *10y8n6FjvllllB<166668 ",106 " S-kg+>666<M<996-y6n<8* ",107 " p71=4 m69996kD8Z-66698&& ",108 " &i0ycm6n4 ogk17,0<6666g ",109 " N-k-<> >=01-kuu666> ",110 " ,6ky& &46-10ul,66, ",111 " Ou0<> o66y<ulw<66& ",112 " *kk5 >66By7=xu664 ",113 " <<M4 466lj<Mxu66o ",114 " *>> +66uv,zN666* ",115 " 566,xxj669 ",116 " 4666FF666> ",117 " >966666M ",118 " oM6668+ ",119 " *4 ",120 " ",121 " "122 ]123124 class WheelbarrowExample:125 # When invoked (via signal delete_event), terminates the application126 def close_application(self, widget, event, data=None):127 gtk.mainquit()128 return gtk.FALSE129130 def __init__(self):131 # Create the main window, and attach delete_event signal to terminate132 # the application. Note that the main window will not have a titlebar133 # since we’re making it a popup.134 window = gtk.Window(gtk.WINDOW_POPUP)135 window.connect("delete_event", self.close_application)136 window.set_events(window.get_events() | gtk.gdk.BUTTON_PRESS_MASK)137 window.connect("button_press_event", self.close_application)138 window.show()139140 # Now for the pixmap and the image widget141 pixmap, mask = gtk.gdk.pixmap_create_from_xpm_d(142 window.window, None, WheelbarrowFull_xpm)143 image = gtk.Image()144 image.set_from_pixmap(pixmap, mask)145 image.show()146147 # To display the image, we use a fixed widget to place the image148 fixed = gtk.Fixed()149 fixed.set_size_request(200, 200)150 fixed.put(image, 0, 0)151 window.add(fixed)152 fixed.show()153154 # This masks out everything except for the image itself155 window.shape_combine_mask(mask, 0, 0)156157 # show the window158 window.set_position(gtk.WIN_POS_CENTER_ALWAYS)

79

Page 88: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

159 window.show()160161 def main():162 gtk.main()163 return 0164165 if __name__ == "__main__":166 WheelbarrowExample()167 main()

Para hacer la imagen sensible, conectamos la señal "button_press_event" para hacer que el programa finalice.Las lineas 137-138 hacen el dibujo sensible a una pulsación de un botón del ratón y lo conectan al métodoclose_application () .

9.7. ReglasLos controlesRuler (Regla) se usan para indicar la posición del puntero del ratón 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. Unpequeño triángulo indicador en la regla muestra la posición exacta del puntero relativa a la regla.

Una regla debe crearse primero. Reglas horizontales y verticales se crean usando las siguientes funciones:

hruler = gtk.HRuler() # regla horizontal

vruler = gtk.VRuler() # regla vertical

Una vez que una regla se crea, podemos definir la unidad de medida. Las unidades de medida para las reglaspueden serPIXELS (PIXELES),INCHES(PULGADAS) oCENTIMETERS(CENTIMETROS). Esto se fija usandoel método:

ruler.set_metric( metric )

La medida predeterminada esPIXELS .

ruler.set_metric(gtk.PIXELS)

Otra característica importante de una regla es cómo marca las unidades de escala y donde se coloca el indicadorde posición inicialmente. Esto se fija usando el método:

ruler.set_range( lower , upper , position , max_size )

Los argumentoslower (bajo) yupper (alto) definen la extensión de la regla, ymax_size (tamaño máximo) esel mayor número posible que se visualizará. LaPosition (Posición) define la posición inicial del indicador delpuntero dentro de la regla.

Una regla vertical puede medir una ventana de 800 píxeles de ancho de la siguiente forma:

80

Page 89: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

vruler.set_range(0, 800, 0, 800)

Las marcas mostradas en la regla irán desde 0 a 800, con un número cada 100 píxeles. Si en lugar de esoquisieramos una regla de 7 a 16, escribiríamos

vruler.set_range(7, 16, 0, 20)

El indicador de la regla es una pequeña marca triangular que indica la posición del puntero relativa a la regla.Si la regla se usa para seguir el puntero del ratón, la señal "motion_notify_event" debe conectarse al método"motion_notify_event" de la regla. Hay que configurar una retrollamada para "motion_notify_event" para el áreay usarconnect_object () para que la regla emita una señal "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 ejemplorulers.py [examples/rulers.py] crea un área de dibujo con una regla horizontal en la partede arriba y una regla vertical a su izquierda. El tamaño del área de dibujo es de 600 píxeles de ancho por 400píxeles de alto. La regla horizontal va desde 7 hasta 13 con una marca cada 100 píxeles, mientras que la reglavertical va de 0 a 400 con una marca cada 100 píxeles. La colocación del área de dibujo y las reglas se hace conuna tabla. La figura Figura 9.8. Ejemplo de Reglas ilustra el resultado:

81

Page 90: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

Figura 9.8. Ejemplo de Reglas

El código fuente esrulers.py [examples/rulers.py] :

1 #!/usr/bin/env python23 # example rulers.py45 import gtk67 class RulersExample:8 XSIZE = 6009 YSIZE = 400

10

82

Page 91: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

11 # This routine gets control when the close button is clicked12 def close_application(self, widget, event, data=None):13 gtk.mainquit()14 return gtk.FALSE1516 def __init__(self):17 window = gtk.Window(gtk.WINDOW_TOPLEVEL)18 window.connect("delete_event", self.close_application)19 window.set_border_width(10)2021 # Create a table for placing the ruler and the drawing area22 table = gtk.Table(3, 2, gtk.FALSE)23 window.add(table)2425 area = gtk.DrawingArea()26 area.set_size_request(self.XSIZE, self.YSIZE)27 table.attach(area, 1, 2, 1, 2,28 gtk.EXPAND|gtk.FILL, gtk.FILL, 0, 0 )29 area.set_events(gtk.gdk.POINTER_MOTION_MASK |30 gtk.gdk.POINTER_MOTION_HINT_MASK )3132 # The horizontal ruler goes on top. As the mouse moves across the33 # drawing area, a motion_notify_event is passed to the34 # appropriate event handler for the ruler.35 hrule = gtk.HRuler()36 hrule.set_metric(gtk.PIXELS)37 hrule.set_range(7, 13, 0, 20)38 def motion_notify(ruler, event):39 return ruler.emit("motion_notify_event", event)40 area.connect_object("motion_notify_event", motion_notify, hrule)41 table.attach(hrule, 1, 2, 0, 1,42 gtk.EXPAND|gtk.SHRINK|gtk.FILL, gtk.FILL, 0, 0 )4344 # The vertical ruler goes on the left. As the mouse moves across45 # the drawing area, a motion_notify_event is passed to the46 # appropriate event handler for the ruler.47 vrule = gtk.VRuler()48 vrule.set_metric(gtk.PIXELS)49 vrule.set_range(0, self.YSIZE, 10, self.YSIZE)50 area.connect_object("motion_notify_event", motion_notify, vrule)51 table.attach(vrule, 0, 1, 1, 2,52 gtk.FILL, gtk.EXPAND|gtk.SHRINK|gtk.FILL, 0, 0 )5354 # Now show everything55 area.show()56 hrule.show()57 vrule.show()58 table.show()59 window.show()6061 def main():62 gtk.main()63 return 06465 if __name__ == "__main__":66 RulersExample()67 main()

83

Page 92: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

Las lineas 40 y 50 conectan la retrollamadamotion_notify () al área pasandolehrule en la linea 40 yvrule

en la linea 50 como datos de usuario. La retrollamadamotion_notify () se llamará dos veces cada vez que elratón se mueva - una vez conhrule y otra vez convrule .

9.8. Barras de EstadoLasStatusbars (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 elmensaje anterior.

Para que distintas partes de la aplicación puedan usar la misma barra de estado para visualizar mensajes, el controlde barra de estado mantiene Identificadores de Contexto que se usan para identificar diferentes "usuarios". Elmensaje en el tope de la pila es el que se visualiza, no importa el contexto al que pertenezca. Los mensajes seapilan 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 método con una pequeñadescripción textual del contexto:

context_id = statusbar.get_context_id( context_description )

Hay tres métodos 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 añadir un nuevo mensaje a lastatusbar (barra de estado). Devuelve unmessage_id (identificador de mensaje), que puede usarse con el métodoremove () para borrar el mensaje quecumpla la combinación demessage_id y context_id en la pila de lastatusbar (barra de estado).

El métodopop () elimina el mensaje que esté en la posición más alta de la pila con el identificador de contextocontext_id .

El programa de ejemplostatusbar.py [examples/statusbar.py] crea una barra de estado y dos botones, uno parainsertar elementos en la barra de estado, y otro para sacar el último elemento fuera. La figura Figura 9.9. Ejemplode Barra de Estado muestra el resultado:

84

Page 93: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

Figura 9.9. Ejemplo de Barra de Estado

El código fuente es:

1 #!/usr/bin/env python23 # example statusbar.py45 import gtk67 class StatusbarExample:8 def push_item(self, widget, data):9 buff = " Item %d" % self.count

10 self.count = self.count + 111 self.status_bar.push(data, buff)12 return1314 def pop_item(self, widget, data):15 self.status_bar.pop(data)16 return1718 def __init__(self):19 self.count = 120 # create a new window21 window = gtk.Window(gtk.WINDOW_TOPLEVEL)22 window.set_size_request(200, 100)23 window.set_title("PyGTK Statusbar Example")24 window.connect("delete_event", gtk.mainquit)2526 vbox = gtk.VBox(gtk.FALSE, 1)27 window.add(vbox)28 vbox.show()2930 self.status_bar = gtk.Statusbar()31 vbox.pack_start(self.status_bar, gtk.TRUE, gtk.TRUE, 0)32 self.status_bar.show()3334 context_id = self.status_bar.get_context_id("Statusbar example") 3536 button = gtk.Button("push item")37 button.connect("clicked", self.push_item, context_id)38 vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 2)39 button.show()4041 button = gtk.Button("pop last item")42 button.connect("clicked", self.pop_item, context_id)

85

Page 94: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

43 vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 2)44 button.show()4546 # always display the window as the last step so it all splashes on47 # the screen at once.48 window.show()4950 def main():51 gtk.main()52 return 05354 if __name__ == "__main__":55 StatusbarExample()56 main()

9.9. Entradas de TextoEl controlEntry (Entrada) permite escribir texto y mostrarlo en una caja de texto con una única linea. El textopuede fijarse con llamadas a métodos que permiten que nuevo texto reemplace, se inserte antes o se añada despuésdel contenido actual del controlEntry .

La función para crear un controlEntry es:

entry = gtk.Entry( max=0)

Si el argumentomax se especifica se establece un límite de longitud del texto dentro de laEntry . Si max es 0 nohay límite.

La longitud máxima de una entrada puede cambiarse usando el método:

entry.set_max_length( max)

El siguiente método altera el texto que hay actualmente en el controlEntry .

entry.set_text( text )

El métodoset_text () fija el contenido del controlEntry a text , reemplazando el contenido actual. Fíjate quela claseEntry implementa el interfazEditable (sí, gobject soporta interfaces al estilo de Java) que contienealgunas funciones más para manipular los contenidos. Por ejemplo, el método:

entry.insert_text( text , position =0)

insertatext en la posición indicada dentro de laentry .

El contenido de laEntry puede recuperarse usando una llamada al siguiente método. Esto es útil en lasretrollamadas que se describen más abajo.

86

Page 95: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

text = entry.get_text()

Si no queremos que el contenido de laEntry sea modificada por alguien escribiendo en ella, podemos cambiarsu estado de edición.

entry.set_editable( is_editable )

El método anterior permite intercambiar el estado de edición del controlEntry pasándole un valor TRUE oFALSE en el argumentois_editable .

Si estamos usando el controlEntry y no queremos que el texto que se introduzca sea visible, por ejemplo cuandouna contraseña se escribe, podemos usar el siguiente método, que además acepta una bandera booleana.

entry.set_visibility( visible )

Una región del texto se puede poner como seleccionada usando el siguiente método. Esto se usaría sobre todocuando se ponga algún valor predeterminado en unEntry , haciendo fácil para el usuario el borrado.

entry.select_region( start , end )

Si queremos recibir notificación cuando el usuario introduzca el texto, podemos conectar a las señales "activate"o "changed". La primera se produce cuando el usuario pulsa la tecla enter dentro del controlEntry . La segundase produce cuando ocurre cualquier cambio en el texto, por ejemplo para cualquier inserción o borrado de uncaracter.

El programa de ejemploentry.py [examples/entry.py] muestra el uso de un controlEntry . La figura Figura 9.10.Ejemplo de Entrada muestra el resultado de ejecutar el programa:

Figura 9.10. Ejemplo de Entrada

87

Page 96: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

El código fuenteentry.py [examples/entry.py] es:

1 #!/usr/bin/env python23 # example entry.py45 import gtk67 class EntryExample:8 def enter_callback(self, widget, entry):9 entry_text = entry.get_text()

10 print "Entry contents: %s\n" % entry_text1112 def entry_toggle_editable(self, checkbutton, entry):13 entry.set_editable(checkbutton.get_active())1415 def entry_toggle_visibility(self, checkbutton, entry):16 entry.set_visibility(checkbutton.get_active())1718 def __init__(self):19 # create a new window20 window = gtk.Window(gtk.WINDOW_TOPLEVEL)21 window.set_size_request(200, 100)22 window.set_title("GTK Entry")23 window.connect("delete_event", gtk.mainquit)2425 vbox = gtk.VBox(gtk.FALSE, 0)26 window.add(vbox)27 vbox.show()2829 entry = gtk.Entry()30 entry.set_max_length(50)31 entry.connect("activate", self.enter_callback, entry)32 entry.set_text("hello")33 entry.insert_text(" world", len(entry.get_text()))34 entry.select_region(0, len(entry.get_text()))35 vbox.pack_start(entry, gtk.TRUE, gtk.TRUE, 0)36 entry.show()3738 hbox = gtk.HBox(gtk.FALSE, 0)39 vbox.add(hbox)40 hbox.show()4142 check = gtk.CheckButton("Editable")43 hbox.pack_start(check, gtk.TRUE, gtk.TRUE, 0)44 check.connect("toggled", self.entry_toggle_editable, entry)45 check.set_active(gtk.TRUE)46 check.show()4748 check = gtk.CheckButton("Visible")49 hbox.pack_start(check, gtk.TRUE, gtk.TRUE, 0)50 check.connect("toggled", self.entry_toggle_visibility, entry)51 check.set_active(gtk.TRUE)52 check.show()5354 button = gtk.Button(stock=gtk.STOCK_CLOSE)55 button.connect_object("clicked", gtk.mainquit, window)56 vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 0)

88

Page 97: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

57 button.set_flags(gtk.CAN_DEFAULT)58 button.grab_default()59 button.show()60 window.show()6162 def main():63 gtk.main()64 return 06566 if __name__ == "__main__":67 EntryExample()68 main()

9.10. Botones Aumentar/DisminuirEl control SpinButton (Botón Aumentar/ Disminuir) se usa generalmente para permitir al usuario seleccionarun valor dentro de un rango de valores numéricos. Consiste en una caja de entrada de texto con botones de flechaarriba y abajo a un lado. Seleccionando uno de los botones causa que el valor aumente o disminuya en el rango devalores posibles. La caja de entrada también puede editarse directamente para introducir un valor específico.

El SpinButton permite que el valor tenga cero o más cifras decimales y puede incrementarse/decrementarse enpasos configurables. La acción de mantener pulsado uno de los botones opcionalmente provoca una aceleraciónen el cambio del valor correspondiente al tiempo que se mantenga presionado.

El SpinButton usa un objetoAdjustment (Ajuste) para almacenar la información del rango de valores que elbotón aumentar/disminuir puede tomar. Esto le hace un control muy útil.

Recuerda que un controlAdjustment (Ajuste) se crea con la siguiente función, que muestra la información quealmacena:

adjustment = gtk.Adjustment( value =0, lower =0, upper =0, step_incr =0, page_incr =0,←↩page_size =0)

Estos atributos de unAdjustment (Ajuste) se usan en elSpinButton de la siguiente manera:value valor inicial para el Botón Aumentar/Disminuirlower el valor más bajo del rangoupper el valor más alto del rangostep_increment valor que se incrementa/decrementa cuando se pulsa el botón-1 del ratón en un

botónpage_increment valor que se incrementa/decrementa cuando se pulsa el botón-2 del ratón en un

botónpage_size no se usa

Adicionalmente, el botón del ratón botón-3 se puede usar para saltar directamente a los valoresupper (superior) ylower (inferior) cuando se usa para seleccionar uno de los botones. Veamos como crear unSpinButton (BotónAumentar/Disminuir):

spin_button = gtk.SpinButton( adjustment =None, climb_rate =0.0, digits =0)

El argumentoclimb_rate (razón de escalada) puede tomar un valor entre 0.0 y 1.0 e indica la cantidad deaceleración que elSpinButton tiene. El argumentodigits especifica el número de cifras decimales que semostrarán.

89

Page 98: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

Un SpinButton se puede reconfigurar después de su creación usando el siguiente método:

spin_button.configure( adjustment , climb_rate , digits )

La variablespin_button especifica el botón aumentar/disminuir que se va a reconfigurar. Los otros argumentosson los mismos que antes.

El adjustment (ajuste) se puede fijar y recupar independientemente usando los siguientes dos métodos:

spin_button.set_adjustment( adjustment )

adjustment = spin_button.get_adjustment()

El número de cifras decimales también se puede cambiar usando:

spin_button.set_digits( digits )

El valor que unSpinButton esta mostrando actualmente se puede cambiar usando el siguiente método:

spin_button.set_value( value )

El valor actual de unSpinButton se puede recuperar como un valor real o como un valor entero usando lossiguientes métodos:

float_value = spin_button.get_value()

int_value = spin_button.get_value_as_int()

Si quieres alterar el valor de unSpinButton relativo a su valor actual, entonces usa el siguiente método:

spin_button.spin( direction , increment )

El parámetrodirection (dirección) puede tomar uno de los siguientes valores:

SPIN_STEP_FORWARD # paso hacia adelanteSPIN_STEP_BACKWARD # paso hacia atrásSPIN_PAGE_FORWARD # página hacia adelanteSPIN_PAGE_BACKWARD # página hacia atrásSPIN_HOME # inicioSPIN_END # finSPIN_USER_DEFINED # definido por el usuario

Este método comprime bastante funcionalidad, que intentaré explicar ahora. Muchos de estos parámetros usanvalores del objetoAdjustment (Ajuste) que está asociado con unSpinButton (Botón Aumentar/Disminuir).

90

Page 99: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

SPIN_STEP_FORWARD (paso adelante) y SPIN_STEP_BACKWARD (paso atrás) cambian el valor delSpinButton con una cantidad especificada por elincrement (incremento), a menos queincrement (in-cremento) sea igual a 0, en cuyo caso el valor se modifica con elstep_increment (incremento de paso) delAdjustment .

SPIN_PAGE_FORWARD (página adelante) y SPIN_PAGE_BACKWARD (página atrás) simplemente alteran elvalor delSpinButton por increment (incremento).

SPIN_HOME (inicio) pone el valor delSpinButton a la parte de abajo del rango delAdjustment .

SPIN_END (fin) fija el valor delSpinButton a la parte de arriba del rango delAdjustment .

SPIN_USER_DEFINED (definido por el usuario) simplemente modifica el valor delSpinButton con la cantidadespecificada.

Ahora nos alejamos de los métodos para fijar y recuperar los atributos del rango de unSpinButton , y noscentramos en los métodos que modifican la apariencia y comportamiento de un controlSpinButton propiamente.

El primero de estos métodos se usa para limitar la caja de texto delSpinButton para que solo contenga un valornumérico. Esto evita que el usuario escriba cualquier otra cosa que no sea un valor numérico dentro de la caja detexto de unSpinButton :

spin_button.set_numeric( numeric )

El argumentonumeric es TRUE para limitar la caja de texto a valores numéricos o FALSE para quitar estalimitación.

Puedes fijar si quieres que el valor delSpinButton se quede en los valores inferior y superior del rango con elsiguiente método:

spin_button.set_wrap( wrap )

El SpinButton limitará los valores dentro del rango siwrap es TRUE.

Puedes hacer que elSpinButton redondee el valor alstep_increment (incremento) más cercano, lo cual sefija dentro del objetoAdjustment usado en elSpinButton . Esto se consigue con el siguiente método cuando elargumentosnap_to_ticks es TRUE:

spin_button.set_snap_to_ticks( snap_to_ticks )

La política de actualización de unSpinButton se cambia con el siguiente método:

spin_button.set_update_policy( policy )

Los valores posibles de esta política son:

UPDATE_ALWAYS # actualizar siempre

91

Page 100: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

UPDATE_IF_VALID # actualizar si es válido

Estas políticas afectan el comportamiento de unSpinButton cuando analiza el texto insertado y sincroniza suvalor con los valores delAdjustment .

En el caso de UPDATE_IF_VALID (actualizar si es válido) el valor delSpinButton sólo cambia si el texto esun valor numérico que está dentro del rango especificado por elAdjustment . En cualquier otro caso el texto seresetea al valor actual.

En el caso de UPDATE_ALWAYS (actualizar siempre) se ignoran los errores de conversión al pasar el texto a unvalor numérico.

Finalmente, puedes especificar una actualización por ti mismoSpinButton :

spin_button.update()

El programa de ejemplospinbutton.py [examples/spinbutton.py] muestra el uso de botones de aumen-tar/disminuir incluyendo el uso de un número de características. La figura Figura 9.11. Ejemplo de BotónAumentar/Disminuir muestra el resultado de ejecutar el programa de ejemplo:

Figura 9.11. Ejemplo de Botón Aumentar/Disminuir

92

Page 101: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

El código fuentespinbutton.py [examples/spinbutton.py] es:

1 #!/usr/bin/env python23 # example spinbutton.py45 import gtk67 class SpinButtonExample:8 def toggle_snap(self, widget, spin):9 spin.set_snap_to_ticks(widget.get_active())

1011 def toggle_numeric(self, widget, spin):12 spin.set_numeric(widget.get_active())1314 def change_digits(self, widget, spin, spin1):15 spin1.set_digits(spin.get_value_as_int())1617 def get_value(self, widget, data, spin, spin2, label):18 if data == 1:19 buf = "%d" % spin.get_value_as_int()20 else:21 buf = "%0.*f" % (spin2.get_value_as_int(),22 spin.get_value())23 label.set_text(buf)2425 def __init__(self):26 window = gtk.Window(gtk.WINDOW_TOPLEVEL)27 window.connect("destroy", gtk.mainquit)28 window.set_title("Spin Button")2930 main_vbox = gtk.VBox(gtk.FALSE, 5)31 main_vbox.set_border_width(10)32 window.add(main_vbox)3334 frame = gtk.Frame("Not accelerated")35 main_vbox.pack_start(frame, gtk.TRUE, gtk.TRUE, 0)3637 vbox = gtk.VBox(gtk.FALSE, 0)38 vbox.set_border_width(5)39 frame.add(vbox)4041 # Day, month, year spinners42 hbox = gtk.HBox(gtk.FALSE, 0)43 vbox.pack_start(hbox, gtk.TRUE, gtk.TRUE, 5)4445 vbox2 = gtk.VBox(gtk.FALSE, 0)46 hbox.pack_start(vbox2, gtk.TRUE, gtk.TRUE, 5)4748 label = gtk.Label("Day :")49 label.set_alignment(0, 0.5)50 vbox2.pack_start(label, gtk.FALSE, gtk.TRUE, 0)5152 adj = gtk.Adjustment(1.0, 1.0, 31.0, 1.0, 5.0, 0.0)53 spinner = gtk.SpinButton(adj, 0, 0)54 spinner.set_wrap(gtk.TRUE)55 vbox2.pack_start(spinner, gtk.FALSE, gtk.TRUE, 0)56

93

Page 102: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

57 vbox2 = gtk.VBox(gtk.FALSE, 0)58 hbox.pack_start(vbox2, gtk.TRUE, gtk.TRUE, 5)5960 label = gtk.Label("Month :")61 label.set_alignment(0, 0.5)62 vbox2.pack_start(label, gtk.FALSE, gtk.TRUE, 0)6364 adj = gtk.Adjustment(1.0, 1.0, 12.0, 1.0, 5.0, 0.0)65 spinner = gtk.SpinButton(adj, 0, 0)66 spinner.set_wrap(gtk.TRUE)67 vbox2.pack_start(spinner, gtk.FALSE, gtk.TRUE, 0)6869 vbox2 = gtk.VBox(gtk.FALSE, 0)70 hbox.pack_start(vbox2, gtk.TRUE, gtk.TRUE, 5)7172 label = gtk.Label("Year :")73 label.set_alignment(0, 0.5)74 vbox2.pack_start(label, gtk.FALSE, gtk.TRUE, 0)7576 adj = gtk.Adjustment(1998.0, 0.0, 2100.0, 1.0, 100.0, 0.0)77 spinner = gtk.SpinButton(adj, 0, 0)78 spinner.set_wrap(gtk.FALSE)79 spinner.set_size_request(55, -1)80 vbox2.pack_start(spinner, gtk.FALSE, gtk.TRUE, 0)8182 frame = gtk.Frame("Accelerated")83 main_vbox.pack_start(frame, gtk.TRUE, gtk.TRUE, 0)8485 vbox = gtk.VBox(gtk.FALSE, 0)86 vbox.set_border_width(5)87 frame.add(vbox)8889 hbox = gtk.HBox(gtk.FALSE, 0)90 vbox.pack_start(hbox, gtk.FALSE, gtk.TRUE, 5)9192 vbox2 = gtk.VBox(gtk.FALSE, 0)93 hbox.pack_start(vbox2, gtk.TRUE, gtk.TRUE, 5)9495 label = gtk.Label("Value :")96 label.set_alignment(0, 0.5)97 vbox2.pack_start(label, gtk.FALSE, gtk.TRUE, 0)9899 adj = gtk.Adjustment(0.0, -10000.0, 10000.0, 0.5, 100.0, 0.0)

100 spinner1 = gtk.SpinButton(adj, 1.0, 2)101 spinner1.set_wrap(gtk.TRUE)102 spinner1.set_size_request(100, -1)103 vbox2.pack_start(spinner1, gtk.FALSE, gtk.TRUE, 0)104105 vbox2 = gtk.VBox(gtk.FALSE, 0)106 hbox.pack_start(vbox2, gtk.TRUE, gtk.TRUE, 5)107108 label = gtk.Label("Digits :")109 label.set_alignment(0, 0.5)110 vbox2.pack_start(label, gtk.FALSE, gtk.TRUE, 0)111112 adj = gtk.Adjustment(2, 1, 5, 1, 1, 0)113 spinner2 = gtk.SpinButton(adj, 0.0, 0)114 spinner2.set_wrap(gtk.TRUE)115 adj.connect("value_changed", self.change_digits, spinner2, spinner1)

94

Page 103: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

116 vbox2.pack_start(spinner2, gtk.FALSE, gtk.TRUE, 0)117118 hbox = gtk.HBox(gtk.FALSE, 0)119 vbox.pack_start(hbox, gtk.FALSE, gtk.TRUE, 5)120121 button = gtk.CheckButton("Snap to 0.5-ticks")122 button.connect("clicked", self.toggle_snap, spinner1)123 vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 0)124 button.set_active(gtk.TRUE)125126 button = gtk.CheckButton("Numeric only input mode")127 button.connect("clicked", self.toggle_numeric, spinner1)128 vbox.pack_start(button, gtk.TRUE, gtk.TRUE, 0)129 button.set_active(gtk.TRUE)130131 val_label = gtk.Label("")132133 hbox = gtk.HBox(gtk.FALSE, 0)134 vbox.pack_start(hbox, gtk.FALSE, gtk.TRUE, 5)135 button = gtk.Button("Value as Int")136 button.connect("clicked", self.get_value, 1, spinner1, spinner2,137 val_label)138 hbox.pack_start(button, gtk.TRUE, gtk.TRUE, 5)139140 button = gtk.Button("Value as Float")141 button.connect("clicked", self.get_value, 2, spinner1, spinner2,142 val_label)143 hbox.pack_start(button, gtk.TRUE, gtk.TRUE, 5)144145 vbox.pack_start(val_label, gtk.TRUE, gtk.TRUE, 0)146 val_label.set_text("0")147148 hbox = gtk.HBox(gtk.FALSE, 0)149 main_vbox.pack_start(hbox, gtk.FALSE, gtk.TRUE, 0)150151 button = gtk.Button("Close")152 button.connect("clicked", gtk.mainquit)153 hbox.pack_start(button, gtk.TRUE, gtk.TRUE, 5)154 window.show_all()155156 def main():157 gtk.main()158 return 0159160 if __name__ == "__main__":161 SpinButtonExample()162 main()

9.11. Lista DesplegableLa lista desplegable es otro control bastante simple que es realmente una colección de otros controles. Desde elpunto de vista del usuario, el control consiste en una caja de entrada de texto y un menú desplegable desde el queel usuario puede seleccionar una entrada a partir de un conjunto predefinido. Alternativamente, el usuario puedeescribir una opción diferente directamente en la caja de texto.

El Combotiene dos partes principales de las que te tienes que preocupar: unaentry (entrada) y unalist (lista).Puedes acceder a ellas usando los atributos:

95

Page 104: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

combo.entry

combo.list

Lo primero, para crear una lista desplegable, usa:

combo = gtk.Combo()

Ahora, si quieres fijar la cadena en la sección de la entrada de la lista desplegable, esto se hace manipulando elcontrol entrada directamente:

combo.entry.set_text( text )

Para fijar los valores de la lista desplegable, uno usa el método:

combo.set_popdown_strings( strings )

Antes de que puedas hacer esto, tienes que componer una lista con las opciones que quieras.

Aqui tenemos el típico código para crear un conjunto de opciones:

slist = [ "String 1", "String 2", "String 3", "String 4" ]

combo.set_popdown_strings(slist)

En este punto ya tienes una lista desplegable funcionando. Hay unos cuantos aspectos de su comportamiento quepuedes cambiar. Esto se consigue con los métodos:

combo.set_use_arrows( val )

combo.set_use_arrows_always( val )

combo.set_case_sensitive( val )

El métodoset_use_arrows () permite al usuario cambiar el valor de la entrada usando las teclas de flechaarriba/abajo cuandoval se pone a TRUE. Esto no despliega la lista, si no que sustituye el texto actual de la entradacon la siguiente entrada de la lista (arriba o abajo, según la combinación de tecla indique). Esto se hace buscandoen la lista el elemento correspondiente al valor actual de la entrada y seleccionando el elemento anterior/siguientecorrespondiente. Normalmente en una entrada las teclas de flecha se usan para cambiar el foco (también puedeshacer esto usando el tabulador). Ten en cuenta que cuando el elemento actual es el último de la lista y pulsas latecla flecha abajo se cambia el foco (lo mismo ocurre cuando estas en el primer elemento y pulsas la tecla flechaarriba).

Si el valor actual de la entrada no está en la lista, el métodoset_use_arrows () se desactiva.

96

Page 105: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

El métodoset_use_arrows_always () , cuandoval es TRUE, también permite al usuario el uso de las teclasde flecha arriba/abajo para ciclar por las opciones de la lista desplegable, excepto que da la vuelta a los valores dela lista, desactivando por completo el uso de las flechas arriba y abajo para cambiar el foco.

El métodoset_case_sensitive () dice si GTK busca o no las entradas de una forma sensible a mayúsculas.Esto se usa cuando se le pide al controlComboque busque un valor de la lista usando la entrada actual de la cajade texto. Este completado puede producirse de forma sensible o insensible a mayúsculas, dependiendo de lo quele pasemos a este método. El controlCombotambién puede simplemente completar la entrada actual si el usuariopulsa la combinación de teclas MOD-1-Tab. MOD-1 normalmente corresponde a la teclaAlt , gracias a la utilidadxmodmap. Ten en cuenta, sin embargo, que algunos manejadores de ventana también usan esta combinación deteclas, lo que inutilizará su uso en GTK.

Ahora que tenemos una lista desplegable, y que tiene la apariencia y el comportamiento que queremos, lo únicoque nos falta es la capacidad de obtener los datos de la lista desplegable. Esto es relativamente directo. La mayoríadel tiempo, de lo único que te tienes que preocupar es de obtener los datos de la entrada. La entrada es accesiblesimplemente comocombo.entry . Las dos cosas fundamentales que vas a querer hacer con ella es conectarle laseñal "activate", que indica que el usuario ha pulsado la teclaReturn o la teclaEnter, y leer el texto. Lo primerose consigue usando algo como:

combo.entry.connect("activate", my_callback, my_data)

Obtener el texto en cualquier momento se consigue simplemente usando el siguiente método:

string = combo.entry.get_text()

Eso es todo lo importante. Hay un método:

combo.disable_activate()

que desactivará la señal "activate" en el control de entrada de la lista desplegable. Personalmente, no se me ocurreninguna situación en la que quieras usarlo, pero existe.

9.12. CalendarioEl controlCalendar (Calendario) es una forma efectiva para visualizar y obtener información relativa a fechasmensuales. Es un control muy fácil de usar y trabajar con él.

Crear un controlGtkCalendar es tan simple como:

calendar = gtk.Calendar()

El calendario mostrará el mes y el año actual de manera predeterminada.

Puede haber ocasiones en las que necesites cambiar mucha información dentro de este control y los siguientesmétodos te permitirán realizar múltiples cambios al controlCalendar sin que el usuario vea muchos cambios enpantalla.

calendar.freeze() # congelar

97

Page 106: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

calendar.thaw() # reanudar

Funcionan exactamente igual que los métodos freeze/thaw de cualquier otro control.

El control Calendar tiene unas cuantas opciones que te permiten cambiar la manera en la que el control sevisualiza y se comporta usando el método:

calendar.display_options( flags )

El argumentoflags (banderas) se puede formar combinando cualquiera de las siguientes cinco opciones usandoel operador lógico (|):CALENDAR_SHOW_HEADING esta opción especifica que el mes y el año deben mostrarse cuando se

dibuje el calendario.CALENDAR_SHOW_DAY_NAMES esta opción especifica que la descripción de tres letras para cada día (Lun,

Mar, etc.) debe mostrarse.CALENDAR_NO_MONTH_CHANGE esta opción dice que el usuario no podrá cambiar el mes que se muestra.

Esto puede ser bueno si sólo necesitas mostrar un mes en particular comocuando muestras 12 controles de calendario uno para cada mes dentro deun mes en particular.

CALENDAR_SHOW_WEEK_NUMBERSesta opción especifica que se muestre el número de cada semana en laparte de abajo izquierda del calendario (ejemplo: Enero 1 = Semana 1,Diciembre 31 = Semana 52).

CALENDAR_WEEK_START_MONDAY esta opción dice que la semana empezará en Lunes en lugar de enDomingo, que es el valor predeterminado. Esto solo afecta al orden en elque se muestran los días de izquierda a derecha.

Los siguientes métodos se usan para fijar la fecha que se muestra:

result = calendar.select_month( month , year )

calendar.select_day( day )

El valor que devuelve el métodoselect_month () es un valor booleano que indica si la selección tuvo éxito.

Con el métodoselect_day () el día especificado se selecciona dentro del mes actual, si eso es posible. Un valorpara el día de 0 limpiará la selección actual.

Además de tener un día seleccionado, un número arbitrario de días se pueden "marcar". Un día marcado se destacaen el calendario. Los siguientes métodos se proporcionan para manipular días marcados:

result = calendar.mark_day( day )

result = calendar.unmark_day( day )

calendar.clear_marks()

mark_day () y unmark_day () devuelven un valor booleano que indica si el método tuvo éxito. Fíjate que lasmarcas son persistentes entre cambios de meses y años.

98

Page 107: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

El último método del controlCalendar se usa para obtener la fecha seleccionada, mes y/o año.

año, mes, día = calendar.get_date()

El controlCalendar puede generar varias señales que indican la selección y cambio de la fecha. Los nombres deestas señales son autoexplicativos, y son:

month_changed # cambio de mes

day_selected # día seleccionado

day_selected_double_click # doble clic en día seleccionado

prev_month # mes anterior

next_month # mes siguiente

prev_year # año anterior

next_year # año siguiente

Esto nos deja con la necesidad de poner todo esto junto en el programa de ejemplocalendar.py [exam-ples/calendar.py] . La figura Figura 9.12. Ejemplo de Calendario muestra el resultado del programa:

99

Page 108: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

Figura 9.12. Ejemplo de Calendario

El código fuente escalendar.py [examples/calendar.py]:

1 #!/usr/bin/env python23 # example calendar.py4 #5 # Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson, Mattias Gronlund6 # Copyright (C) 2000 Tony Gale7 # Copyright (C) 2001-2002 John Finlay8 #9 # This program is free software; you can redistribute it and/or modify

10 # it under the terms of the GNU General Public License as published by11 # the Free Software Foundation; either version 2 of the License, or12 # (at your option) any later version.13 #14 # This program is distributed in the hope that it will be useful,15 # but WITHOUT ANY WARRANTY; without even the implied warranty of16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the17 # GNU General Public License for more details.

100

Page 109: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

18 #19 # You should have received a copy of the GNU General Public License20 # along with this program; if not, write to the Free Software21 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.2223 import gtk, pango24 import time2526 class CalendarExample:27 DEF_PAD = 1028 DEF_PAD_SMALL = 529 TM_YEAR_BASE = 19003031 calendar_show_header = 032 calendar_show_days = 133 calendar_month_change = 234 calendar_show_week = 335 calendar_monday_first = 43637 def calendar_date_to_string(self):38 year, month, day = self.window.get_date()39 mytime = time.mktime((year, month+1, day, 0, 0, 0, 0, 0, 0))40 return time.strftime("%x", time.gmtime(mytime))4142 def calendar_set_signal_strings(self, sig_str):43 prev_sig = self.prev_sig.get()44 self.prev2_sig.set_text(prev_sig)4546 prev_sig = self.last_sig.get()47 self.prev_sig.set_text(prev_sig)48 self.last_sig.set_text(sig_str)4950 def calendar_month_changed(self, widget):51 buffer = "month_changed: %s" % self.calendar_date_to_string()52 self.calendar_set_signal_strings(buffer)5354 def calendar_day_selected(self, widget):55 buffer = "day_selected: %s" % self.calendar_date_to_string()56 self.calendar_set_signal_strings(buffer)5758 def calendar_day_selected_double_click(self, widget):59 buffer = "day_selected_double_click: %s"60 buffer = buffer % self.calendar_date_to_string()61 self.calendar_set_signal_strings(buffer)6263 year, month, day = self.window.get_date()6465 if self.marked_date[day-1] == 0:66 self.window.mark_day(day)67 self.marked_date[day-1] = 168 else:69 self.window.unmark_day(day)70 self.marked_date[day-1] = 07172 def calendar_prev_month(self, widget):73 buffer = "prev_month: %s" % self.calendar_date_to_string()74 self.calendar_set_signal_strings(buffer)7576 def calendar_next_month(self, widget):

101

Page 110: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

77 buffer = "next_month: %s" % self.calendar_date_to_string()78 self.calendar_set_signal_strings(buffer)7980 def calendar_prev_year(self, widget):81 buffer = "prev_year: %s" % self.calendar_date_to_string()82 self.calendar_set_signal_strings(buffer)8384 def calendar_next_year(self, widget):85 buffer = "next_year: %s" % self.calendar_date_to_string()86 self.calendar_set_signal_strings(buffer)8788 def calendar_set_flags(self):89 options = 090 for i in range(5):91 if self.settings[i]:92 options = options + (1<<i)93 if self.window:94 self.window.display_options(options)9596 def calendar_toggle_flag(self, toggle):97 j = 098 for i in range(5):99 if self.flag_checkboxes[i] == toggle:

100 j = i101102 self.settings[j] = not self.settings[j]103 self.calendar_set_flags()104105 def calendar_font_selection_ok(self, button):106 self.font = self.font_dialog.get_font_name()107 if self.window:108 font_desc = pango.FontDescription(self.font)109 if font_desc:110 self.window.modify_font(font_desc)111112 def calendar_select_font(self, button):113 if not self.font_dialog:114 window = gtk.FontSelectionDialog("Font Selection Dialog")115 self.font_dialog = window116117 window.set_position(gtk.WIN_POS_MOUSE)118119 window.connect("destroy", self.font_dialog_destroyed)120121 window.ok_button.connect("clicked",122 self.calendar_font_selection_ok)123 window.cancel_button.connect_object("clicked",124 lambda wid: wid.destroy(),125 self.font_dialog)126 window = self.font_dialog127 if not (window.flags() & gtk.VISIBLE):128 window.show()129 else:130 window.destroy()131 self.font_dialog = None132133 def font_dialog_destroyed(self, data=None):134 self.font_dialog = None135

102

Page 111: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

136 def __init__(self):137 flags = [138 "Show Heading",139 "Show Day Names",140 "No Month Change",141 "Show Week Numbers",142 "Week Start Monday"143 ]144 self.window = None145 self.font = None146 self.font_dialog = None147 self.flag_checkboxes = 5*[None]148 self.settings = 5*[0]149 self.marked_date = 31*[0]150151 window = gtk.Window(gtk.WINDOW_TOPLEVEL)152 window.set_title("Calendar Example")153 window.set_border_width(5)154 window.connect("destroy", gtk.mainquit)155156 window.set_resizable(gtk.FALSE)157158 vbox = gtk.VBox(gtk.FALSE, self.DEF_PAD)159 window.add(vbox)160161 # The top part of the window, Calendar, flags and fontsel.162 hbox = gtk.HBox(gtk.FALSE, self.DEF_PAD)163 vbox.pack_start(hbox, gtk.TRUE, gtk.TRUE, self.DEF_PAD)164 hbbox = gtk.HButtonBox()165 hbox.pack_start(hbbox, gtk.FALSE, gtk.FALSE, self.DEF_PAD)166 hbbox.set_layout(gtk.BUTTONBOX_SPREAD)167 hbbox.set_spacing(5)168169 # Calendar widget170 frame = gtk.Frame("Calendar")171 hbbox.pack_start(frame, gtk.FALSE, gtk.TRUE, self.DEF_PAD)172 calendar = gtk.Calendar()173 self.window = calendar174 self.calendar_set_flags()175 calendar.mark_day(19)176 self.marked_date[19-1] = 1177 frame.add(calendar)178 calendar.connect("month_changed", self.calendar_month_changed)179 calendar.connect("day_selected", self.calendar_day_selected)180 calendar.connect("day_selected_double_click",181 self.calendar_day_selected_double_click)182 calendar.connect("prev_month", self.calendar_prev_month)183 calendar.connect("next_month", self.calendar_next_month)184 calendar.connect("prev_year", self.calendar_prev_year)185 calendar.connect("next_year", self.calendar_next_year)186187 separator = gtk.VSeparator()188 hbox.pack_start(separator, gtk.FALSE, gtk.TRUE, 0)189190 vbox2 = gtk.VBox(gtk.FALSE, self.DEF_PAD)191 hbox.pack_start(vbox2, gtk.FALSE, gtk.FALSE, self.DEF_PAD)192193 # Build the Right frame with the flags in194 frame = gtk.Frame("Flags")

103

Page 112: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

195 vbox2.pack_start(frame, gtk.TRUE, gtk.TRUE, self.DEF_PAD)196 vbox3 = gtk.VBox(gtk.TRUE, self.DEF_PAD_SMALL)197 frame.add(vbox3)198199 for i in range(5):200 toggle = gtk.CheckButton(flags[i])201 toggle.connect("toggled", self.calendar_toggle_flag)202 vbox3.pack_start(toggle, gtk.TRUE, gtk.TRUE, 0)203 self.flag_checkboxes[i] = toggle204205 # Build the right font-button206 button = gtk.Button("Font...")207 button.connect("clicked", self.calendar_select_font)208 vbox2.pack_start(button, gtk.FALSE, gtk.FALSE, 0)209210 # Build the Signal-event part.211 frame = gtk.Frame("Signal events")212 vbox.pack_start(frame, gtk.TRUE, gtk.TRUE, self.DEF_PAD)213214 vbox2 = gtk.VBox(gtk.TRUE, self.DEF_PAD_SMALL)215 frame.add(vbox2)216217 hbox = gtk.HBox (gtk.FALSE, 3)218 vbox2.pack_start(hbox, gtk.FALSE, gtk.TRUE, 0)219 label = gtk.Label("Signal:")220 hbox.pack_start(label, gtk.FALSE, gtk.TRUE, 0)221 self.last_sig = gtk.Label("")222 hbox.pack_start(self.last_sig, gtk.FALSE, gtk.TRUE, 0)223224 hbox = gtk.HBox (gtk.FALSE, 3)225 vbox2.pack_start(hbox, gtk.FALSE, gtk.TRUE, 0)226 label = gtk.Label("Previous signal:")227 hbox.pack_start(label, gtk.FALSE, gtk.TRUE, 0)228 self.prev_sig = gtk.Label("")229 hbox.pack_start(self.prev_sig, gtk.FALSE, gtk.TRUE, 0)230231 hbox = gtk.HBox (gtk.FALSE, 3)232 vbox2.pack_start(hbox, gtk.FALSE, gtk.TRUE, 0)233 label = gtk.Label("Second previous signal:")234 hbox.pack_start(label, gtk.FALSE, gtk.TRUE, 0)235 self.prev2_sig = gtk.Label("")236 hbox.pack_start(self.prev2_sig, gtk.FALSE, gtk.TRUE, 0)237238 bbox = gtk.HButtonBox ()239 vbox.pack_start(bbox, gtk.FALSE, gtk.FALSE, 0)240 bbox.set_layout(gtk.BUTTONBOX_END)241242 button = gtk.Button("Close")243 button.connect("clicked", gtk.mainquit)244 bbox.add(button)245 button.set_flags(gtk.CAN_DEFAULT)246 button.grab_default()247248 window.show_all()249250 def main():251 gtk.main()252 return 0253

104

Page 113: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

254 if __name__ == "__main__":255 CalendarExample()256 main()

9.13. Selección de ColorEl control de selección de color es, como cabe de esperar, un control para seleccionar colores interactivamente.Este control compuesto permite al usuario seleccionar un color manipulando triples RGB (Rojo, Verde, Azul)y HSV (Tono, Saturación, Valor). Esto se consigue ajustando valores simples con deslizadores o entradas, ohaciendo clic en el color deseado en una rueda de tono-saturación y una barra de valor. Opcionalmente, la opacidaddel color también se puede especificar.

El control de selección de color solo emite una señal por ahora, "color_changed", que se emite siempre que elcolor actual del control cambie, bien porque el usuario lo cambia o porque se especifique explícitamente a travésdel métodoset_color () .

Veamos lo que nos ofrece el control de selección de color. El control viene en dos sabores:GtkColorSelection

y GtkColorSelectionDialog .

colorsel = gtk.ColorSelection()

Probablemente no uses este constructor directamente. Crea un controlColorSelection huerfano que tendrásque apadrinar tu mismo. El controlColorSelection hereda del controlVBox .

colorseldlg = gtk.ColorSelectionDialog( title )

dondetitle (título) es una cadena usada para la barra de título del diálogo.

Este es el constructor más común del selector de color. Crea unColorSelectionDialog . Este consiste enun Frame que contiene un controlColorSelection , un HSeparator y un HBox con tres botones, "Ok","Cancelar" y "Ayuda". Puedes obtener estos botones accediendo a los atributosok_button , cancel_button

y help_button del ColorSelectionDialog , (por ejemplo, colorseldlg.ok_button ). El controlColorSelection es accesible usando la variablecolorsel :

colorsel = colorseldlg.colorsel

El controlColorSelection tiene unos cuantos métodos que cambian sus características o proporcionan accesoa la selección de color.

colorsel.set_has_opacity_control( has_opacity )

El control de selección de color permite ajustar la opacidad de un color (también conocida como el canal alfa).Esto esta desactivado por defecto. Llamando a este método conhas_opacity igual TRUE activa la opacidad.De la misma forma,has_opacity igual a FALSE desactivará la opacidad.

colorsel.set_current_color( color )

105

Page 114: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

colorsel.set_current_alpha( alpha )

Puedes poner el color actual explícitamente llamando al métodoset_current_color () con unGdkColor .La opacidad (canal alfa) se pone con el métodoset_current_alpha ().El canal alfa debe estar entre 0(completamente transparente) y 65636 (completamente opaco).

color = colorsel.get_current_color()alpha = colorsel.get_current_alpha()

Cuando tengas que mirar el color actual, típicamente al recibir la señal "color_changed", puedes usar estosmétodos.

El programa de ejemplocolorsel.py[examples/colorsel.py] demuestra el uso delColorSelectionDialog . Esteprograma muestra una ventana que contiene un área de dibujo. Al hacer clic en ella se abre un diálogo de selecciónde color, y cambiando el color en dicho diálogo se cambia el color de fondo. La figura Figura 9.13. Ejemplo deDiálogo de Selección de Color muestra el programa en acción:

Figura 9.13. Ejemplo de Diálogo de Selección de Color

106

Page 115: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

El código fuente decolorsel.py[examples/colorsel.py] es:

1 #!/usr/bin/env python23 # example colorsel.py45 import gtk67 class ColorSelectionExample:8 # Color changed handler9 def color_changed_cb(self, widget):

10 # Get drawingarea colormap11 colormap = self.drawingarea.get_colormap()1213 # Get current color14 color = self.colorseldlg.colorsel.get_current_color()1516 # Set window background color17 self.drawingarea.modify_bg(gtk.STATE_NORMAL, color)1819 # Drawingarea event handler20 def area_event(self, widget, event):21 handled = gtk.FALSE2223 # Check if we’ve received a button pressed event24 if event.type == gtk.gdk.BUTTON_PRESS:25 handled = gtk.TRUE2627 # Create color selection dialog28 if self.colorseldlg == None:29 self.colorseldlg = gtk.ColorSelectionDialog(30 "Select background color")3132 # Get the ColorSelection widget33 colorsel = self.colorseldlg.colorsel3435 colorsel.set_previous_color(self.color)36 colorsel.set_current_color(self.color)37 colorsel.set_has_palette(gtk.TRUE)3839 # Connect to the "color_changed" signal40 colorsel.connect("color_changed", self.color_changed_cb)41 # Show the dialog42 response = self.colorseldlg.run()4344 if response -- gtk.RESPONSE_OK:45 self.color = colorsel.get_current_color()46 else:47 self.drawingarea.modify_bg(gtk.STATE_NORMAL, self.color)4849 self.colorseldlg.hide()5051 return handled5253 # Close down and exit handler54 def destroy_window(self, widget, event):55 gtk.mainquit()56 return gtk.TRUE

107

Page 116: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

5758 def __init__(self):59 self.colorseldlg = None60 # Create toplevel window, set title and policies61 window = gtk.Window(gtk.WINDOW_TOPLEVEL)62 window.set_title("Color selection test")63 window.set_resizable(gtk.TRUE)6465 # Attach to the "delete" and "destroy" events so we can exit66 window.connect("delete_event", self.destroy_window)6768 # Create drawingarea, set size and catch button events69 self.drawingarea = gtk.DrawingArea()7071 self.color = self.drawingarea.get_colormap().alloc_color(0, 65535, 0)7273 self.drawingarea.set_size_request(200, 200)74 self.drawingarea.set_events(gtk.gdk.BUTTON_PRESS_MASK)75 self.drawingarea.connect("event", self.area_event)7677 # Add drawingarea to window, then show them both78 window.add(self.drawingarea)79 self.drawingarea.show()80 window.show()8182 def main():83 gtk.main()84 return 08586 if __name__ == "__main__":87 ColorSelectionExample()88 main()

9.14. Selectores de FicheroEl control de selección de fichero es una forma rápida y fácil de mostrar una caja de diálogo de Fichero. Vienecon botones Ok, Cancelar y Ayuda, por lo que es estupendo para ahorrase tiempo de programación.

Para crear una nueva caja de selección de fichero usa:

filesel = gtk.FileSelection( title =None)

Para fijar el nombre de fichero, por ejemplo para mostrar un directorio específico, o establecer un ficheropredeterminado, usa este método:

filesel.set_filename( filename )

Para obtener el nombre de fichero que el usuario ha escrito o seleccionado, usa este método:

filename = filesel.get_filename()

108

Page 117: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

También hay referencias a los controles contenidos en el control de selección de ficheros. Estos son los atributos:

filesel.dir_list # lista de directoriosfilesel.file_list # lista de ficherosfilesel.selection_entry # entrada de selecciónfilesel.selection_text # texto de selecciónfilesel.main_vbox # caja vertical principalfilesel.ok_button # botón okfilesel.cancel_button # botón cancelarfilesel.help_button # botón ayudafilesel.history_pulldown # lista de historiafilesel.history_menu # menú de historiafilesel.fileop_dialogfilesel.fileop_entryfilesel.fileop_filefilesel.fileop_c_dirfilesel.fileop_del_filefilesel.fileop_ren_filefilesel.button_area # área de botonesfilesel.action_area # área de acción

Lo más probable es que quieras usar los atributosok_button , cancel_button , y help_button para conectarsus señales a tus retrollamadas.

El programa de ejemplofilesel.py[examples/filesel.py] ilustra el uso del control de selección de ficheros. Comoverás, no hay mucho más que decir para crear un control de selección de ficheros. Aunque en este ejemplo elbotón Ayuda aparece en pantalla, no hace nada porque no hay ninguna señal conectada a él. La figura Figura 9.14.Ejemplo de Selección de Ficheros muestra la pantalla resultante:

109

Page 118: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

Figura 9.14. Ejemplo de Selección de Ficheros

El código fuente de filesel.py es:

1 #!/usr/bin/env python23 # example filesel.py45 import gtk67 class FileSelectionExample:8 # Get the selected filename and print it to the console9 def file_ok_sel(self, w):

10 print "%s" % self.filew.get_filename()1112 def destroy(self, widget):13 gtk.mainquit()1415 def __init__(self):16 # Create a new file selection widget17 self.filew = gtk.FileSelection("File selection")18

110

Page 119: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

19 self.filew.connect("destroy", self.destroy)20 # Connect the ok_button to file_ok_sel method21 self.filew.ok_button.connect("clicked", self.file_ok_sel)2223 # Connect the cancel_button to destroy the widget24 self.filew.cancel_button.connect("clicked",25 lambda w: self.filew.destroy())2627 # Lets set the filename, as if this were a save dialog,28 # and we are giving a default filename29 self.filew.set_filename("penguin.png")3031 self.filew.show()3233 def main():34 gtk.main()35 return 03637 if __name__ == "__main__":38 FileSelectionExample()39 main()

9.15. Diálogo de Selección de FuentesEl Diálogo de Selección de Fuentes permite al usuario seleccionar una fuente de forma interactiva. El diálogocontiene un controlFontSelection y botones de "OK y "Cancelar". Un botón de ’Aplicar’ también estádisponible en el diálogo, pero inicialmente está oculto. El Diálogo de Selección de Fuentes permite al usuarioseleccionar una fuente de las fuentes de sistema disponibles (las mismas que se obtienen al usarxlsfonts)).

La figura Figura 9.15. Diálogo de Selección de Fuentes ilustra unFontSelectionDialog :

111

Page 120: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

Figura 9.15. Diálogo de Selección de Fuentes

El diálogo contiene un conjunto de tres fichas que proporcionan:

• un interface para seleccionar la fuente, el estilo y el tamaño

• información detallada sobre la fuente seleccionada

• un interfaz para el mecanismo de filtrado de fuente que restringe las fuentes disponibles para seleccionar

La función para crear unFontSelectionDialog es:

fontseldlg = gtk.FontSelectionDialog( title )

El title (título) es una cadena que se usará en el texto de la barra de título.

Una instancia de un Diálogo de Selección de Fuentes tiene varios atributos:

fontselmain_vboxaction_areaok_buttonapply_button

112

Page 121: Tutorial Pygtk

Capítulo 9. Miscelanea de Controles

cancel_button

El atributo fontsel es una referencia al control de selección de fuente.main_vbox es una referencia a lagtk.VBox que contiene elfontsel y el action_area en el diálogo. El atributoaction_area es una referenciaa la gtk.HButtonBox que contiene los botones de ’OK’, ’Aplicar’ y ’Cancelar’. Los atributosok_button ,cancel_button y apply_button son referencias a los botones ’OK’, ’Cancelar’ y ’Aplicar’ que se puedenusar para realizar las conexiones a las señales de los botones. La referenciaapply_button también se puedeusar para mostrar el botón mediante el métodoshow() .

Puedes fijar la fuente inicial que se mostrará en el diálogo usando el método:

fontseldlg.set_font_name( fontname )

El argumentofontname es el nombre de una fuente de sistema completa o parcialmente especificado. Porejemplo:

fontseldlg.set_font_name(’-adobe-courier-bold-*-*-*-*-120-*-*-*-*-*-*’)

especifica una fuente inicial parcialmente.

El nombre de la fuente seleccionada se puede obtener con el método:

font_name = fontseldlg.get_font_name()

El Diálogo de Selección de Fuentes tiene un área de previsualización que muestra texto usando la fuenteseleccionada. El texto que se usa en el área de previsualización se puede establecer con el método:

fontseldlg.set_preview_text( text )

El texto de previsualización se puede obtener con el método:

text = fontseldlg.get_preview_text()

El programa de ejemplocalendar.py [examples/calendar.py] usa un diálogo de selección de fuentes para selec-cionar la fuente que se usa para mostrar la información del calendario. Las lineas 105-110 definen una retrollamadapara obtener el nombre de la fuente a partir del Diálogo de Selección de Fuentes y lo usa para fijar la fuente parael control del calendario. Las lineas 112-131 definen el método que crea un Diálogo de Selección de Fuentes,configura las retrollamadas para los botones ’OK’ y ’Cancelar’ y muestra el diálogo.

113

Page 122: Tutorial Pygtk

Capítulo 10. Controles Contenedores10.1. La Caja de EventosAlgunos controles GTK no tienen ventanas X asociadas, por lo que simplemente se dibujan encima de sus padres.A causa de esto, no pueden recibir eventos y si se dimensionan incorrectamente, no pueden recortarse por lo quepuedes ver partes mal, etc. Si necesitas más de estos controles, laEventBox (Caja de Eventos) es para ti.

A primera vista, el controlEventBox puede aparecer completamente inútil. No dibuja nada en la pantalla y noresponde a ningún evento. Sin embargo, tiene una función - proporciona una ventana X a sus controles hijos. Estoes importante ya que muchos controles GTK no tienen una ventana X asociada. No tener una ventana X ahorramemoria y mejora el rendimiento, pero también tiene inconvenientes. Un control sin ventana X no puede recibireventos, no realiza ningun recortado sobre sus contenidos y no puede establecer su color de fondo. Aunque elnombreEventBox enfatiza la función de manejador de eventos, el control también se puede usar para recorte. (ymás, mira el ejemplo más abajo).

Para crear un nuevo controlEventBox , usa:

event_box = gtk.EventBox()

Un control hijo se puede añadir a estaevent_box :

event_box.add( widget )

El programa de ejemploeventbox.py[examples/eventbox.py] muestra dos usos de unEventBox - una etiquetaes creada y se recorta en una caja pequeña, tiene un fondo verde y se ha configurado para que un clic de ratón enla etiqueta haga que el programa termine. Al redimensionar la ventana se cambian cantidades en la etiqueta. Lafigura Figura 10.1. Ejemplo de Caja de Eventos muestra la ventana del programa:

Figura 10.1. Ejemplo de Caja de Eventos

114

Page 123: Tutorial Pygtk

Capítulo 10. Controles Contenedores

El código fuente deeventbox.py[examples/eventbox.py] es:

1 #!/usr/bin/env python23 # example eventbox.py45 import gtk67 class EventBoxExample:8 def __init__(self):9 window = gtk.Window(gtk.WINDOW_TOPLEVEL)

10 window.set_title("Event Box")11 window.connect("destroy", gtk.mainquit)12 window.set_border_width(10)1314 # Create an EventBox and add it to our toplevel window15 event_box = gtk.EventBox()16 window.add(event_box)17 event_box.show()1819 # Create a long label20 label = gtk.Label("Click here to quit, quit, quit, quit, quit")21 event_box.add(label)22 label.show()2324 # Clip it short.25 label.set_size_request(110, 20)2627 # And bind an action to it28 event_box.set_events(gtk.gdk.BUTTON_PRESS_MASK)29 event_box.connect("button_press_event", gtk.mainquit)3031 # More things you need an X window for ...32 event_box.realize()33 event_box.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.HAND1))3435 # Set background color to green36 event_box.modify_bg(gtk.STATE_NORMAL,37 event_box.get_colormap().alloc_color(’green’))3839 window.show()4041 def main():42 gtk.main()43 return 04445 if __name__ == "__main__":46 EventBoxExample()47 main()

10.2. El control AlineadorEl controlAlignment (Alineador) te permite colocar un control dentro de su ventana en una posición y un tamañorelativos al tamaño del propio controlAlignment . Por ejemplo, puede ser útil para centrar un control dentro dela ventana.

115

Page 124: Tutorial Pygtk

Capítulo 10. Controles Contenedores

Sólo hay dos llamadas asociadas al controlAlignment :

alignment = gtk.Alignment( xalign =0.0, yalign =0.0, xscale =0.0, yscale =0.0)

alignment.set( xalign , yalign , xscale , yscale )

La funcióngtk.Alignment () crea un nuevo controlAlignment con los parámetros especificados. El métodoset () permite alterar los parámetros de alineación de un controlAlignment existente.

Los cuatro parámetros son números en coma flotante que pueden estar entre 0.0 y 1.0. Los argumentosxalign yyalign afectan a la posición del control dentro delAlignment . Los argumentosxscale y yscale afectan a lacantidad de espacio reservada al control.

Un control hijo puede añadirse a esteAlignment usando:

alignment.add( widget )

Para un ejemplo del uso de un controlAlignment , consulta el ejemplo del control de Barra de Progresoprogressbar.py [examples/progressbar.py]

10.3. Contenedor FijoEl contenedorFixed (Fijo) te permite colocar controles en una posición fija dentro de su ventana, relativa a suesquina superior izquierda. La posición de los controles se puede cambiar dinámicamente.

Sólo hay dos llamadas asociadas al control fijo:

fixed = gtk.Fixed()

fixed.put( widget , x , y)

fixed.move( widget , x , y)

La funcióngtk.Fixed () te permite crear un nuevo contenedorFixed .

El métodoput () coloca al control en el contenedor fijo en la posición especificada porx e y .

El métodomove() te permite mover el control especificado a una nueva posición.

El ejemplo fixed.py [examples/fixed.py] ilustra cómo usar el contenedorFixed . La figura Figura 10.2. Ejemplode Fijo muestra el resultado:

116

Page 125: Tutorial Pygtk

Capítulo 10. Controles Contenedores

Figura 10.2. Ejemplo de Fijo

El código fuente defixed.py [examples/fixed.py] es:

1 #!/usr/bin/env python23 # example fixed.py45 import gtk67 class FixedExample:8 # This callback method moves the button to a new position9 # in the Fixed container.

10 def move_button(self, widget):11 self.x = (self.x+30)%30012 self.y = (self.y+50)%30013 self.fixed.move(widget, self.x, self.y)1415 def __init__(self):16 self.x = 5017 self.y = 501819 # Create a new window20 window = gtk.Window(gtk.WINDOW_TOPLEVEL)21 window.set_title("Fixed Container")2223 # Here we connect the "destroy" event to a signal handler

117

Page 126: Tutorial Pygtk

Capítulo 10. Controles Contenedores

24 window.connect("destroy", gtk.mainquit)2526 # Sets the border width of the window.27 window.set_border_width(10)2829 # Create a Fixed Container30 self.fixed = gtk.Fixed()31 window.add(self.fixed)32 self.fixed.show()3334 for i in range(1, 4):35 # Creates a new button with the label "Press me"36 button = gtk.Button("Press me")3738 # When the button receives the "clicked" signal, it will call the39 # method move_button().40 button.connect("clicked", self.move_button)4142 # This packs the button into the fixed containers window.43 self.fixed.put(button, i*50, i*50)4445 # The final step is to display this newly created widget.46 button.show()4748 # Display the window49 window.show()5051 def main():52 # Enter the event loop53 gtk.main()54 return 05556 if __name__ == "__main__":57 FixedExample()58 main()

10.4. Contenedor de DisposiciónEl contenedorLayout (Disposición) es similar al contenedorFixed (Fijo) excepto en que implementa un áreade desplazamiento infinita (donde infinito es menor que 2^32). El sistema de ventanas X tiene una limitación quehace uqe las ventanas sólo puedan tener 32767 píxeles de ancho o alto. El contenedorLayout soluciona estalimitación haciendo algunos trucos exóticos mediante el uso de gravedad de ventana y de bit, para que tengas undesplazamiento suave incluso cuando tienes muchos controles en tu área de desplazamiento.

Un contenedorLayout se crea con:

layout = gtk.Layout( hadjustment =None, vadjustment =None)

Como puedes ver, puedes especificar opcionalmente los objetosAdjustment que el controlLayout usará parasu desplazamiento. Si no especificas los objetosAdjustment , unos nuevos se crearán.

Puedes añadir y mover controles en el contenedorLayout usando los dos siguientes métodos:

layout.put( child_widget , x , y)

118

Page 127: Tutorial Pygtk

Capítulo 10. Controles Contenedores

layout.move( child_widget , x , y)

El tamaño del contenedorLayout se puede establecer y consultar usando los siguientes métodos:

layout.set_size( width , height )

size = layout.get_size()

Los últimos cuatro métodos para los controlesLayout widgets son para manipular los controles de ajustehorizontal y vertical:

hadj = layout.get_hadjustment()

vadj = layout.get_vadjustment()

layout.set_hadjustment( adjustment )

layout.set_vadjustment( adjustment )

El programa de ejemplolayout.py [examples/layout.py] crea tres botones y los pone en un control disposición.Cuando se hace clic en un botón, se mueve a una posición aleatoria en el control disposición. La figura Figura10.3. Ejemplo de Disposición ilustra la ventana inicial del programa:

119

Page 128: Tutorial Pygtk

Capítulo 10. Controles Contenedores

Figura 10.3. Ejemplo de Disposición

El código fuente delayout.py [examples/layout.py] es:

1 #!/usr/bin/env python23 # example layout.py45 import gtk6 import whrandom78 class LayoutExample:9 def WindowDeleteEvent(self, widget, event):

10 # return false so that window will be destroyed11 return gtk.FALSE1213 def WindowDestroy(self, widget, *data):14 # exit main loop15 gtk.mainquit()1617 def ButtonClicked(self, button):18 # move the button19 self.layout.move(button, whrandom.randint(0,500),20 whrandom.randint(0,500))2122 def __init__(self):23 # create the top level window

120

Page 129: Tutorial Pygtk

Capítulo 10. Controles Contenedores

24 window = gtk.Window(gtk.WINDOW_TOPLEVEL)25 window.set_title("Layout Example")26 window.set_default_size(300, 300)27 window.connect("delete-event", self.WindowDeleteEvent)28 window.connect("destroy", self.WindowDestroy)29 # create the table and pack into the window30 table = gtk.Table(2, 2, gtk.FALSE)31 window.add(table)32 # create the layout widget and pack into the table33 self.layout = gtk.Layout(None, None)34 self.layout.set_size(600, 600)35 table.attach(self.layout, 0, 1, 0, 1, gtk.FILL|gtk.EXPAND,36 gtk.FILL|gtk.EXPAND, 0, 0)37 # create the scrollbars and pack into the table38 vScrollbar = gtk.VScrollbar(None)39 table.attach(vScrollbar, 1, 2, 0, 1, gtk.FILL|gtk.SHRINK,40 gtk.FILL|gtk.SHRINK, 0, 0)41 hScrollbar = gtk.HScrollbar(None)42 table.attach(hScrollbar, 0, 1, 1, 2, gtk.FILL|gtk.SHRINK,43 gtk.FILL|gtk.SHRINK, 0, 0)44 # tell the scrollbars to use the layout widget’s adjustments45 vAdjust = self.layout.get_vadjustment()46 vScrollbar.set_adjustment(vAdjust)47 hAdjust = self.layout.get_hadjustment()48 hScrollbar.set_adjustment(hAdjust)49 # create 3 buttons and put them into the layout widget50 button = gtk.Button("Press Me")51 button.connect("clicked", self.ButtonClicked)52 self.layout.put(button, 0, 0)53 button = gtk.Button("Press Me")54 button.connect("clicked", self.ButtonClicked)55 self.layout.put(button, 100, 0)56 button = gtk.Button("Press Me")57 button.connect("clicked", self.ButtonClicked)58 self.layout.put(button, 200, 0)59 # show all the widgets60 window.show_all()6162 def main():63 # enter the main loop64 gtk.main()65 return 06667 if __name__ == "__main__":68 LayoutExample()69 main()

10.5. MarcosLos Marcos se pueden usar para encerrar un widget o un grupo de ellos dentro de una caja que, opcionalmentepuede llevar un título. La posición del título y el estilo de la caja se puede alterar a tu gusto.

Un Frame (Marco) se puede crear con la siguiente función

frame = gtk.Frame( label =None)

121

Page 130: Tutorial Pygtk

Capítulo 10. Controles Contenedores

El label (título) se coloca en la esquina superior izquierda del marco de manera predeterminada. Especificandoun valor de None para el argumentolabel o sin especificar el argumentolabel hará que no se visualice ningúntítulo. El texto del título se puede cambiar usando el método:

frame.set_label( label )

La posición del título se puede cambiar usando el método:

frame.set_label_align( xalign , yalign )

xalign y yalign toman valores entre 0.0 y 1.0.xalign indica la posición del título en la horizontal superior delmarco.yalign no se usa por ahora. El valor por defecto dexalign es 0.0 lo que coloca al título en la esquinaizquierda del marco.

El siguiente método modifica el estilo de la caja que se usa para rodear el marco.

frame.set_shadow_type( type )

El argumentotype puede tomar uno de los siguientes valores:

SHADOW_NONE # sin sombraSHADOW_IN # sombra hacia dentroSHADOW_OUT # sombra hacia fueraSHADOW_ETCHED_IN # sombra abrupta hacia dentroSHADOW_ETCHED_OUT # sombra abrupta hacia fuera (valor predeterminado)

El ejemploframe.py [examples/frame.py] muestra el uso del control Marco. La figura Figura 10.4. Ejemplo deMarco muestra la ventana resultante:

122

Page 131: Tutorial Pygtk

Capítulo 10. Controles Contenedores

Figura 10.4. Ejemplo de Marco

EL código fuente deframe.py [examples/frame.py] es:

1 #!/usr/bin/env python23 # example frame.py45 import gtk67 class FrameExample:8 def __init__(self):9 # Create a new window

10 window = gtk.Window(gtk.WINDOW_TOPLEVEL)11 window.set_title("Frame Example")1213 # Here we connect the "destroy" event to a signal handler14 window.connect("destroy", gtk.mainquit)15 window.set_size_request(300, 300)1617 # Sets the border width of the window.18 window.set_border_width(10)1920 # Create a Frame21 frame = gtk.Frame()22 window.add(frame)23

123

Page 132: Tutorial Pygtk

Capítulo 10. Controles Contenedores

24 # Set the frame’s label25 frame.set_label("GTK Frame Widget")2627 # Align the label at the right of the frame28 frame.set_label_align(1.0, 0.0)2930 # Set the style of the frame31 frame.set_shadow_type(gtk.SHADOW_ETCHED_OUT)32 frame.show()3334 # Display the window35 window.show()3637 def main():38 # Enter the event loop39 gtk.main()40 return 04142 if __name__ == "__main__":43 FrameExample()44 main()

Los programascalendar.py [examples/calendar.py],label.py [examples/label.py] yspinbutton.py [exam-ples/spinbutton.py] también usan Marcos.

10.6. Marcos ProporcionalesEl control de marco proporcional es como un control de marco, excepto en que también mantiene el cociente deproporcionalidad (esto es, el cociente del ancho entre el alto) del control hijo a un determinado valor, añadiendoespacio extra si es necesario. Esto es útil, por ejemplo, cuando quieres previsualizar una imágen más grande.El tamaño de la previsualización debería variar cuando el usuario redimensione la ventana, pero el cociente deproporcionalidad debe siempre corresponderse al de la imágen original.

Para crear un nuevo marco proporcional utiliza:

aspect_frame = gtk.AspectFrame( label =None, xalign =0.5, yalign =0.5, ratio =1.0, ←↩obey_child =TRUE)

label especifica el texto a mostrar en el título.xalign y yalign especifican la alineación como en el controlAlignment . Si obey_child es TRUE, el cociente de proporcionalidad de un control hijo se corresponderá conel cociente de proporcionalidad del tamaño ideal que solicite. En caso contrario, se especifica dicho cociente conel argumentoratio .

Para cambiar las opciones de un marco proporcional existente, puedes utilizar:

aspect_frame.set( xalign =0.0, yalign =0.0, ratio =1.0, obey_child =TRUE)

Como ejemplo, el programaaspectframe.py[examples/aspectframe.py] usa unAspectFrame para presentarun área de dibujo cuyo cociente de proporcionalidad será siempre 2:1, no importa si el usuario redimensiona laventana contenedora. La figura Figura 10.5. Ejemplo de Marco Proporcional muestra la ventana del programa:

124

Page 133: Tutorial Pygtk

Capítulo 10. Controles Contenedores

Figura 10.5. Ejemplo de Marco Proporcional

El código fuente del programaaspectframe.py[examples/aspectframe.py] es:

1 #!/usr/bin/env python23 # example aspectframe.py45 import gtk67 class AspectFrameExample:8 def __init__(self):9 window = gtk.Window(gtk.WINDOW_TOPLEVEL);

10 window.set_title("Aspect Frame")11 window.connect("destroy", gtk.mainquit)12 window.set_border_width(10)1314 # Create an aspect_frame and add it to our toplevel window15 aspect_frame = gtk.AspectFrame("2x1", # label16 0.5, # center x17 0.5, # center y18 2, # xsize/ysize = 219 gtk.FALSE) # ignore child’s aspect20 window.add(aspect_frame)21 aspect_frame.show()2223 # Now add a child widget to the aspect frame24 drawing_area = gtk.DrawingArea()2526 # Ask for a 200x200 window, but the AspectFrame will give us a 200x10027 # window since we are forcing a 2x1 aspect ratio28 drawing_area.set_size_request(200, 200)29 aspect_frame.add(drawing_area)

125

Page 134: Tutorial Pygtk

Capítulo 10. Controles Contenedores

30 drawing_area.show()31 window.show()3233 def main():34 gtk.main()35 return 03637 if __name__ == "__main__":38 AspectFrameExample()39 main()

10.7. Controles de PanelLos controles de paneles son útiles cuando quieres dividir un área en dos partes, con el tamaño relativo de lasdos partes controlado por el usuario. Una barra se dibuja entre las dos partes con un mango que el usuario puedearrastrar para cambiar la relación. La división puede ser horizontal (HPaned) o vertical (VPaned).

Para crear una nueva ventana con panel, haz una llamada a uno de:

hpane = gtk.HPaned()

o

vpane = gtk.VPaned()

Después de crear el control de panel, tienes que añadir hijos a sus dos mitades. Para hacer eso, usa los métodos:

paned.add1( child )

paned.add2( child )

El métodoadd1 () añade el control hijo a la izquierda o arriba del control de panel. EL métodoadd2 () añade uncontrol hijo a la derecha o abajo del panel.

El programa de ejemplopaned.py [examples/paned.py] crea parte de la interfaz de usuario de un programa decorreo electrónico imaginario. Una ventana se divide en dos partes verticalmente, donde la parte de arriba es unalista de correos electrónicos y la parte de abajo es el texto del mensaje electrónico. La mayoría del programa esbastante sencillo. Hay un par de puntos en los que hacer hincapie: no se puede añadir texto a un control Text hastaque se realiza. Esto podría conseguirse llamando al métodorealize () , pero como demostración de una técnicaalternativa, conectamos un manejador para la señal "realize" para añadir el texto. Además, necesitamos añadir laopciónSHRINK (encoger) a algunos de los elementos de la tabla que contiene la ventana de texto y sus barras dedesplazamiento, para que cuando la parte de abajo se haga más pequeña, las secciones correctas se encojan envez de que desaparezcan por la parte de abajo de la ventana. La figura Figura 10.6. Ejemplo de Panel muestra elresultado de ejecutar el programa:

126

Page 135: Tutorial Pygtk

Capítulo 10. Controles Contenedores

Figura 10.6. Ejemplo de Panel

El código fuente del programapaned.py[examples/paned.py] es:

1 #!/usr/bin/env python23 # example paned.py45 import gtk, gobject67 class PanedExample:8 # Create the list of "messages"9 def create_list(self):

10 # Create a new scrolled window, with scrollbars only if needed11 scrolled_window = gtk.ScrolledWindow()12 scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)13

127

Page 136: Tutorial Pygtk

Capítulo 10. Controles Contenedores

14 model = gtk.ListStore(gobject.TYPE_STRING)15 tree_view = gtk.TreeView(model)16 scrolled_window.add_with_viewport (tree_view)17 tree_view.show()1819 # Add some messages to the window20 for i in range(10):21 msg = "Message #%d" % i22 iter = model.append()23 model.set(iter, 0, msg)2425 cell = gtk.CellRendererText()26 column = gtk.TreeViewColumn("Messages", cell, text=0)27 tree_view.append_column(column)2829 return scrolled_window3031 # Add some text to our text widget - this is a callback that is invoked32 # when our window is realized. We could also force our window to be33 # realized with GtkWidget.realize, but it would have to be part of a34 # hierarchy first35 def insert_text(self, buffer):36 iter = buffer.get_iter_at_offset(0)37 buffer.insert(iter,38 "From: [email protected]\n"39 "To: [email protected]\n"40 "Subject: Made it!\n"41 "\n"42 "We just got in this morning. The weather has been\n"43 "great - clear but cold, and there are lots of fun sights.\n"44 "Sojourner says hi. See you soon.\n"45 " -Path\n")4647 # Create a scrolled text area that displays a "message"48 def create_text(self):49 view = gtk.TextView()50 buffer = view.get_buffer()51 scrolled_window = gtk.ScrolledWindow()52 scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)53 scrolled_window.add(view)54 self.insert_text(buffer)55 scrolled_window.show_all()56 return scrolled_window5758 def __init__(self):59 window = gtk.Window(gtk.WINDOW_TOPLEVEL)60 window.set_title("Paned Windows")61 window.connect("destroy", gtk.mainquit)62 window.set_border_width(10)63 window.set_size_request(450, 400)6465 # create a vpaned widget and add it to our toplevel window66 vpaned = gtk.VPaned()67 window.add(vpaned)68 vpaned.show()6970 # Now create the contents of the two halves of the window71 list = self.create_list()72 vpaned.add1(list)

128

Page 137: Tutorial Pygtk

Capítulo 10. Controles Contenedores

73 list.show()7475 text = self.create_text()76 vpaned.add2(text)77 text.show()78 window.show()7980 def main():81 gtk.main()82 return 08384 if __name__ == "__main__":85 PanedExample()86 main()

10.8. Puertos de VisiónEs poco probable que necesites usar el controlViewport (Puerto de Visión) directamente. Es mucho más probableque uses el controlScrolledWindow , el cual usa unViewport .

Un control de puerto de visión te permite colocar un control más grande dentro de él de tal forma que puedes veruna parte de él de una vez. UsaAdjustments para definir el área que se ve.

Un Viewport se crea con la función:

viewport = gtk.Viewport( hadjustment =None, vadjustment =None)

Como puedes ver puedes especificar losAdjustments horizontal y vertical que el control usa cuando lo crees.Creará sus propios ajustes si le pasas None como el valor de los argumentos o simplemente no le pasas argumentos.

Puedes consultar y fijar los ajustes después de que el control se haya creado usando los siguientes cuatro métodos:

viewport.get_hadjustment()

viewport.get_vadjustment()

viewport.set_hadjustment( adjustment )

viewport.set_vadjustment( adjustment )

El otro método que se usa para modificar la apariencia es:

viewport.set_shadow_type( type )

Los valores posibles para el parámetrotype son:

SHADOW_NONE # sin sombraSHADOW_IN # sombra hacia adentroSHADOW_OUT # sombra hacia afueraSHADOW_ETCHED_IN # sombra abrupta hacia adentro

129

Page 138: Tutorial Pygtk

Capítulo 10. Controles Contenedores

SHADOW_ETCHED_OUT # sombra abrupta hacia fuera

10.9. Ventanas de DesplazamientoLas ventanas de desplazamiento se usan para crear un área de desplazamiento con otro control dentro de ella.Puede insertar cualquier tipo de control dentro de una ventana de desplazamiento, y será accesible da igual sutamaño usando barras de desplazamiento.

La siguiente función se usa para crear una nueva ventana de desplazamiento.

scrolled_window = gtk.ScrolledWindow( hadjustment =None, vadjustment =None)

Donde el primer argumento es el ajuste para la dirección horizontal, y el segundo, el ajuste para la direcciónvertical. Casi siempre se ponen a None o no se especifican.

scrolled_window.set_policy( hscrollbar_policy , vscrollbar_policy )

Este método especifica la política a usar con respecto a las barras de desplazamiento. El primer argumento le fijala política a la barra de desplazamiento horizontal, y el segundo, la política de la barra de desplazamiento vertical.

La política puede tomar los valores POLICY_AUTOMATIC o POLICY_ALWAYS. POLICY_AUTOMATICautomáticamente decidirá si es necesario las barras de desplazamiento, mientras que POLICY_ALWAYS siempredejará las barras de desplazamiento visibles.

Entonces puedes colocar tu objeto dentro de la ventana de desplazamiento usando el siguiente método.

scrolled_window.add_with_viewport( child )

El programa de ejemploscrolledwin.py [examples/scrolledwin.py] coloca una tabla con 100 botones biestadodentro de una ventana de desplazamiento. Sólo he comantado las partes que pueden ser nuevas para ti. La figuraFigura 10.7. Ejemplo de Ventana de Desplazamiento muestra la ventana del programa:

130

Page 139: Tutorial Pygtk

Capítulo 10. Controles Contenedores

Figura 10.7. Ejemplo de Ventana de Desplazamiento

El código fuente descrolledwin.py [examples/scrolledwin.py] es:

1 #!/usr/bin/env python23 # example scrolledwin.py45 import gtk67 class ScrolledWindowExample:8 def destroy(self, widget):9 gtk.mainquit()

1011 def __init__(self):12 # Create a new dialog window for the scrolled window to be13 # packed into.14 window = gtk.Dialog()15 window.connect("destroy", self.destroy)16 window.set_title("ScrolledWindow example")17 window.set_border_width(0)18 window.set_size_request(300, 300)1920 # create a new scrolled window.21 scrolled_window = gtk.ScrolledWindow()22 scrolled_window.set_border_width(10)23

131

Page 140: Tutorial Pygtk

Capítulo 10. Controles Contenedores

24 # the policy is one of POLICY AUTOMATIC, or POLICY_ALWAYS.25 # POLICY_AUTOMATIC will automatically decide whether you need26 # scrollbars, whereas POLICY_ALWAYS will always leave the scrollbars27 # there. The first one is the horizontal scrollbar, the second, the28 # vertical.29 scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_ALWAYS)3031 # The dialog window is created with a vbox packed into it.32 window.vbox.pack_start(scrolled_window, gtk.TRUE, gtk.TRUE, 0)33 scrolled_window.show()3435 # create a table of 10 by 10 squares.36 table = gtk.Table(10, 10, gtk.FALSE)3738 # set the spacing to 10 on x and 10 on y39 table.set_row_spacings(10)40 table.set_col_spacings(10)4142 # pack the table into the scrolled window43 scrolled_window.add_with_viewport(table)44 table.show()4546 # this simply creates a grid of toggle buttons on the table47 # to demonstrate the scrolled window.48 for i in range(10):49 for j in range(10):50 buffer = "button (%d,%d)" % (i, j)51 button = gtk.ToggleButton(buffer)52 table.attach(button, i, i+1, j, j+1)53 button.show()5455 # Add a "close" button to the bottom of the dialog56 button = gtk.Button("close")57 button.connect_object("clicked", self.destroy, window)5859 # this makes it so the button is the default.60 button.set_flags(gtk.CAN_DEFAULT)61 window.action_area.pack_start( button, gtk.TRUE, gtk.TRUE, 0)6263 # This grabs this button to be the default button. Simply hitting64 # the "Enter" key will cause this button to activate.65 button.grab_default()66 button.show()67 window.show()6869 def main():70 gtk.main()71 return 07273 if __name__ == "__main__":74 ScrolledWindowExample()75 main()

Prueba a cambiarle el tamaño a la ventana. Notarás cómo reaccionan las barras de desplazamiento. Puede quetambién quieras usar el métodoset_size_request () para fijar el tamaño por defecto de la ventana o de otroscontroles.

132

Page 141: Tutorial Pygtk

Capítulo 10. Controles Contenedores

10.10. Cajas de BotonesLasButtonBoxes (Cajas de Botones) proporcionan un sistema fácil de agrupar rápidamente botones. Vienen envariedades horizontales y verticales. Puedes crear una nuevaButtonBox con una de las siguiente llamadas, quecrean una caja horizontal o vertical, respectivamente:

hbutton_box = gtk.HButtonBox()

vbutton_box = gtk.VButtonBox()

Los únicos métodos de las cajas de botones afectan a la disposición de los botones.

La disposición de los botones dentro de la caja se establece usando:

button_box.set_layout( layout_style )

El argumentolayout_style puede tomar uno de los siguientes valores:

BUTTONBOX_DEFAULT_STYLE # estilo predeterminadoBUTTONBOX_SPREAD # esparcidosBUTTONBOX_EDGE # filoBUTTONBOX_START # principioBUTTONBOX_END # fin

El valor actual delayout_style se puede consultar usando:

layout_style = button_box.get_layout()

Los botones se añaden a laButtonBox usando el típico método delContainer (Contenedor):

button_box.add( widget )

El programa de ejemplobuttonbox.py [examples/buttonbox.py] ilustra todos los tipos de disposición de lasButtonBoxes . La ventana resultante es:

133

Page 142: Tutorial Pygtk

Capítulo 10. Controles Contenedores

El código fuente del programabuttonbox.py [examples/buttonbox.py] es:

1 #!/usr/bin/env python

134

Page 143: Tutorial Pygtk

Capítulo 10. Controles Contenedores

23 # example buttonbox.py45 import gtk67 class ButtonBoxExample:8 # Create a Button Box with the specified parameters9 def create_bbox(self, horizontal, title, spacing, layout):

10 frame = gtk.Frame(title)1112 if horizontal:13 bbox = gtk.HButtonBox()14 else:15 bbox = gtk.VButtonBox()1617 bbox.set_border_width(5)18 frame.add(bbox)1920 # Set the appearance of the Button Box21 bbox.set_layout(layout)22 bbox.set_spacing(spacing)2324 button = gtk.Button(stock=gtk.STOCK_OK)25 bbox.add(button)2627 button = gtk.Button(stock=gtk.STOCK_CANCEL)28 bbox.add(button)2930 button = gtk.Button(stock=gtk.STOCK_HELP)31 bbox.add(button)3233 return frame3435 def __init__(self):36 window = gtk.Window(gtk.WINDOW_TOPLEVEL)37 window.set_title("Button Boxes")3839 window.connect("destroy", gtk.mainquit)4041 window.set_border_width(10)4243 main_vbox = gtk.VBox(gtk.FALSE, 0)44 window.add(main_vbox)4546 frame_horz = gtk.Frame("Horizontal Button Boxes")47 main_vbox.pack_start(frame_horz, gtk.TRUE, gtk.TRUE, 10)4849 vbox = gtk.VBox(gtk.FALSE, 0)50 vbox.set_border_width(10)51 frame_horz.add(vbox)5253 vbox.pack_start(self.create_bbox(gtk.TRUE, "Spread (spacing 40)",54 40, gtk.BUTTONBOX_SPREAD),55 gtk.TRUE, gtk.TRUE, 0)5657 vbox.pack_start(self.create_bbox(gtk.TRUE, "Edge (spacing 30)",58 30, gtk.BUTTONBOX_EDGE),59 gtk.TRUE, gtk.TRUE, 5)60

135

Page 144: Tutorial Pygtk

Capítulo 10. Controles Contenedores

61 vbox.pack_start(self.create_bbox(gtk.TRUE, "Start (spacing 20)",62 20, gtk.BUTTONBOX_START),63 gtk.TRUE, gtk.TRUE, 5)6465 vbox.pack_start(self.create_bbox(gtk.TRUE, "End (spacing 10)",66 10, gtk.BUTTONBOX_END),67 gtk.TRUE, gtk.TRUE, 5)6869 frame_vert = gtk.Frame("Vertical Button Boxes")70 main_vbox.pack_start(frame_vert, gtk.TRUE, gtk.TRUE, 10)7172 hbox = gtk.HBox(gtk.FALSE, 0)73 hbox.set_border_width(10)74 frame_vert.add(hbox)7576 hbox.pack_start(self.create_bbox(gtk.FALSE, "Spread (spacing 5)",77 5, gtk.BUTTONBOX_SPREAD),78 gtk.TRUE, gtk.TRUE, 0)7980 hbox.pack_start(self.create_bbox(gtk.FALSE, "Edge (spacing 30)",81 30, gtk.BUTTONBOX_EDGE),82 gtk.TRUE, gtk.TRUE, 5)8384 hbox.pack_start(self.create_bbox(gtk.FALSE, "Start (spacing 20)",85 20, gtk.BUTTONBOX_START),86 gtk.TRUE, gtk.TRUE, 5)8788 hbox.pack_start(self.create_bbox(gtk.FALSE, "End (spacing 20)",89 20, gtk.BUTTONBOX_END),90 gtk.TRUE, gtk.TRUE, 5)9192 window.show_all()9394 def main():95 # Enter the event loop96 gtk.main()97 return 09899 if __name__ == "__main__":

100 ButtonBoxExample()101 main()

10.11. Barra de HerramientasLas Toolbars (Barras de Herramientas) se usan normalmente para agrupar un número de controles parasimplificar la personalización de su apariencia y disposición. Típicamente una barra de herramientas consisteen botones con iconos, etiquetas y pistas, pero cualquier otro control se puede poner en una barra de herramientas.Finalmente, los elementos se pueden colocar horizontal o verticalmente y se pueden ver los botones con iconos,texto, o ambos.

Crear una barra de herramientas se consigue (como uno podría sospechar) con la siguiente función:

toolbar = gtk.Toolbar()

136

Page 145: Tutorial Pygtk

Capítulo 10. Controles Contenedores

Después de crear una barra de herramientas uno puede añadir al principio, al final o en otro posición, items (textossimples) o elementos (cualquier tipo de control) en la barra de herramientas. Para describir un item necesitamosun texto, un texto para la pista, un texto privado para la pista, un icono para el botón y una retrollamada. Porejemplo, para añadir al principio o al final un item puedes usar los siguientes métodos:

toolbar.append_item( text , tooltip_text , tooltip_private_text , icon , callback ,←↩user_data =None)

toolbar.prepend_item( text , tooltip_text , tooltip_private_text , icon , callback , user_data )

Si quieres usar el métodoinsert_item (), el único parámetro adicional que debe especificarse, es la posición enla cual el item debe insertarse, así:

toolbar.insert_item( text , tooltip_text , tooltip_private_text , icon , callback ,user_data , position )

Para añadir espacios entre items de la barra de herramientas de forma sencilla, puedes usar los siguientes métodos:

toolbar.append_space()

toolbar.prepend_space()

toolbar.insert_space( position )

Si es necesario, la orientación de una barra de herramientas, su estilo y el hecho de que las pistas estén disponibles,se puede cambiar sobre la marcha usando los siguientes métodos:

toolbar.set_orientation( orientation )

toolbar.set_style( style )

toolbar.set_tooltips( enable )

Donde laorientation puede ser ORIENTATION_HORIZONTAL o ORIENTATION_VERTICAL. Elstyle

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 argumentoenable puede ser o TRUE o FALSE.

Para mostrar algunas otras cas que se pueden hacer con una barra de herramientas veamos el siguiente programade ejemplotoolbar.py [examples/toolbar.py] (interrumpiremos el listado con algunas explicaciones adicionales):

1 #!/usr/bin/env python23 # example toolbar.py45 import gtk67 class ToolbarExample:8 # This method is connected to the Close button or9 # closing the window from the WM

10 def delete_event(self, widget, event=None):

137

Page 146: Tutorial Pygtk

Capítulo 10. Controles Contenedores

11 gtk.mainquit()12 return gtk.FALSE13

Este principio debería resultarte familiar si no es tu primer programa PyGTK. No obstante hay una cosa más,vamos a importar un bonito dibujo XPM (gtk.xpm [examples/gtk.xpm]) para que nos sirva de icono para todoslos botones. La linea 8 empieza la clase de ejemploToolbarExample y las lineas 10-12 definen el método deretrollamada que finalizará el programa.

14 # that’s easy... when one of the buttons is toggled, we just15 # check which one is active and set the style of the toolbar16 # accordingly17 def radio_event(self, widget, toolbar):18 if self.text_button.get_active():19 toolbar.set_style(gtk.TOOLBAR_TEXT)20 elif self.icon_button.get_active():21 toolbar.set_style(gtk.TOOLBAR_ICONS)22 elif self.both_button.get_active():23 toolbar.set_style(gtk.TOOLBAR_BOTH)2425 # even easier, just check given toggle button and enable/disable26 # tooltips27 def toggle_event(self, widget, toolbar):28 toolbar.set_tooltips(widget.get_active())29

Las lineas 17-28 son dos métodos de retrollamada que se llamarán cuando uno de los botones de la barra deherramientas se active. Debería conocer cosas como esta si ya has usado botones biestado (y botones de exclusiónmútua).

30 def __init__(self):31 # Here is our main window (a dialog) and a handle for the handlebox32 # Ok, we need a toolbar, an icon with a mask (one for all of33 # the buttons) and an icon widget to put this icon in (but34 # we’ll create a separate widget for each button)35 # create a new window with a given title, and nice size36 dialog = gtk.Dialog()37 dialog.set_title("GTKToolbar Tutorial")38 dialog.set_size_request(450, 250)39 dialog.set_resizable(gtk.TRUE)4041 # typically we quit if someone tries to close us42 dialog.connect("delete_event", self.delete_event)4344 # to make it nice we’ll put the toolbar into the handle box,45 # so that it can be detached from the main window46 handlebox = gtk.HandleBox()47 dialog.vbox.pack_start(handlebox, gtk.FALSE, gtk.FALSE, 5)48

Lo anterior debería ser común a cualquier otra aplicación PyGTK. Inicialización de una instancia deToolbarExample , creación de la ventana, etc. Sólo hay una cosa que probablemente necesite más expli-cación: una caja con mango (HandleBox). Una caja con mango es simplemente otra caja cualquiera que puedeusarse para meter controles dentro. La diferencia entre ella y las cajas típicas es que puede despegarse de la

138

Page 147: Tutorial Pygtk

Capítulo 10. Controles Contenedores

ventana padre (o, de hecho, la caja con mango permanece en el padre, pero se reduce a un rectángulo muypequeño, mientras que todos sus contenidos cambian de padre a una nueva ventana flotante). Normalmente esbueno tener una barra de herramientas desplegable, por lo que estos dos controles suelen ir juntos normalmente.

49 # toolbar will be horizontal, with both icons and text, and50 # with 5pxl spaces between items and finally,51 # we’ll also put it into our handlebox52 toolbar = gtk.Toolbar()53 toolbar.set_orientation(gtk.ORIENTATION_HORIZONTAL)54 toolbar.set_style(gtk.TOOLBAR_BOTH)55 toolbar.set_border_width(5)56 handlebox.add(toolbar)57

Bien, lo que hacemos arriba es una simple inicialización del control de la barra de herramientas.

58 # our first item is (close) button59 iconw = gtk.Image() # icon widget60 iconw.set_from_file("gtk.xpm")61 close_button = toolbar.append_item(62 "Close", # button label63 "Closes this app", # this button’s tooltip64 "Private", # tooltip private info65 iconw, # icon widget66 self.delete_event) # a signal67 toolbar.append_space() # space after item

En el código anterior puedes ver el caso más simple: añadir un botón a la barra de herramientas. Justo antes deañadir el nuevo item, tenemos que construir un control de imágen que servirá como icono para este item; estepaso tiene que repetirse para cada item nuevo. Justo después del item añadimos también espacio, para que lositems siguientes no se tocan unos a otros. Como puedes ver el métodoappend_item () devuelve una referencia anuestro control de botón recién creado, para que podamos trabajar con él de la manera normal.

69 # now, let’s make our radio buttons group...70 iconw = gtk.Image() # icon widget71 iconw.set_from_file("gtk.xpm")72 icon_button = toolbar.append_element(73 gtk.TOOLBAR_CHILD_RADIOBUTTON, # type of element74 None, # widget75 "Icon", # label76 "Only icons in toolbar", # tooltip77 "Private", # tooltip private string78 iconw, # icon79 self.radio_event, # signal80 toolbar) # data for signal81 toolbar.append_space()82 self.icon_button = icon_button

Aqui empezamos a crear un grupo de botones de exclusión mútua. Para hacer esto usamos el métodoappend_element () . De hecho, usando este método, uno puede también puede añadir items normales o inclusoespacios (type = gtk.TOOLBAR_CHILD_SPACE (espacio) o gtk.TOOLBAR_CHILD_BUTTON (botón)). Enel ejemplo anterior hemos empezado a crear un grupo de botones de exclusión mútua. Cuando se crean otros

139

Page 148: Tutorial Pygtk

Capítulo 10. Controles Contenedores

botones de exclusión mútua se necesita una referencia al botón anterior en el grupo, para que se pueda construiruna lista de botones fácilmente (mira la secciónRadioButtons de este tutorial). También tenemos una referenciaal botón en la instancia deToolbarExample para acceder a él más tare.

84 # following radio buttons refer to previous ones85 iconw = gtk.Image() # icon widget86 iconw.set_from_file("gtk.xpm")87 text_button = toolbar.append_element(88 gtk.TOOLBAR_CHILD_RADIOBUTTON,89 icon_button,90 "Text",91 "Only texts in toolbar",92 "Private",93 iconw,94 self.radio_event,95 toolbar)96 toolbar.append_space()97 self.text_button = text_button9899 iconw = gtk.Image() # icon widget

100 iconw.set_from_file("gtk.xpm")101 both_button = toolbar.append_element(102 gtk.TOOLBAR_CHILD_RADIOBUTTON,103 text_button,104 "Both",105 "Icons and text in toolbar",106 "Private",107 iconw,108 self.radio_event,109 toolbar)110 toolbar.append_space()111 self.both_button = both_button112 both_button.set_active(gtk.TRUE)

Creamos otros botones de exclusión mútua de la misma forma excepto que le pasamos uno de los botones creadosal métodoappend_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,impediendonos que los vayamos intercambiando).

114 # here we have just a simple toggle button115 iconw = gtk.Image() # icon widget116 iconw.set_from_file("gtk.xpm")117 tooltips_button = toolbar.append_element(118 gtk.TOOLBAR_CHILD_TOGGLEBUTTON,119 None,120 "Tooltips",121 "Toolbar with or without tips",122 "Private",123 iconw,124 self.toggle_event,125 toolbar)126 toolbar.append_space()127 tooltips_button.set_active(gtk.TRUE)

140

Page 149: Tutorial Pygtk

Capítulo 10. Controles Contenedores

Un botón biestado se puede crear de la forma obvia (si ya has creado botones de exclusión mútua).

129 # to pack a widget into toolbar, we only have to130 # create it and append it with an appropriate tooltip131 entry = gtk.Entry()132 toolbar.append_widget(entry, "This is just an entry", "Private")133134 # well, it isn’t created within the toolbar, so we must still show it135 entry.show()

Como verás, añadir cualquier tipo de control a una barra de herramientas es fácil. La única cosa que tienes querecordar es que este control debe mostrarse manualmente (al contrario que los items, que se mostrarán de golpecon la barra de herramientas).

137 # that’s it ! let’s show everything.138 toolbar.show()139 handlebox.show()140 dialog.show()141142 def main():143 # rest in gtk_main and wait for the fun to begin!144 gtk.main()145 return 0146147 if __name__ == "__main__":148 ToolbarExample()149 main()

La linea 140 termina la definición de la claseToolbarExample . Las lineas 142-145 definen la funciónmain ()que simplemente llama a la funcióngtk.main () para empezar el bucle de procesamiento de eventos. Las lineas147-149 crean una instancia deToolbarExample y luego entran en el bucle de procesamiento de eventos. Asitermina el tutorial de la barra de herramientas. Por supuesto, para apreciarla en su máximo esplendor necesitastambién el icono XPM,gtk.xpm [examples/gtk.xpm]. La figura Figura 10.8. Ejemplo de Barra de Herramientasmuestar la ventana resultante:

141

Page 150: Tutorial Pygtk

Capítulo 10. Controles Contenedores

Figura 10.8. Ejemplo de Barra de Herramientas

10.12. FichasEl controlNoteBook (Fichas) es una colección de "páginas" que se solapan unas con otras; cada página contieneinformación diferente y sólo una es visible al mismo tiempo. Este control se ha hecho muy popular últimamenteen la programación de interfaces gráficas de usuario, y es una buena manera de mostrar bloques de informaciónsimilar que guardan una separación en la pantalla.

La primera función que necesitas saber, como probablemente adivines, se usa para crear un nuevo control defichas.

notebook = gtk.Notebook()

Una vez que has creado las fichas, hay unos cuantos métodos que manipulan el control de fichas. Veámoslosindividualmente.

El primero que trataremos especifica cómo colocar los indicadores de página. Estos indicadores o "pestañas"como se les conoce, se pueden colocar de cuatro maneras: arriba, abajo, izquierda o derecha.

notebook.set_tab_pos( pos )

pos será uno de las siguientes, que son bastante descriptivas:

142

Page 151: Tutorial Pygtk

Capítulo 10. Controles Contenedores

POS_LEFT # izquierdaPOS_RIGHT # derechaPOS_TOP # arribaPOS_BOTTOM # abajo

POS_TOP es el valor predeterminado.

Lo siguiente que trataremos será cómo añadir páginas a las fichas. Hay tres formas de añadir páginas a unNoteBook . Veamos las dos primeras ya que son bastante similares.

notebook.append_page( child , tab_label )

notebook.prepend_page( child , tab_label )

Estos métodos añaden páginas a las fichas insertándolas al final (append), o al principio (prepend).child esel control que se colocará dentro de la página en cuestión, ytab_label es el título para la página que seestá añadiendo. El controlchild debe crearse por separado, y normalmente es un conjunto de opciones deconfiguración dentro de otro control contenedor, como una tabla.

El último método para añadir una página a las fichas contiene todas las propiedades de los otros dos, y además tepermite especificar en qué posición se insertará la página en las fichas.

notebook.insert_page( child , tab_label , position )

Los parámetros son los mismos que enappend () y prepend () excepto en que contiene un parámetro extra,position . Este parámetro se usa para especificar en qué lugar la página se insertará; la primera página está en laposición cero.

Ahora que sabemos cómo añadir una página, veamos cómo podemos borrar una página de las fichas.

notebook.remove_page( page_num)

Este método borra la página especificada porpage_num de las fichas contenidas en la variablenotebook .

Para saber cuál es la página actual de las fichas utiliza el método:

page = notebook.get_current_page()

Los próximos dos métodos son simples llamadas para mover la página hacia alante o hacia atrás. Simplementeutilizalos en el control de fichas sobre el que quieras operar.

Nota

Cuando elnotebook tiene como página actual la última página, y se llama anext_page () , no ocurrenada. De forma análoga, si elnotebook está en la primera página, y se llama aprev_page () , no pasanada.

143

Page 152: Tutorial Pygtk

Capítulo 10. Controles Contenedores

notebook.next_page()

notebook.prev_page()

Este método fija la página "activa". Si quieres que las fichas comiencen con la página 5 activa, usarás este método.Si no lo usas, el valor predeterminado de las fichas es mostrar la primera página.

notebook.set_current_page( page_num)

Los siguientes dos métodos añaden o borran las pestañas y el borde respectivamente.

notebook.set_show_tabs( show_tabs )

notebook.set_show_border( show_border )

El siguiente método es útil cuando tienes un gran número de páginas, y las pestañas no caben en la página. Tepermite desplazar las pestañas usando dos botones de flechas.

notebook.set_scrollable( scrollable )

show_tabs (mostrar pestañas),show_border (mostrar border) yscrollable (desplazable) pueden ser TRUEo FALSE.

Ahora veamos un ejemplo, está ampliado a partir del códigotestgtk.py que viene con la distribución PyGTK.El programanotebook.py [examples/notebook.py] crea una ventana con unas fichas y seis botones. Las fichascontienen 11 páginas, añadidas de tres formas diferentes, al principio, en medio o al final. Los botones te permitenrotar la posición de las pestañas, añadir o borrar las pestañas y el borde, borrar una página, cambiar las páginashacia delante y hacia atrás, y salir del programa. La figura Figura 10.9. Ejemplo de Fichas muestra la ventana delprograma:

144

Page 153: Tutorial Pygtk

Capítulo 10. Controles Contenedores

Figura 10.9. Ejemplo de Fichas

El código fuente denotebook.py[examples/notebook.py] es:

1 #!/usr/bin/env python23 # example notebook.py45 import gtk67 class NotebookExample:8 # This method rotates the position of the tabs9 def rotate_book(self, button, notebook):

10 notebook.set_tab_pos((notebook.get_tab_pos()+1) %4)1112 # Add/Remove the page tabs and the borders13 def tabsborder_book(self, button, notebook):14 tval = gtk.FALSE15 bval = gtk.FALSE16 if self.show_tabs == gtk.FALSE:17 tval = gtk.TRUE18 if self.show_border == gtk.FALSE:19 bval = gtk.TRUE2021 notebook.set_show_tabs(tval)22 self.show_tabs = tval23 notebook.set_show_border(bval)24 self.show_border = bval

145

Page 154: Tutorial Pygtk

Capítulo 10. Controles Contenedores

2526 # Remove a page from the notebook27 def remove_book(self, button, notebook):28 page = notebook.get_current_page()29 notebook.remove_page(page)30 # Need to refresh the widget --31 # This forces the widget to redraw itself.32 notebook.draw((0,0,-1,-1))3334 def delete(self, widget, event=None):35 gtk.mainquit()36 return gtk.FALSE3738 def __init__(self):39 window = gtk.Window(gtk.WINDOW_TOPLEVEL)40 window.connect("delete_event", self.delete)41 window.set_border_width(10)4243 table = gtk.Table(3,6,gtk.FALSE)44 window.add(table)4546 # Create a new notebook, place the position of the tabs47 notebook = gtk.Notebook()48 notebook.set_tab_pos(gtk.POS_TOP)49 table.attach(notebook, 0,6,0,1)50 notebook.show()51 self.show_tabs = gtk.TRUE52 self.show_border = gtk.TRUE5354 # Let’s append a bunch of pages to the notebook55 for i in range(5):56 bufferf = "Append Frame %d" % (i+1)57 bufferl = "Page %d" % (i+1)5859 frame = gtk.Frame(bufferf)60 frame.set_border_width(10)61 frame.set_size_request(100, 75)62 frame.show()6364 label = gtk.Label(bufferf)65 frame.add(label)66 label.show()6768 label = gtk.Label(bufferl)69 notebook.append_page(frame, label)7071 # Now let’s add a page to a specific spot72 checkbutton = gtk.CheckButton("Check me please!")73 checkbutton.set_size_request(100, 75)74 checkbutton.show ()7576 label = gtk.Label("Add page")77 notebook.insert_page(checkbutton, label, 2)7879 # Now finally let’s prepend pages to the notebook80 for i in range(5):81 bufferf = "Prepend Frame %d" % (i+1)82 bufferl = "PPage %d" % (i+1)83

146

Page 155: Tutorial Pygtk

Capítulo 10. Controles Contenedores

84 frame = gtk.Frame(bufferf)85 frame.set_border_width(10)86 frame.set_size_request(100, 75)87 frame.show()8889 label = gtk.Label(bufferf)90 frame.add(label)91 label.show()9293 label = gtk.Label(bufferl)94 notebook.prepend_page(frame, label)9596 # Set what page to start at (page 4)97 notebook.set_current_page(3)9899 # Create a bunch of buttons

100 button = gtk.Button("close")101 button.connect("clicked", self.delete)102 table.attach(button, 0,1,1,2)103 button.show()104105 button = gtk.Button("next page")106 button.connect("clicked", lambda w: notebook.next_page())107 table.attach(button, 1,2,1,2)108 button.show()109110 button = gtk.Button("prev page")111 button.connect("clicked", lambda w: notebook.prev_page())112 table.attach(button, 2,3,1,2)113 button.show()114115 button = gtk.Button("tab position")116 button.connect("clicked", self.rotate_book, notebook)117 table.attach(button, 3,4,1,2)118 button.show()119120 button = gtk.Button("tabs/border on/off")121 button.connect("clicked", self.tabsborder_book, notebook)122 table.attach(button, 4,5,1,2)123 button.show()124125 button = gtk.Button("remove page")126 button.connect("clicked", self.remove_book, notebook)127 table.attach(button, 5,6,1,2)128 button.show()129130 table.show()131 window.show()132133 def main():134 gtk.main()135 return 0136137 if __name__ == "__main__":138 NotebookExample()139 main()

Espero que esto te ayude en tu camino para crear fichas en tus programas PyGTK.

147

Page 156: Tutorial Pygtk

Capítulo 11. Control MenúHay dos formas de crear menús: la forma fácil y la dificil. Las dos tienen sus usos, pero normalmente puedesusar elItemfactory (Factoria de Elementos) (la forma fácil). La forma "difícil" es crear los menús usando lasllamadas directamente. La forma fácil consiste en usar las llamadas aGtkItemFactory . Esto es mucho mássimple, pero cada enfoque tiene sus ventajas y desventajas.

La Itemfactory es mucho más fácil de usar, y más fácil de añadir menús, aunque escribir unas cuantas funcionesauxiliares para crear menús usando el método manual puede ser mucho mejor en términos de usabilidad. Con laItemfactory , no es posible añadir imágenes o el carácter ’/’ a los menús.

11.1. Creación de Menús ManualSiguiendo la tradición docente pura, primero te enseñaremos la forma difícil. :)

Hay tres controles involucrados en la creación de una barra de menús y de submenús:

• un elemento de menú, que es lo que el usuario va a seleccionar, por ejemplo, "Guardar"

• un menú, que actua como contenedor para elementos de menú, y

• una barra de menú, que es un contenedor para cada menú individual.

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 seselecciona, activa el menú.

Veamos las funciones que se usan para crear menús y barras de menús. La primera función se usa para crear unanueva barra de menú:

menu_bar = gtk.MenuBar()

Esta función bastante autoexplicativa crea una nueva barra de menús. Puedes usar el método add() deGtkContainer para meter la barra de menús en una ventana, o los métodos pack deGtkBox para meterlo enuna caja - igual que los botones.

menu = gtk.Menu()

Esta función devuelve una referencia a un nuevo menú; nunca se mostrará (con el métodoshow() ), es sólo uncontenedor para los elementos de menús. Espero que esto se aclare un poco cuando veas el ejemplo más abajo.

La siguiente función se usa para crear elementos de menús que se colocan en el menú (y la barra de menús):

menu_item = gtk.MenuItem( label =None)

El parámetrolabel , si existe, se analizará buscando caracteres mnemónicos. Esta llamada se usa para crearelementos de menú que se van a visualizar. Acuerdate de diferenciar entre un "menú" como el que creas congtk.Menu () y un "elemento de menú" como el que creas congtk.MenuItem () . El elemento de menú será en

148

Page 157: Tutorial Pygtk

Capítulo 11. Control Menú

realidad un botón con una acción asociada, mientras que un menú será un contenedor que contiene elementos demenú.

Una vez que has creado un elemento de menú tienes que meterlo en un menú. Esto se consigue con el métodoappend () . Para poder saber cuándo el usuario selecciona un elemento, necesitamos conectarlo a la señal "activate"de la forma habitual. Por tanto, si queremos crear un menú estándard Archivo , con las opciones Abrir, Guardar,y Salir, el código sería algo asi como:

file_menu = gtk.Menu() # Don’t need to show menus

# Create the menu itemsopen_item = gtk.MenuItem("Abrir")save_item = gtk.MenuItem("Guardar")quit_item = gtk.MenuItem("Salir")

# Add them to the menufile_menu.append(open_item)file_menu.append(save_item)file_menu.append(quit_item)

# Conecta las funciones de retrollamada a la señal "activate"open_item.connect_object("activate", menuitem_response, "file.open")save_item.connect_object("activate", menuitem_response, "file.save")

# conectamos el elemento quit a nuestra función de salidaquit_item.connect_object ("activate", destroy, "file.quit")

# necesitamos mostrar los elementos de menúopen_item.show()save_item.show()quit_item.show()

En este punto tenemos nuestro menú. Ahora necesitamos crear una barra de menú y un elemento de menú para laentrada Archivo , que añadiremos a nuestro menú. El código 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ú confile_item . Esto se hace con el método:

menu_item.set_submenu( submenu)

Por tanto, nuestro ejemplo continúa asi:

menu_item.set_submenu(file_menu)

Lo único que nos queda es añadir el menú a la barra de menús, lo cual se consigue con el método:

149

Page 158: Tutorial Pygtk

Capítulo 11. Control Menú

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 menús, como los menús de ayuda, podemos usar elsiguiente método (de nuevo enfile_item en nuestro ejemplo) antes de añadirlo a la barra de menús.

menu_item.set_right_justified(right_justified)

Aqui tienes un resumen de los pasos necesarios para crear una barra de menús con menús en ella:

• Crea un nuevo menú congtk.Menu ()

• Usa varias llamadas agtk.MenuItem () una para cada elemento que quieras tener en tu menú. Y usa el métodoappend () para poner cada uno de estos elementos en el menú.

• Crea un elemento de menú usandogtk.MenuItem (). Este será la raiz del menú, el texto que aparezca aquiestará en la propia barra de menús.

• Usa el métodoset_submenu () para añadir el menú al elemento raiz del menú (el que creamos en el pasoanterior).

• Crea una nueva barra de menús usandogtk.MenuBar (). Este paso sólo hay que hacerlo una vez cuandoestemos creando una serie de menús en una barra de menús.

• Usa el método append() para poner el menú raiz en la barra de menús.

Crear un menú emergente es casi lo mismo. La diferencia es que el menú no se visualiza "automáticamente" poruna barra de menús, sino explícitamente llamando al métodopopup () desde un evento de pulsación de botón, porejemplo. Sigue estos pasos:

• Crea una retrollamada que maneje el evento. Necesita tener el siguiente formato:

def handler(widget, event):

• y usará el evento para saber donde mostrar el menú.

• En el manejador del evento, si el evento es una pulsación de botón, trata el evento como un evento de botón(realmente lo es) y usalo como en el código de ejemplo, para pasar información al métodopopup () .

• Enlaza el manejador del evento al control con:

widget.connect_object("event", handler, menu)

150

Page 159: Tutorial Pygtk

Capítulo 11. Control Menú

• donde widget es el control que estas conectando, handler es la función manejadora, y menu es un menú creadocon GtkMenu (). Esto puede ser un menú que también está en una barra de menús, como se muestra en elcódigo de ejemplo.

11.2. Ejemplo de Menú ManualEsto debería hacerlo. Veamos el programa de ejemplomenu.py [examples/menu.py] para ayudarnos a clarificarlos conceptos. La figura Figura 11.1. Ejemplo de Menú muestra la ventana del programa:

Figura 11.1. Ejemplo de Menú

El código fuente demenu.py [examples/menu.py] es:

1 #!/usr/bin/env python23 # example menu.py45 import gtk67 class MenuExample:8 def __init__(self):9 # create a new window

10 window = gtk.Window(gtk.WINDOW_TOPLEVEL)11 window.set_size_request(200, 100)12 window.set_title("GTK Menu Test")13 window.connect("delete_event", gtk.mainquit)1415 # Init the menu-widget, and remember -- never16 # show() the menu widget!!17 # This is the menu that holds the menu items, the one that18 # will pop up when you click on the "Root Menu" in the app19 menu = gtk.Menu()2021 # Next we make a little loop that makes three menu-entries for22 # "test-menu". Notice the call to gtk_menu_append. Here we are23 # adding a list of menu items to our menu. Normally, we’d also24 # catch the "clicked" signal on each of the menu items and setup a25 # callback for it, but it’s omitted here to save space.26 for i in range(3):27 # Copy the names to the buf.28 buf = "Test-undermenu - %d" % i2930 # Create a new menu-item with a name...

151

Page 160: Tutorial Pygtk

Capítulo 11. Control Menú

31 menu_items = gtk.MenuItem(buf)3233 # ...and add it to the menu.34 menu.append(menu_items)3536 # Do something interesting when the menuitem is selected37 menu_items.connect("activate", self.menuitem_response, buf)3839 # Show the widget40 menu_items.show()4142 # This is the root menu, and will be the label43 # displayed on the menu bar. There won’t be a signal handler attached,44 # as it only pops up the rest of the menu when pressed.45 root_menu = gtk.MenuItem("Root Menu")4647 root_menu.show()4849 # Now we specify that we want our newly created "menu" to be the50 # menu for the "root menu"51 root_menu.set_submenu(menu)5253 # A vbox to put a menu and a button in:54 vbox = gtk.VBox(gtk.FALSE, 0)55 window.add(vbox)56 vbox.show()5758 # Create a menu-bar to hold the menus and add it to our main window59 menu_bar = gtk.MenuBar()60 vbox.pack_start(menu_bar, gtk.FALSE, gtk.FALSE, 2)61 menu_bar.show()6263 # Create a button to which to attach menu as a popup64 button = gtk.Button("press me")65 button.connect_object("event", self.button_press, menu)66 vbox.pack_end(button, gtk.TRUE, gtk.TRUE, 2)67 button.show()6869 # And finally we append the menu-item to the menu-bar -- this is the70 # "root" menu-item I have been raving about =)71 menu_bar.append (root_menu)7273 # always display the window as the last step so it all splashes on74 # the screen at once.75 window.show()7677 # Respond to a button-press by posting a menu passed in as widget.78 #79 # Note that the "widget" argument is the menu being posted, NOT80 # the button that was pressed.81 def button_press(self, widget, event):82 if event.type == gtk.gdk.BUTTON_PRESS:83 widget.popup(None, None, None, event.button, event.time)84 # Tell calling code that we have handled this event the buck85 # stops here.86 return gtk.TRUE87 # Tell calling code that we have not handled this event pass it on.88 return gtk.FALSE89

152

Page 161: Tutorial Pygtk

Capítulo 11. Control Menú

90 # Print a string when a menu item is selected91 def menuitem_response(self, widget, string):92 print "%s" % string9394 def main():95 gtk.main()96 return 09798 if __name__ == "__main__":99 MenuExample()

100 main()

También puedes hacer un elemento de menú insensitivo y, usando una tabla de atajos, conectar teclas aretrollamadas de menús.

11.3. Usando la Factoria de ElementosAhora que te hemos enseñado la forma difícil, así es como lo harías usando las llamadas a gtk.ItemFactory.

11.4. Ejemplo de Factoria de ElementosEl programa de ejemploitemfactory.py [examples/itemfactory.py] usa lagtk.ItemFactory . La figura Figura11.2. Ejemplo de Factoria de Elementos muestra la ventana del programa:

Figura 11.2. Ejemplo de Factoria de Elementos

153

Page 162: Tutorial Pygtk

Capítulo 11. Control Menú

El código fuente deitemfactory.py [examples/itemfactory.py] es:

1 #!/usr/bin/env python23 # example itemfactory.py45 import gtk67 class ItemFactoryExample:8 # Obligatory basic callback9 def print_hello(self, w, data):

10 print "Hello, World!"1112# Esta es la estructura de la Factoria de Elementos usada para generar nuevos menús

13 # Item 1: La ruta del menú. La letra después del subrayado indica14 # una tecla de atajo cuando el menú se abra15 # Item 2: La tecla de atajo para el elemento16 # Item 3: La retrollamada17 # Item 4: La acción de retrollamada. Esto cambia los parámetros con18 # los que la retrollamada se llama. El valor predeterminado es 019 # Item 5: El tipo de elemento, usado para definir el tipo al que pertenece el ←↩

elemento20 # Aqui están los posibles valores2122 # NULL -> "<Item>"23 # "" -> "<Item>"24 # "<Title>" -> título25 # "<Item>" -> simple26 # "<CheckItem>" -> activación27 # "<ToggleItem>" -> biestado28 # "<RadioItem>" -> exclusión mútua29 # <path> -> ruta de un elemento de exclusión mútua30 # "<Separator>" -> separador31 # "<Branch>" -> contenedor de nuevos elementos32 # "<LastBranch>" -> rama justificada a la derecha3334 def get_main_menu(self, window):35 accel_group = gtk.AccelGroup()3637 # This function initializes the item factory.38 # Param 1: The type of menu - can be MenuBar, Menu,39 # or OptionMenu.40 # Param 2: The path of the menu.41 # Param 3: A reference to an AccelGroup. The item factory sets up42 # the accelerator table while generating menus.43 item_factory = gtk.ItemFactory(gtk.MenuBar, "<main>", accel_group)4445 # This method generates the menu items. Pass to the item factory46 # the list of menu items47 item_factory.create_items(self.menu_items)4849 # Attach the new accelerator group to the window.50 window.add_accel_group(accel_group)5152 # need to keep a reference to item_factory to prevent its destruction53 self.item_factory = item_factory54 # Finally, return the actual menu bar created by the item factory.

154

Page 163: Tutorial Pygtk

Capítulo 11. Control Menú

55 return item_factory.get_widget("<main>")5657 def __init__(self):58 self.menu_items = (59 ( "/_File", None, None, 0, "<Branch>" ),60 ( "/File/_New", "<control>N", self.print_hello, 0, None ),61 ( "/File/_Open", "<control>O", self.print_hello, 0, None ),62 ( "/File/_Save", "<control>S", self.print_hello, 0, None ),63 ( "/File/Save _As", None, None, 0, None ),64 ( "/File/sep1", None, None, 0, "<Separator>" ),65 ( "/File/Quit", "<control>Q", gtk.mainquit, 0, None ),66 ( "/_Options", None, None, 0, "<Branch>" ),67 ( "/Options/Test", None, None, 0, None ),68 ( "/_Help", None, None, 0, "<LastBranch>" ),69 ( "/_Help/About", None, None, 0, None ),70 )71 window = gtk.Window(gtk.WINDOW_TOPLEVEL)72 window.connect("destroy", gtk.mainquit, "WM destroy")73 window.set_title("Item Factory")74 window.set_size_request(300, 200)7576 main_vbox = gtk.VBox(gtk.FALSE, 1)77 main_vbox.set_border_width(1)78 window.add(main_vbox)79 main_vbox.show()8081 menubar = self.get_main_menu(window)8283 main_vbox.pack_start(menubar, gtk.FALSE, gtk.TRUE, 0)84 menubar.show()85 window.show()8687 def main():88 gtk.main()89 return 09091 if __name__ == "__main__":92 ItemFactoryExample()93 main()

Por ahora esto es sólo un ejemplo. Una explicación y muchos comentarios vendrán después.

155

Page 164: Tutorial Pygtk

Capítulo 12. Área de DibujoEl control DrawingArea (Área de Dibujo) proporciona un lienzo simple en el que se puede dibujar usando losmétodos degtk.DrawingArea . Un DrawingArea se crea con la función:

drawing_area = gtk.DrawingArea()

Un DrawingArea (Área de Dibujo) se crea con un tamaño de (0,0) por lo que deberías usar el siguiente métodopara hacer eldrawing_area visible:

drawing_area.set_size_request( ancho , alto )

12.1. Contexto GráficoHay unos cuantos métodos para dibujar en un área de dibujo y todos requieren un contexto gráfico (gtk.gdk.GC ), que encapsula la información necesaria para dibujar. Los atributos de un contexto gráfico son:

background # fondocap_style # estilo de fin de lineaclip_mask # máscara de recorteclip_x_origin # orígen x del rectángulo de recorteclip_y_origin # orígen y del rectángulo de recortefill # rellenofont # fuenteforeground # color de frentefunction # funcióngraphics_exposures # exposiciones gráficasjoin_style # estilo de uniónline_style # estilo de linealine_width # ancho de lineastipple # patrón de rellenosub_window # subventanatile # títulots_x_origin # orígen xts_y_origin # orígen y

background (fondo) especifica elgtk.gdk.Color que se usa para dibujar el fondo.

foreground (frente) especifica elgtk.gdk.Color que se usa para dibujar el color de primer plano.

Para crear ungtk.gdk.Color se usa el métodoalloc_color () de la clasegtk.gdk.Colormap (Mapa deColor). El mapa de color asociado a un control se puede obtener con el método:

colormap = widget.get_colormap()

Se puede especificar ungtk.gdk.Color cómo una cadena de texto (por ejemplo, "red" (rojo), "orange" (naranja),"navajo white" (blanco navajo) definida en el ficherorgb.txt de X Window), o cómo un triple de rojo, verde yazúl de enteros en el rango de 0 a 65535. Además, puedes especificar si el color es modificable (se puede cambiar

156

Page 165: Tutorial Pygtk

Capítulo 12. Área de Dibujo

después pero no puede compartirse) y si se debe hacer una búsqueda y quedarse con el color más parecido de entrelos existentes si el color exacto no está disponible. El métodoalloc_color () se define así:

color = colormap.alloc_color( red , green , blue , writeable =FALSE, best_match =TRUE)

color = colormap.alloc_color( spec , writeable =FALSE, best_match =TRUE)

Por ejemplo:

navajowhite = colormap.alloc(’navajo white’)

cyan = colormap.alloc(0, 65535, 65535)

cap_style especifica el estilo de fin de linea que se usa cuando se dibuja el final de una linea que no esta unidaa otras. Los estilos disponibles son:CAP_NOT_LAST igual que CAP_BUTT para lineas con un ancho distinto de cero. Para lineas de

ancho igual a cero, el punto final de la linea no se dibujará.CAP_BUTT los finales de las lineas se dibujan como cuadrados y se extienden hasta las

coordenadas del punto final.CAP_ROUND los finales de las lineas se dibujan como semicírculos con el diámetro igual al

ancho de la linea y centrados en el punto final.CAP_PROJECTING los finales de las lineas se dibujan como cuadrados y se extienden la mitad del

ancho más allá del punto final.

clip_mask especifica ungtk.gdk.Pixmap que se usa para recortar el dibujo.

clip_x_origin y clip_y_origin especifican los valores x e y del orígen del rectángulo de recorte relativos ala esquina superior izquierda deldrawing_area (área de dibujo).

fill especifica el estilo de relleno que se usará al dibujar. Los estilos de relleno disponibles son:SOLID dibuja con el color de primer plano.TILED dibuja con un pixmap cuadriculado.STIPPLED dibuja con un mapa de bits de patrón. Los píxeles correspondientes a los bits del

mapa de bits que están a uno se dibujarán con el color de primer plano; los píxelescorrespondientes a los bits que no están a uno se dejarán sin modificar.

OPAQUE_STIPPLED dibuja usando el mapa de bits de patrón. Los píxeles correspondientes a los bitsen el mapa de bits de patrón que están a uno se dibujarán con el color de primerplano; los píxeles correspondientes a los bits que no están a uno se dibujarán conel color de fondo.

font es lagtk.gdk.Font (Fuente) que se usa cómo la fuente predeterminada para dibujar texto.

Nota

El uso del atributofont está obsoleto.

function especifica cómo se combinan los valores de los bits en los píxeles de orígen con los valores de losbits en los píxeles de destino para producir el resultado final. Los 16 valores diferentes se corresponden con lasposibles tablas de verdad 2x2. Sólo un par de estos valores es útil normalmente; para imágenes a color, sóloCOPY, XOR e INVERT generalmente son útiles. Para mapas de bits, AND y OR también suelen ser útiles. Losvalores de función son:

157

Page 166: Tutorial Pygtk

Capítulo 12. Área de Dibujo

COPY # CopiarINVERT # InvertirXOR # XORCLEAR # LimpiarAND # YAND_REVERSE # Y al revésAND_INVERT # Y invertidaNOOP # NadaOR # OEQUIV # equivalenciaOR_REVERSE # O al revésCOPY_INVERT # Copiar invertidoOR_INVERT # O invertidoNAND # NANDSET # Fijar

graphics_exposures especifica si las exposiciones gráficas están activadas (TRUE) o desactivadas (FALSE).Cuandographics_exposures es TRUE, un fallo al copiar un píxel en una operación de dibujo producirá unevento expose; si la copia tiene éxito, se producirá un evento noexpose.

join_style especifica el estilo de unión que se usa cuando las lineas se juntan en un ángulo. Los estilosdisponibles son:JOIN_MITER los lados de cada linea se extienden para unirse en un ángulo.JOIN_ROUND los lados de las dos lineas se unen en un arco circular.JOIN_BEVEL los lados de las dos lineas se unen con una linea recta que tiene el mismo ángulo

con cada linea.

line_style especifica el estilo con el que se dibuja una linea. Los estilos disponibles son:LINE_SOLID las lineas se dibujan solidas.LINE_ON_OFF_DASH se dibujan los segmentos impares; los segmentos pares no se dibujan.LINE_DOUBLE_DASH los segmentos impares son normales. Los segmentos pares se dibujan con el color

de fondo si el estilo de relleno es SOLID, o con el color de fondo aplicado a lamáscara del patrón si el estilo de relleno es STIPPLED.

line_width especifica el ancho con el que se dibujan las lineas.

stipple especifica elgtk.gdk.Pixmap que se usará para dibujo con patrón cuando el relleno está puesto aSTIPPLED o a OPAQUE_STIPPLED.

sub_window especifica el modo de dibujo de unagtk.gdk.Window que tiene hijasgtk.gdk.Window . Losvalores posibles son:CLIP_BY_CHILDREN sólo se dibuja en la propia ventana pero no en sus ventanas hijas.INCLUDE_INFERIORS dibuja en la ventana y en sus ventanas hijas.

tile especifica elgtk.gdk.Pixmap que se usará para dibujo cuadriculado cuando el relleno esta puesto aTILED.

ts_x_origin y ts_y_origin especifican la posición iniciales de los mapas de bits de patrón y de dibujocuadriculado).

Se puede crear un nuevo Contexto Gráfico con una llamada al métodogtk.gdk.Window new_gc () :

gc = widget.window.new_gc( foreground =None, background =None, font =None,

158

Page 167: Tutorial Pygtk

Capítulo 12. Área de Dibujo

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 =-1join_style =-1)

Para crear un nuevo Contexto Gráfico con este método, el control debe ser del tipo de los que tienen unagtk.gdk.Window y el control debe estar realizado (es decir, lagtk.gdk.Window ha sido creada).

Los atributos del Contexto Gráfico pueden dejarse sin tocar o especificarse en el métodonew_gc () usandoargumentos de palabras clave de Python.

Los atributos individuales de ungtk.gdk.GC se pueden establecer asignándole un valor al atributo. Por ejemplo:

gc.cap_style = CAP_BUTTgc.line_width = 10gc.fill = SOLDgc.foreground = micolor

o usando los siguientes métodos:

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 patrón intermitente que se usa cuando el estilo de linea es LINE_ON_OFF_DASH o LINE_DOUBLE_DASHse puede fijar usando el siguiente método:

gc.set_dashes(offset, dash_list)

dondeoffset (desplazamiento) es el índice del valor inicial endash_list y dash_list (lista de intermitencia)es una lista o tupla que contiene los números de los píxeles a dibujar o saltar para formar los segmentos. Lossegmentos se dibujan empezando con el número de píxeles en la posición de desplazamiento; entonces, el siguientenúmero de píxeles no se dibuja; y luego el siguiente número es dibujado; y así, recorriendo todos los números dela lista de intermitencia y empezando otra vez cuando se llega al final. Por ejemplo, si la lista de intermitencia es(2, 4, 8, 16) y el desplazamiento es 1, los segmentos se dibujarán así: dibuja 4 píxeles, sáltate 8 píxeles, dibuja 16píxeles, sáltate 2 píxeles, dibuja 4 píxeles y así sucesivamente.

Se puede copiar ungtk.gdk.GC existente usando el método:

159

Page 168: Tutorial Pygtk

Capítulo 12. Área de Dibujo

gc.copy( src_gc )

Los atributos degc serán los mismos que los desrc_gc .

12.2. Métodos de DibujoHay un conjunto general de métodos que se pueden usar para dibujar en el área de dibujo. Estos métodos dedibujo funcionan en cualquiergtk.gdk.Drawable (Dibujable), que es unagtk.gdk.Window (Ventana) o ungtk.gdk.Pixmap (Mapa de Píxeles). Los métodos de dibujo son:

drawable.draw_point( gc , x , y) # dibuja_punto

gc es el Contexto Gráfico a 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 Gráfico.

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 rectángulo

gc es el Contexto Gráfico.

filled es un valor booleano que indica si el rectángulo debe ser rellenado con el color de primer plano o no.

x e y son las coordenadas de la esquina superior izquierda del rectángulo.

width y height son el ancho y el alto del rectángulo.

drawable.draw_arc( gc , filled , x , y , width , height , angle1 , angle2 ) # dibuja arco

gc es el Contexto Gráfico.

filled es un valor booleano que indica si el arco debe ser rellenado con el color de primer plano o no.

x e y son las coordenadas de la esquina superior izquierda del rectángulo que bordea al arco.width y height

son el ancho y el alto del rectángulo que bordea al arco.

angle1 es el ángulo inicial del arco, relativo a la posición de las 3 en punto del reloj, en el sentido contrario delas agujas del reloj, en seseinta y cuatroavos de grado.

angle2 es el ángulo final del arco, relativo aangle1 , en seseinta y cuatroavos de grado en el sentido de las agujasdel reloj.

160

Page 169: Tutorial Pygtk

Capítulo 12. Área de Dibujo

drawable.draw_polygon( gc , filled , points ) # dibuja polígono

gc es el Contexto Gráfico.

filled es un valor booleano que especifica si el polígono debe ser rellenado con el color de primer plano o no.

points es una lista de pares de coordenadas en tuplas, por ejemplo [ (0,0), (2,5), (3,7), (4,11) ], de los puntos quese van a dibujar como un polígono conectado.

drawable.draw_string( font , gc , x , y , string ) # dibuja cadena

drawable.draw_text( font , gc , x , y , string ) # dibuja texto

font es lagtk.gdk.Font (fuente) que se usará para pintar la cadena.

gc es el Contexto Gráfico.

x e y son las coordenadas del punto donde se empezará a dibujar la cadena, es decir, la línea de base izquierda.

string es la cadena de caracteres a dibujar.

Nota

Ambos métodosdraw_string () y draw_text () están obsoletos - en su lugar usa unpango.Layout

con el métododraw_layout () .

drawable.draw_layout( gc , x , y , layout ) # dibuja disposición

gc es el Contexto Gráfico.

x e y son las coordenadas del punto desde el que se empieza a dibujar la disposición.

layout es elpango.Layout que se va a dibujar.

drawable.draw_drawable( gc , src , xsrc , ysrc , xdest , ydest , width , height ) # dibuja ←↩dibujable

gc es el Contexto Gráfico.

src es el dibujable de orígen.

xsrc e ysrc son las coordenadas de la esquina superior izquierda del rectángulo en el dibujable de orígen.

xdest e ydest son las coordenadas de la esquina superior izquierda en el área de dibujo.

width y height son el ancho y el alto del área del dibujable de orígen que será copiada en eldrawable . Siwidth o height es -1 entonces el ancho o el alto completo del dibujable se usará.

drawable.draw_image( gc , image , xsrc , ysrc , xdest , ydest , width , height ) # dibuja imágen

161

Page 170: Tutorial Pygtk

Capítulo 12. Área de Dibujo

gc es el Contexto Gráfico.

image es la imágen orígen.

xsrc e ysrc son las coordenadas de la esquina superior izquierda del rectángulo en el dibujable orígen.

xdest e ydest son las coordenadas de la esquina superior izquierda del rectángulo en el área de dibujo.

width y height son el ancho y el alto del área del dibujable orígen que se copiará en el dibujable destino. Siwidth o height es -1 entonces se usará el ancho o el alto completo de la imágen.

drawable.draw_points( gc , points ) # dibuja puntos

gc es el Contexto Gráfico.

points es una lista o tupla de pares de coordenadas en tuplas, por ejemplo [ (0,0), (2,5), (3,7), (4,11) ], de lospuntos a dibujar.

drawable.draw_segments( gc , segs ) # dibuja segmentos

gc es el Contexto Gráfico.

segs es una lista o tupla de pares de coordenadas iniciales y finales en tuplas, por ejemplo [ (0,0, 1,5), (2,5, 1,7),(3,7, 1,11), (4,11, 1,13) ], de los segmentos de lineas a dibujar.

drawable.draw_lines( gc , points ) # dibuja lineas

gc es el Contexto Gráfico.

points es una lista o tupla de pares de coordenadas en tuplas, por ejemplo [ (0,0), (2,5), (3,7), (4,11) ], de lospuntos a conectar con lineas.

drawable.draw_rgb_image( gc , x , y , width , height , dith , rgb_buf , rowstride ) # dibuja ←↩imagen rgb

drawable.draw_rgb_32_image( gc , x , y , width , height , dith , buf , rowstride ) # dibuja ←↩imagen rgb 32

drawable.draw_gray_image( gc , x , y , width , height , dith , buf , rowstride ) # dibuja ←↩imagen en escala de grises

gc es el Contexto Gráfico.

x e y son las coordenadas de la esquina superior izquierda del rectángulo que bordea la imágen.

width y height son el ancho y el alto del rectángulo que bordea la imágen.

dith es el método de mezclado y se explica a continuación:

Para el métododraw_rgb_image () , rgb_buf es el conjunto de los datos de la imágen RGB codificados en unacadena como una secuencia de triples de píxeles RGB de 8 bits. Para el métododraw_rgb_32_image () , buf es

162

Page 171: Tutorial Pygtk

Capítulo 12. Área de Dibujo

el conjunto de los datos de la imágen RGB codificados en una cadena como una secuencia de triples de píxelesRGB de 8 bits con relleno de 8 bits (4 caracteres por cada píxel RGB). Para el métododraw_gray_image () , buf

es el conjunto de datos de la imágen codificados en una cadena como píxeles de 8 bits.

rowstride es el número de caracteres desde el principio de una fila hasta el principio de la siguiente fila en laimágen. rowstride normalmente es 3 * ancho en el métododraw_rgb_image () ; 4 * ancho para el métododraw_rgb_32_image (); y el ancho para el métododraw_gray_image () . Si rowstride es 0 entonces la linease repetirá un número de veces igual al alto.

Los tipos de mezclado son:

RGB_DITHER_NONE # Nunca usar mezclado

RGB_DITHER_NORMAL # Usar mezclado cuando se usen 8 bits por píxel (o menos)sólamente.

RGB_DITHER_MAX # Usar mezclado cuando se usen 16 bits por píxel o menos.

El programa de ejemplodrawingarea.py [examples/drawingarea.py] muestra el uso de la mayoría de los métodosdeDrawingArea . La figura Figura 12.1. Ejemplo de Área de Dibujo muestra la ventana resultante:

Figura 12.1. Ejemplo de Área de Dibujo

163

Page 172: Tutorial Pygtk

Capítulo 12. Área de Dibujo

El código fuentedrawingarea.py [examples/drawingarea.py] está abajo y usa el pixmapgtk.xpm [exam-ples/gtk.xpm] :

1 #!/usr/bin/env python23 # example drawingarea.py45 import gtk6 import operator7 import time8 import string9

10 class DrawingAreaExample:11 def __init__(self):12 window = gtk.Window(gtk.WINDOW_TOPLEVEL)13 window.set_title("Drawing Area Example")14 window.connect("destroy", gtk.mainquit)15 self.area = gtk.DrawingArea()16 self.area.set_size_request(400, 300)17 self.pangolayout = self.area.create_pango_layout("")18 window.add(self.area)19 self.area.connect("expose-event", self.area_expose_cb)20 self.area.show()21 window.show()2223 def area_expose_cb(self, area, event):24 self.style = self.area.get_style()25 self.gc = self.style.fg_gc[gtk.STATE_NORMAL]26 self.draw_point(10,10)27 self.draw_points(110, 10)28 self.draw_line(210, 10)29 self.draw_lines(310, 10)30 self.draw_segments(10, 100)31 self.draw_rectangles(110, 100)32 self.draw_arcs(210, 100)33 self.draw_pixmap(310, 100)34 self.draw_polygon(10, 200)35 self.draw_rgb_image(110, 200)36 return gtk.TRUE3738 def draw_point(self, x, y):39 self.area.window.draw_point(self.gc, x+300, y+300)40 self.pangolayout.set_text("Point", -1)41 self.area.window.draw_layout(self.gc, x+5, y+50, self.pangolayout)42 return4344 def draw_points(self, x, y):45 points = [(x+10,y+10), (x+10,y), (x+40,y+30),46 (x+30,y+10), (x+50,y+10)]47 self.area.window.draw_points(self.gc, points)48 self.pangolayout.set_text("Points", -1)49 self.area.window.draw_layout(self.gc, x+5, y+50, self.pangolayout)50 return5152 def draw_line(self, x, y):53 self.area.window.draw_line(self.gc, x+10, y+10, x+20, y+30)54 self.pangolayout.set_text("Line", -1)55 self.area.window.draw_layout(self.gc, x+5, y+50, self.pangolayout)

164

Page 173: Tutorial Pygtk

Capítulo 12. Área de Dibujo

56 return5758 def draw_lines(self, x, y):59 points = [(x+10,y+10), (x+10,y), (x+40,y+30),60 (x+30,y+10), (x+50,y+10)]61 self.area.window.draw_lines(self.gc, points)62 self.pangolayout.set_text("Lines", -1)63 self.area.window.draw_layout(self.gc, x+5, y+50, self.pangolayout)64 return6566 def draw_segments(self, x, y):67 segments = ((x+20,y+10, x+20,y+70), (x+60,y+10, x+60,y+70),68 (x+10,y+30 , x+70,y+30), (x+10, y+50 , x+70, y+50))69 self.area.window.draw_segments(self.gc, segments)70 self.pangolayout.set_text("Segments", -1)71 self.area.window.draw_layout(self.gc, x+5, y+80, self.pangolayout)72 return7374 def draw_rectangles(self, x, y):75 self.area.window.draw_rectangle(self.gc, gtk.FALSE, x, y, 80, 70)76 self.area.window.draw_rectangle(self.gc, gtk.TRUE, x+10, y+10, 20, 20)77 self.area.window.draw_rectangle(self.gc, gtk.TRUE, x+50, y+10, 20, 20)78 self.area.window.draw_rectangle(self.gc, gtk.TRUE, x+20, y+50, 40, 10)79 self.pangolayout.set_text("Rectangles", -1)80 self.area.window.draw_layout(self.gc, x+5, y+80, self.pangolayout)81 return8283 def draw_arcs(self, x, y):84 self.area.window.draw_arc(self.gc, gtk.FALSE, x+10, y, 70, 70,85 0, 360*64)86 self.area.window.draw_arc(self.gc, gtk.TRUE, x+30, y+20, 10, 10,87 0, 360*64)88 self.area.window.draw_arc(self.gc, gtk.TRUE, x+50, y+20, 10, 10,89 0, 360*64)90 self.area.window.draw_arc(self.gc, gtk.TRUE, x+30, y+10, 30, 50,91 210*64, 120*64)92 self.pangolayout.set_text("Arcs", -1)93 self.area.window.draw_layout(self.gc, x+5, y+80, self.pangolayout)94 return9596 def draw_pixmap(self, x, y):97 pixmap, mask = gtk.gdk.pixmap_create_from_xpm(98 self.area.window, self.style.bg[gtk.STATE_NORMAL], "gtk.xpm")99

100 self.area.window.draw_drawable(self.gc, pixmap, 0, 0, x+15, y+25,101 -1, -1)102 self.pangolayout.set_text("Pixmap", -1)103 self.area.window.draw_layout(self.gc, x+5, y+80, self.pangolayout)104 return105106 def draw_polygon(self, x, y):107 points = [(x+10,y+60), (x+10,y+20), (x+40,y+70),108 (x+30,y+30), (x+50,y+40)]109 self.area.window.draw_polygon(self.gc, gtk.TRUE, points)110 self.pangolayout.set_text("Polygon", -1)111 self.area.window.draw_layout(self.gc, x+5, y+80, self.pangolayout)112 return113114 def draw_rgb_image(self, x, y):

165

Page 174: Tutorial Pygtk

Capítulo 12. Área de Dibujo

115 b = 80*3*80*[’\0’]116 for i in range(80):117 for j in range(80):118 b[3*80*i+3*j] = chr(255-3*i)119 b[3*80*i+3*j+1] = chr(255-3*abs(i-j))120 b[3*80*i+3*j+2] = chr(255-3*j)121 buff = string.join(b, ”)122 self.area.window.draw_rgb_image(self.gc, x, y, 80, 80,123 gtk.gdk.RGB_DITHER_NONE, buff, 80*3)124 self.pangolayout.set_text("RGB Image", -1)125 self.area.window.draw_layout(self.gc, x+5, y+80, self.pangolayout)126 return127128 def main():129 gtk.main()130 return 0131132 if __name__ == "__main__":133 DrawingAreaExample()134 main()

166

Page 175: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto13.1. Perspectiva general de la Vista de TextoEl control TextView y sus objetos asociados (TextBuffer s, TextMark s, TextIter s, TextTag s yTextTagTable s) proporcionan un potente marco para edición multilinea de textos.

Un TextBuffer (Buffer de Texto) contiene el texto que se visualizará en uno o más controlesTextView (Vistade Texto)

En GTK+ 2.0 el texto se codifica en UTF-8 lo cual significa que un caracter puede codificarse con varios bytes.Dentro de unTextBuffer es necesario diferenciar entre índices de carácteres (llamados desplazamientos) eíndices de bytes (llamados índices).

Los TextIters (Iteradores de Texto) proporcionan una representación volátil de la posición entre dos carácteresen unTextBuffer . LosTextIters son válidos hasta que el número de carácteres en elTextBuffer cambia;Por ejemplo, siempre que se inserten o se borren carácteres en elTextBuffer todos losTextIters se invalidan.Los TextIters son la principal forma de especificar localizaciones en unTextBuffer para manipular texto.

Los TextMarks (Marcas de Texto) se proporcionan para permitir almacenar posiciones en unTextBuffer quese mantienen entre modificaciones del buffer. Una marca es cómo unTextIter (representa una posición entredos carácteres en unTextBuffer ) pero si el texto alrededor de la marca se borra, la marca permanece donde eltexto borrado estuvo. De la misma forma, si se inseta texto en la marca, la marca acaba bien a la izquierda o ala derecha del texto insertado, dependiendo de la gravedad de la marca - gravedad a la derecha deja la marca a laderecha del texto insertado mientras que gravedad a la izquierda deja la marca a la izquierda. LasTextMark s sepueden asociar a un nombre o dejarlas anónimas si no se les da un nombre. CadaTextBuffer tiene dos marcaspredefinidas llamadasinsert (insertar) yselection_bound (límite de selección). Estas marcas se refieren alpunto de inserción y al límite de la selección (la selección está entre las marcasinsert y selection_bound ).

Las TextTag s (Etiquetas de Texto) son objetos que especifican un conjunto de atributos que se pueden aplicara un rango de texto en unTextBuffer . CadaTextBuffer tiene unaTextTagTable (Tabla de Etiquetas deTexto) que contiene las etiquetas disponibles en ese buffer. LasTextTagTable s se pueden compartir entreTextBuffer s para ofrecer consistencia. LosTextTag s normalmente se usan para cambiar la apariencia de unrango de texto pero también pueden usarse para evitar que un rango de texto sea editado.

13.2. Vistas de TextoSólo hay una función para crear un controlTextView (Vista de Texto).

textview = gtk.TextView( buffer =None)

Cuando se crea unaTextView también se creará unTextBuffer (Buffer de Texto) asociado y unaTextTagTable (Tabla de Etiquetas de Texto) de forma predeterminada. Si quieres usar unTextBuffer

ya existente puedes especificarlo en el método anterior. Para cambiar elTextBuffer que usa unaTextView usael siguiente método:

textview.set_buffer( buffer )

Usa el siguiente método para obtener una referencia alTextBuffer a partir de unaTextView :

167

Page 176: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

buffer = textview.get_buffer()

Un control deTextView no tiene barras de desplazamiento para ajustar la vista en caso de que el texto seamás grande que la ventana. Para incluir barras de desplazamiento, tienes que añadir laTextView a unaScrolledWindow (Ventana de Desplazamiento).

UnaTextView se puede usar para permitir al usuario editar un cuerpo de texto, o para visualizar varias lineas deun texto de sólo lectura para el usuario. Para cambiar entre estos modos de operación, utiliza el siguiente método:

textview.set_editable( valor )

El argumentovalor puede ser TRUE o FALSE y especifica si el usuario puede editar el contenido del controlTextView . El modo de edición de laTextView puede cambiarse en rangos de texto dentro delTextBuffer

usandoTextTag s.

Puedes obtener el modo actual de edición usando el método:

setting = textview.get_editable()

Cuando laTextView no es editable probablemente deberías ocultar el cursor usando el método:

textview.set_cursor_visible( valor )

El argumentovalor puede ser TRUE o FALSE y especifica si el cursor debe ser visible. LaTextView puedeajustar las lineas de texto que son demasiado largas para que quepan en una única linea de la ventana. Elcomportamiento predeterminado es no ajustar las lineas. Esto se puede cambiar usando el método:

textview.set_wrap_mode( wrap_mode )

Este método te permite especificar que el texto debe ajustarse en los límites de palabras o caracteres. El argumentoword_wrap puede ser:

gtk.WRAP_NONE # sin ajustegtk.WRAP_CHAR # ajuste por caracteresgtk.WRAP_WORD # ajuste por palabras

La justificación predetermianda del texto en unaTextView se puede fijar y obtener usando los métodos:

textview.set_justification( justification )justification = textview.get_justification()

dondejustification puede ser:

gtk.JUSTIFY_LEFT # justificación a la izquierda

168

Page 177: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

gtk.JUSTIFY_RIGHT # justificación a la derechagtk.JUSTIFY_CENTER # justificación al centro

Nota

La justificación será JUSTIFY_LEFT si elmodo de ajuste es WRAP_NONE. Las etiquetasasociadas con unTextBuffer pueden cambiar la justificación predeterminada.

Se pueden modificar y ver otros atributos predeterminados en unaTextView como el margen izquierdo, el margenderecho, las tabulaciones y la indentación de párrafos, usando los siguientes métodos:

# margen izquierdotextview.set_left_margin( left_margin )left_margin = textview.get_left_margin()

# margen derechotextview.set_right_margin( right_margin )right_margin = textview.get_right_margin()

# indentacióntextview.set_indent( indent )indent = textview.get_indent()

textview.set_pixels_above_lines( pixels_above_line )pixels_above_line = textview.get_pixels_above_lines()

textview.set_pixels_below_lines( pixels_below_line )pixels_below_line = textview.get_pixels_below_lines()

textview.set_pixels_inside_wrap( pixels_inside_wrap )pixels_inside_wrap = textview.get_pixels_inside_wrap()

# tabulacionestextview.set_tabs( tabs )tabs = textview.get_tabs()

left_margin , right_margin , indent , pixels_above_lines , pixels_below_lines ypixels_inside_wrap se especifican en píxeles. Los valores predeterminados de estos parámetros sepueden modificar con etiquetas asociadas a unTextBuffer . tabs es unpango.TabArray .

El programa de ejemplotextview-basic.py [examples/textview-basic.py] ilustra el uso básico del controlTextView :

169

Page 178: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

Figura 13.1. Ejemplo básico de Vista de Texto

El código fuente del programa es:

1 #!/usr/bin/env python23 # example textview-basic.py45 import gtk67 class TextViewExample:8 def toggle_editable(self, checkbutton, textview):9 textview.set_editable(checkbutton.get_active())

1011 def toggle_cursor_visible(self, checkbutton, textview):12 textview.set_cursor_visible(checkbutton.get_active())13

170

Page 179: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

14 def toggle_left_margin(self, checkbutton, textview):15 if checkbutton.get_active():16 textview.set_left_margin(50)17 else:18 textview.set_left_margin(0)1920 def toggle_right_margin(self, checkbutton, textview):21 if checkbutton.get_active():22 textview.set_right_margin(50)23 else:24 textview.set_right_margin(0)2526 def new_wrap_mode(self, radiobutton, textview, val):27 if radiobutton.get_active():28 textview.set_wrap_mode(val)2930 def new_justification(self, radiobutton, textview, val):31 if radiobutton.get_active():32 textview.set_justification(val)3334 def close_application(self, widget):35 gtk.mainquit()3637 def __init__(self):38 window = gtk.Window(gtk.WINDOW_TOPLEVEL)39 window.set_resizable(gtk.TRUE)40 window.connect("destroy", self.close_application)41 window.set_title("TextView Widget Basic Example")42 window.set_border_width(0)4344 box1 = gtk.VBox(gtk.FALSE, 0)45 window.add(box1)46 box1.show()4748 box2 = gtk.VBox(gtk.FALSE, 10)49 box2.set_border_width(10)50 box1.pack_start(box2, gtk.TRUE, gtk.TRUE, 0)51 box2.show()5253 sw = gtk.ScrolledWindow()54 sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)55 textview = gtk.TextView()56 textbuffer = textview.get_buffer()57 sw.add(textview)58 sw.show()59 textview.show()6061 box2.pack_start(sw)62 # Load the file textview-basic.py into the text window63 infile = open("textview-basic.py", "r")6465 if infile:66 string = infile.read()67 infile.close()68 textbuffer.set_text(string)6970 hbox = gtk.HButtonBox()71 box2.pack_start(hbox, gtk.FALSE, gtk.FALSE, 0)72 hbox.show()

171

Page 180: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

7374 vbox = gtk.VBox()75 vbox.show()76 hbox.pack_start(vbox, gtk.FALSE, gtk.FALSE, 0)77 # check button to toggle editable mode78 check = gtk.CheckButton("Editable")79 vbox.pack_start(check, gtk.FALSE, gtk.FALSE, 0)80 check.connect("toggled", self.toggle_editable, textview)81 check.set_active(gtk.TRUE)82 check.show()83 # check button to toggle cursor visiblity84 check = gtk.CheckButton("Cursor Visible")85 vbox.pack_start(check, gtk.FALSE, gtk.FALSE, 0)86 check.connect("toggled", self.toggle_cursor_visible, textview)87 check.set_active(gtk.TRUE)88 check.show()89 # check button to toggle left margin90 check = gtk.CheckButton("Left Margin")91 vbox.pack_start(check, gtk.FALSE, gtk.FALSE, 0)92 check.connect("toggled", self.toggle_left_margin, textview)93 check.set_active(gtk.FALSE)94 check.show()95 # check button to toggle right margin96 check = gtk.CheckButton("Right Margin")97 vbox.pack_start(check, gtk.FALSE, gtk.FALSE, 0)98 check.connect("toggled", self.toggle_right_margin, textview)99 check.set_active(gtk.FALSE)

100 check.show()101 # radio buttons to specify wrap mode102 vbox = gtk.VBox()103 vbox.show()104 hbox.pack_start(vbox, gtk.FALSE, gtk.FALSE, 0)105 radio = gtk.RadioButton(None, "WRAP__NONE")106 vbox.pack_start(radio, gtk.FALSE, gtk.TRUE, 0)107 radio.connect("toggled", self.new_wrap_mode, textview, gtk.WRAP_NONE)108 radio.set_active(gtk.TRUE)109 radio.show()110 radio = gtk.RadioButton(radio, "WRAP__CHAR")111 vbox.pack_start(radio, gtk.FALSE, gtk.TRUE, 0)112 radio.connect("toggled", self.new_wrap_mode, textview, gtk.WRAP_CHAR)113 radio.show()114 radio = gtk.RadioButton(radio, "WRAP__WORD")115 vbox.pack_start(radio, gtk.FALSE, gtk.TRUE, 0)116 radio.connect("toggled", self.new_wrap_mode, textview, gtk.WRAP_WORD)117 radio.show()118119 # radio buttons to specify justification120 vbox = gtk.VBox()121 vbox.show()122 hbox.pack_start(vbox, gtk.FALSE, gtk.FALSE, 0)123 radio = gtk.RadioButton(None, "JUSTIFY__LEFT")124 vbox.pack_start(radio, gtk.FALSE, gtk.TRUE, 0)125 radio.connect("toggled", self.new_justification, textview,126 gtk.JUSTIFY_LEFT)127 radio.set_active(gtk.TRUE)128 radio.show()129 radio = gtk.RadioButton(radio, "JUSTIFY__RIGHT")130 vbox.pack_start(radio, gtk.FALSE, gtk.TRUE, 0)131 radio.connect("toggled", self.new_justification, textview,

172

Page 181: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

132 gtk.JUSTIFY_RIGHT)133 radio.show()134 radio = gtk.RadioButton(radio, "JUSTIFY__CENTER")135 vbox.pack_start(radio, gtk.FALSE, gtk.TRUE, 0)136 radio.connect("toggled", self.new_justification, textview,137 gtk.JUSTIFY_CENTER)138 radio.show()139140 separator = gtk.HSeparator()141 box1.pack_start(separator, gtk.FALSE, gtk.TRUE, 0)142 separator.show()143144 box2 = gtk.VBox(gtk.FALSE, 10)145 box2.set_border_width(10)146 box1.pack_start(box2, gtk.FALSE, gtk.TRUE, 0)147 box2.show()148149 button = gtk.Button("close")150 button.connect("clicked", self.close_application)151 box2.pack_start(button, gtk.TRUE, gtk.TRUE, 0)152 button.set_flags(gtk.CAN_DEFAULT)153 button.grab_default()154 button.show()155 window.show()156157 def main():158 gtk.main()159 return 0160161 if __name__ == "__main__":162 TextViewExample()163 main()

Las lineas 8-32 definen las retrollamadas para los botones de exclusión mútua y los botones de activación que seusan para cambiar los atributos predeterminados de laTextView . Las lineas 53-61 crean unaScrolledWindow

para contener laTextView . La ScrolledWindow se coloca en unaVBox con los botones que se crean en laslineas 70-138. ElTextBuffer asociado con laTextView se rellena con el contenido del archivo fuente en laslineas 62-68.

13.3. Buffers de TextoEl TextBuffer (Buffer de Texto) es el componente principal del sistema de edición de texto de PyGTK. Contieneel texto, lasTextTag (Etiquetas de Texto) en unaTextTagTable (Tabla de Etiquetas de Texto) y lasTextMark

(Marcas de Texto) que juntos describen cómo debe visualizarse el texto y permiten al usuario modificar el textointeractivamente. Como ya se dijo en la sección anterior, unTextBuffer está asociado con una o másTextView

(Vistas de Texto), que muestran el contenido delTextBuffer .

Un TextBuffer se puede crear automáticamente cuando se crea unaTextView o se puede crear con la función:

textbuffer = TextBuffer( tabla =None)

dondetabla es unaTextTagTable . Si no se especificatabla (o si es None) unaTextTagTable se creará parael Buffer de Texto.

173

Page 182: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

Hay un montón de métodos que se pueden usar para:

• insertar y borrar texto en un buffer

• crear, borrar y manipular marcas

• manipular el cursor y la selección

• crear, aplicar y borrar etiquetas

• especificar y manipularTextIter (Iteradores de Texto)

• obtener información de estado

13.3.1. Información de estado de un Buffer de Texto

Puedes saber el número de lineas en untextbuffer usando el método:

line_count = textbuffer.get_line_count()

De manera análoga puedes saber el número de caracteres en eltextbuffer usando:

char_count = textbuffer.get_char_count()

Cuando el contenido deltextbuffer cambia la bandera de modificación del buffer de texto se activa. El estadode la bandera de modificación se puede consultar usando el método:

modified = textbuffer.get_modified()

Si el programa guarda el contenido del buffer de texto el siguiente método se puede usar para reiniciar la banderade modificación:

textbuffer.set_modified( setting )

13.3.2. Creación de Iteradores de Texto

Un TextIter se usa para especificar una localización dentro de unTextBuffer entre dos caracteres. Losmétodos deTextBuffer que manipulan texto usan Iteradores de Texto para especificar dónde se aplicará elmétodo. Los Iteradores de Texto tienen un gran número de métodos que se describen en la secciónTextIter .

Los métodos básicos delTextBuffer que se usan para crearTextIter s son:

iter = textbuffer.get_iter_at_offset( char_offset )

iter = textbuffer_get_iter_at_line( line_number )

iter = textbuffer.get_iter_at_line_offset( line_number , line_offset )

174

Page 183: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

iter = textbuffer.get_iter_at_mark( mark )

get_iter_at_offset () crea un iterador que se situa después de tantos caracteres como diga el argumentochar_offset a partir del comienzo del buffer de texto.

get_iter_at_line () crea un iterador que está justo antes del primer caracter en la linea que diga el parámetroline_number .

get_iter_at_line_offset () crea un iterador que está justo detrás del carácter especificado por el parámetroline_offset en la linea especificada por el parámetroline_number .

get_iter_at_mark () crea un iterador que está en la misma posición que la marca especificada por el parámetromark .

Los siguientes métodos crean uno o más iteradores en localizaciones específicas del buffer de texto:

startiter = textbuffer.get_start_iter()

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 después del último caracter en el buffer de texto.

get_bounds () crea una tupla de dos iteradores que están justo antes del primer caracter y justo detrás del últimocaracter del buffer de texto respectivamente.

get_selection_bounds () crea una tupla de dos iteradores que están en las mismas posiciones que las marcasinsert y selection_bound en el buffer de texto.

13.3.3. Inserción, Obtención y Eliminación de Texto

El texto de unTextBuffer se puede fijar con el método:

textbuffer.set_text( text )

Este método reemplaza el contenido actual del buffer de texto con el texto que se le pasa como parámetro.

El método más general para insertar caracteres en un buffer de texto es:

textbuffer.insert( iter , text )

el cual inserta el textotext en la posición del buffer de texto especificada por el iteradoriter .

Si quieres simular la inserción de texto que produciría un usuario interactivo usa este método:

175

Page 184: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

result = textbuffer.insert_interactive( iter , text , default_editable )

el cual inserta el textotext en la posición del buffer de texto especificada por el iteradoriter pero sólo si laposición es editable (es decir, no tiene una etiqueta que especifique que el texto no es editable) y el argumentodefault_editable es TRUE. El resultado indica si el texto fue insertado o no.

El argumentodefault_editable indica si es editable o no cuando el texto no tiene una etiqueta que lo especi-fique;default_editable normalmente se determina llamando al métodoget_editable () deTextView .

Otros métodos 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 función auxiliar que inserta el texto en la posición actual del cursor.

insert_range () copia el texto, pixbuffers y etiquetas entrestart y end de unTextBuffer (si las etiquetaspertenecen a otro buffer de texto la tabla de etiquetas debe ser la misma) y los inserta en el buffer de texto en laposición especificada poriter .

Las versiones interactivas de estos métodos funcionan de la misma forma excepto que sólo insertarán el texto silas posiciones son editables.

Finalmente, el texto se puede insertar y asociar a etiquetas al mismo tiempo usando los métodos:

textbuffer.insert_with_tags(iter, text, tag1, tag2, ...)

textbuffer.insert_with_tags_by_name(iter, text, tagname1, tagname2, ...)

insert_with_tags () inserta el textotext en el buffer de texto en la posición especificada poriter y le aplicalas 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 métodos:

textbuffer.delete( start , end )

result = textbuffer.delete_interactive( start , end , default_editable )

delete () borra el texto entre los iteradoresstart y end en el buffer de texto.

delete_interactive () borra todo el texto editable (determinado por las etiquetas de texto aplicables y elargumentodefault_editable ) entrestart y end .

176

Page 185: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

Puedes obtener una copia del texto de un buffer de texto usando los métodos:

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 entrestart y end ; si el argumentoinclude_hidden_chars es FALSE el texto que no se visualice no se devuelve. Los caracteres querepresentan imágenes incrustadas o controles, son excluidos.

get_slice () es igual queget_text () excepto que el texto que devuelve incluye un carácter 0xFFFC por cadaimágene incrustada o control.

13.3.4. Marcas de Texto

LasTextMark s (Marcas de Texto) son similares a losTextIter s ya que también especifican posiciones en unTextBuffer entre dos caracteres. Sin embargo, las Marcas de Texto mantienen su posición aunque el buffercambie. Los métodos de las Marcas de Texto se describirán en la secciónTextMark .

Un buffer de texto contiene dos marcas de fábrica: la marcainsert (insertar) y la marcaselection_bound

(límite de la selección). La marcainsert es la posición predeterminada de inserción del texto y la marcaselection_bound combinada con la marcainsert mark definen un rango de selección.

Las marcas de fábricas se pueden recuperar con los métodos:

insertmark = textbuffer.get_insert()

selection_boundmark = textbuffer.get_selection_bound()

Las marcasinsert y selection_bound se puden colocar simultáneamente en una posición usando el método:

textbuffer.place_cursor( where )

where es un iterador de texto que especifica la posición. El métodoplace_cursor () es necesario para evitarcrear una selección temporal si las marcas se movieran individualmente.

LasTextMark s se crean usando el método:

mark = textbuffer.create_mark( mark_name , where , left_gravity =FALSE)

dondemark_name es el nombre que se le asigna a la marca (puede ser None para crear una marca anónima),where es el iterador de texto que especifica la posición de la marca en el buffer de texto yleft_gravity indicadónde se pondrá la marca cuando se inserte texto en la marca (a la izquierda si es TRUE o a la derecha si esFALSE).

Se puede mover una marca en el buffer de texto usando los métodos:

textbuffer.move_mark( mark , where )

177

Page 186: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

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 esun iterador de texto qeu especifica la nueva posición.

Una marca se puede borrar de un buffer de texto usando los métodos:

textbuffer.delete_mark( mark )

textbuffer.delete_mark_by_name( name)

Se puede recuperar una marca a partir de su nombre con el método:

mark = textbuffer.get_mark( name)

13.3.5. Creación y Uso de Etiquetas de Texto

Las TextTag s (Etiquetas de Texto) contienen uno o más atributos (por ejemplo, colores de frente y de fondo,fuentes de texto, editabilidad) que se pueden aplicar a uno o más rangos de texto en un buffer de texto. Losatributos que se pueden especificar en unaTextTag se describirán en la secciónTextTag .

UnaTextTag se puede crear con atributos e instalada en laTextTagTable (Tabla de Etiquetas de Texto) de unTextBuffer (Buffer de Texto) usando el siguiente método:

tag = textbuffer.create_tag(name=None, attr1=val1, attr2=val2, ...)

dondename es una cadena de texto que especifica el nombre de la etiqueta o None si la etiqueta es una etiquetaanónima y los pares de clave-valor especifican los atributos que la etiqueta tendrá. Mira la secciónTextTag paramás información acerca de qué atributos se pueden establecer mediante las propiedades de unaTextTag

Una etiqueta se puede aplicar a un rango de texto en un buffer de texto usando los métodos:

textbuffer.apply_tag( tag , start , end )

textbuffer.apply_tag_by_name( name, start , end )

tag es la etiqueta que se va a aplicar al texto.name es el nombre de la etiqueta a aplicar.start (comienzo) yend (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 métodos:

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 método:

178

Page 187: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

textbuffer.remove_all_tags( start , end )

13.3.6. Inserción de Imágenes y Controles

Además de texto, unTextBuffer puede contener imágenes y un punto de anclaje para controles. Se puede añadirun control a unaTextView en un punto de anclaje. Se puede añadir un control diferente en cadaTextView quetenga un buffer con un punto de anclaje.

Se puede insertar un pixbuf con el siguiente método:

textbuffer.insert_pixbuf( iter , pixbuf )

dondeiter especifica la posición en eltextbuffer donde insertar elpixbuf . La imágen contará como uncaracter y será representada en lo que devuelveget_slice () (pero no en lo que devuelveget_text () ) como elcaracter Unicode "0xFFFC".

Un control GTK+ se puede insertar en unaTextView en una posición del buffer especificada por unTextChildAnchor (Anclaje de Hijo del Texto). ElTextChildAnchor contará como un carácter representadopor "0xFFFC" de manera similar a un pixbuf.

El TextChildAnchor se puede crear e insertar en el buffer usando este método:

anchor = text_buffer.create_child_anchor( iter )

dondeiter es la posición del anclaje.

También se puede crear e insertar unTextChildAnchor con dos operaciones por separado:

anchor = gtk.TextChildAnchor()

text_buffer.insert_child_anchor( iter , anchor )

Después se puede añadir el control alTextView en la posición del anclaje usando el método:

text_view.add_child_at_anchor( child , anchor )

Se puede obtener la lista de controles en una posición especifica del buffer usando el método:

widget_list = anchor.get_widgets()

También se puede añadir un control alTextView usando el método:

text_view.add_child_in_window( child , which_window , xpos , ypos )

179

Page 188: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

donde el controlechild se coloca en la ventanawhich_window en la posición especificada porxpos e ypos .El parámetrowhich_window indica en cuál de las ventanas que componen el controlTextView se colocará elcontrol:

gtk.TEXT_WINDOW_TOP # ventana superior del textogtk.TEXT_WINDOW_BOTTOM # ventana inferior del textogtk.TEXT_WINDOW_LEFT # ventana izquierda del textogtk.TEXT_WINDOW_RIGHT # ventana derecha del textogtk.TEXT_WINDOW_TEXT # ventana de texto del textogtk.TEXT_WINDOW_WIDGET # ventana de control del texto

13.4. Iteradores de TextoLos TextIters (Iteradores de Texto) representan una posición entre dos caracteres en unTextBuffer . LosTextIters se crean normalmente usando un método de la claseTextBuffer . Los TextIters se invalidancuando el número de caracteres de unTextBuffer cambia (excepto para elTextIter que se usa para insercióno borrado). Insertar o borrar pixbuffers o anclajes también invalidan unTextIter .

Hay un gran número de métodos asociados con un objetoTextIter . Se agrupan en las siguientes seccionessegún funciones similares.

13.4.1. Atributos de los Iteradores de Texto

El TextBuffer que contiene elTextIter se puede recuperar usando el método:

buffer = iter.get_buffer()

Los siguientes métodos se pueden usar para obtener la posición delTextIter en elTextBuffer :

offset = iter.get_offset() # devuelve el desplazamiento en el buffer

line_number = iter.get_line() # devuelve el número de linea

line_offset = iter.get_line_offset() # devuelve el desplazamiento en la linea

numchars = iter.get_chars_in_line() # devuelve el número de caracteres en la linea

13.4.2. Atributos de Texto de un Iterador de Texto

El objetoPangoLanguage que se usa en una determinada posición del iterador en elTextBuffer se obtienellamando al método:

language = iter.get_language()

Hay otro método más general para obtener los atributos de texto en una posición de unTextIter :

result = iter.get_attributes( values )

180

Page 189: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

donderesult indica si el parámetrovalues (un objeto de la claseTextAttributes ) fue modificado. Elparámetrovalues se obtiene usando el siguiente método de la claseTextView :

values = textview.get_default_attributes()

Los siguientes atributos se pueden obtener a partir de un objeto de la claseTextAttributes (no está implemen-tado en PyGTK <=1.99.15):bg_color color de fondofg_color color de frentebg_stipple bitmap de patrón de fondofg_stipple bitmap de patrón de frenterise desplazamiento del texto sobre la linea baseunderline estilo de subrayadostrikethrough indica si el texto aparece tachadodraw_bg TRUE si algunas banderas afectan al dibujado del fondojustification estilo de la justificacióndirection la dirección del textofont PangoFontDescription en usofont_scale escala de la fuente en usoleft_margin posición del márgen izquierdoright_margin posición del márgen derechopixels_above_lines espacio en píxeles sobre una lineapixels_below_lines espacio en píxeles debajo de una lineapixels_inside_wrap espacio en píxeles entre lineas solapadastabs PangoTabArray en usowrap_mode modo de ajuste en usolanguage PangoLanguage en usoinvisible si el texto es invisible (sin implementar en GTK+ 2.0)bg_full_height si el fondo está ocupando el alto completo de lineaeditable si el texto es editablerealized si el texto está realizadopad1pad2pad3pad4

13.4.3. Copiar un Iterador de Texto

Se puede duplicar unTextIter usando el método:

iter_copy = iter.copy()

13.4.4. Recuperar Texto y Objetos

Se pueden obtener varias cantidades de texto y objetods de unTextBuffer usando los siguientes métodosTextBuffer :

char = iter.get_char() # devuelve un caracter o 0 si se ha llegado al final del buffer

text = start.get_slice( end )# devuelve el texto entre los iteradores de principio y fin

181

Page 190: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

text = start.get_text( end ) # devuelve el texto entre los iteradores de principio y fin

pixbuf = iter.get_pixbuf() # devuelve el pixbuf en esa posición (o None)

anchor = iter.get_child_anchor() # devuelve el anclaje (o None)

mark_list = iter.get_marks() # devuelve una lista de marcas

tag_list = iter.get_toggled_tags()# devuelve una lista de etiquetas que están activadas o desactivadas

tag_list = iter.get_tags() # devuelve una lista de etiquetas por prioridades

13.4.5. Comprobar Condiciones en un Iterador de Texto

Las condiciones de una etiqueta en unTextIter se pueden comprobar usando los siguientes métodos:

result = iter.begins_tag( tag =None) # TRUE si la etiqueta está activada en el iterador

result = iter.ends_tag( tag =None)# TRUE si la etiqueta está desactivada en el iterador

result = iter.toggles_tag( tag =None)# TRUE si la etiqueta está activa o desactivada en el iterador

result = iter.has_tag( tag ) # TRUE si existe la etiqueta en el iterador

Estos métodos devuelven TRUE si el parámetrotag satisface la condición en eliter . Si el parámetrotag esNone en los primeros tres métodos entonces el resultado es TRUE si cualquier etiqueta satisface la condición enel iterador.

Los siguientes métodos indican si el texto en la posición del iterador es editable o permite inserción de texto:

result = iter,editable()

result = iter.can_insert( default_editability )

El métodoeditable () indica si eliter está en un rango editable de texto mientras que el métodocan_insert ()indica si el texto se puede insertar en eliter considerando la editabilidad predeterminada de laTextView , elTextBuffer y las etiquetas aplicables. La editabilidad predeterminada se obtiene con el método:

default_editability = textview.get_editable()

La equivalencia de dosTextIter s se puede determinar con el método:

are_equal = lhs.equal( rhs )

DosTextIter s se pueden comparar con el método:

182

Page 191: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

result = lhs.compare( rhs )

El result será: -1 silhs es menor querhs ; 0 si lhs es igual querhs ; y, 1 si lhs es mayor querhs .

Para determinar si unTextIter está entre otros dosTextIter s usa el método:

result = iter.in_range( start , end )

El result será TRUE siiter está entrestart y end . Atención: start y end deben estar en un ordenascendente. Esto se puede garantizar con el método:

first.order( second )

que reordenará los desplazamientos para quefirst esté antes quesecond .

13.4.6. Comprobar la localización de un Iterador de Texto

La posición de unTextIter con respecto al texto en unTextBuffer se puede determinar con los siguientesmétodos:

result = iter.starts_word() # empieza palabra

result = iter.ends_word() # termina palabra

result = iter.inside_word() # dentro de palabra

result = iter.starts_sentence() # empieza frase

result = iter.ends_sentence() # termina frase

result = iter.inside_sentence() # dentro de frase

result = starts_line() # empieza linea

result = iter.ends_line() # termina linea

result será TRUE si elTextIter está en la posición dada. Estos métodos son autoexplicativos. La definiciónde los elementos de texto y sus límites se determina por el lenguaje usado en elTextIter . Fíjate que una lineaes una colección de frases similar a un párrafo.

Los siguientes métodos se pueden usar para determinar si unTextIter está al principio o al final de unTextBuffer :

result = iter.is_start()

result = iter.is_end()

result es TRUE si elTextIter está al principio o al final delTextBuffer .

183

Page 192: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

Ya que unTextBuffer puede contener múltiples caracteres que se visualizan efectivamente como una posicióndel cursor (por ejemplo retorno de carro y nueva linea o una letra con un símbolo de acento) es posible que unTextIter podría estar en una posición que no corresponde con una posición de cursor. El siguiente métodoindica si un iterador está en una posición del cursor:

result = iter.is_cursor_position()

13.4.7. Movimiento a través del Texto

Los TextIter s se pueden mover por unTextBuffer en varias unidades de texto. La definición de las unidadesde texto se establece en el objetoPangoLanguage que se use en la posición delTextIter . Los métodos básicosson:

result = iter.forward_char() # avanzar un caracter

result = iter.backward_char() # retroceder un caracter

result = iter.forward_word_end() # avanzar hasta el final de la palabra

result = iter.backward_word_start() # retroceder al principio de la palabra

result = iter.forward_sentence_end() # avanzar al final de la frase

result = iter.backward_sentence_start() # retroceder al principio de la frase

result = iter.forward_line() # avanzar al principio de la linea

result = iter.backward_line() # retroceder al principio de la linea

result = iter.forward_to_line_end() # avanzar al final de la linea

result = iter.forward_cursor_position() # avanzar una posición del cursor

result = iter.backward_cursor_position() # retroceder una posición del cursor

result es TRUE si elTextIter se movió y FALSE si elTextIter está al principio o al final delTextBuffer .

Todos estos métodos (exceptoforward_to_line_end ()) tiene métodos equivalentes que reciben una cantidad(que puede ser positiva o negativa) para mover elTextIter en múltiples 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 )

184

Page 193: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

result = iter.backward_lines( count )

result = iter.forward_cursor_positions( count )

result = iter.backward_cursor_positions( count )

13.4.8. Moverse a una Posición Específica

Un TextIter se puede mover a una posición específica en elTextBuffer usando los siguientes métodos:

iter.set_offset( char_offset ) # moverse al desplazamiento de caracteres específico

iter.set_line( line_number ) # moverse al principio de la linea dada como parámetro

iter.set_line_offset( char_on_line )# moverse al caracter especificado en la linea actual

iter.forward_to_end() # moverse al final del buffer

Además, unTextIter se puede mover a una posición donde una etiqueta esté activada o desactivada usando losmétodos:

result = iter.forward_to_tag_toggle( tag )

result = iter.backward_to_tag_taoggle( tag )

result es TRUE si elTextIter se movió a la nueva posición donde exista la etiquetatag . Si la etiquetatag

es None entonces elTextIter se moverá a la siguiente posición donde exista una etiqueta.

13.4.9. Buscar en el Texto

Una búsqueda de una cadena de texto en unTextBuffer se hace con los siguientes métodos:

match_start, match_end = iter.forward_search( str , flags , limit =None) # búsqueda ←↩hacia adelante

match_start, match_end = iter.backward_search( str , flags , limit =None) # búsqueda ←↩hacia atrás

El valor de retorno es una tupla que contiene losTextIter s que indican la posición del primer caracter quecoincide con la búsqueda y la posición del primer caracter después del encuentro.str es la cadena a buscar. Elparámetroflags modifica las condiciones de la búsqueda y puede tomar los siguientes valores:

gtk.TEXT_SEARCH_VISIBLE_ONLY # se ignoran los caracteres invisibles

gtk.TEXT_SEARCH_TEXT_ONLY # se ignoran los pixbuffers y los anclajes de hijos

limit es un parámetro opcional que limita el rango de la búsqueda.

185

Page 194: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

13.5. Marcas de TextoUna TextMark (Marca de Texto) indica una posición en unTextBuffer entre dos caracteres que se mantieneaunque se modifique el buffer. LasTextMark s se crean, se mueven y se borran usando los métodos delTextBuffer que se describen en la secciónTextBuffer .

Un TextBuffer tiene dos marcas de fábrica llamadas:insert y selection_bound que se refieren al punto deinserción y el límite de la selección (puede que se refieran a la misma posición).

El nombre de unaTextMark se puede recuperar usando el método:

name = textmark.get_name()

Por defecto las marcas que no soninsert no son visibles (esa marca se muestra como una barra vertical). Lavisibilidad de una marca se puede activar y obtener usando los métodos:

setting = textmark.get_visible()

textmark.set_visible( setting )

dondesetting es TRUE si la marca es visible.

El TextBuffer que contiene unaTextMark se puede recuperar usando el método:

buffer = textmark.get_buffer()

Puedes determinar si unaTextMark ha sido borrada usando el método:

setting = textmark.get_deleted()

La gravedad izquierda de unaTextMark se puede recuperar usando el método:

setting = textmark.get_left_gravity()

La gravedad izquierda de una TextMark indica si donde acabará la marca después de una inserción. Si la gravedadizquierda es TRUE la marca se pondrá a la izquierda de la inserción; si es FALSE, a la derecha de la inserción

13.6. Etiquetas de Texto y Tablas de EtiquetasLas etiquetas de texto especifican atributos que se pueden aplicar a un rango de texto en un buffer de texto. Cadabuffer de texto tiene una tabla de etiquetas de texto que contiene las etiquetas de texto que se pueden aplicar dentrodel buffer. Las tablas de etiquetas de texto se pueden usar en más de un buffer de texto para ofrecer estilos de textoconsistentes.

13.6.1. Etiquetas de Texto

LasTextTag s (Etiquetas de Texto) pueden tener nombre o ser anónimas. UnaTextTag se crea usando la función:

186

Page 195: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

tag = gtk.TextTag( name=None)

Si el name (nombre) no se especifica o si es None latag (etiqueta) será anónima. LasTextTag s también sepueden crear usando el método deTextBuffer create_tag () que también te permite especificar los atributos yañade la etiqueta a la tabla de etiquetas del buffer (veáse la subsecciónTextBuffer ).

Los atributos que pueden aparecer en unaTextTag son:

187

Page 196: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

name Lectura / Escrit-ura

Nombre de la etiqueta de texto. None si es anónima.

background Escritura Color de fondo como una cadena de textoforeground Escritura Color de frente como una cadena de textobackground-gdk Lectura / Escrit-

uraColor de fondo como un GdkColor

foreground-gdk Lectura / Escrit-ura

Color de frente como un GdkColor

background-stipple Lectura / Escrit-ura

Bitmap a usar como una máscara cuando se dibuje el texto de fondo

foreground-stipple Lectura / Escrit-ura

Bitmap a usar como una máscara cuando se dibuje el texto de frente

font Lectura / Escrit-ura

Descripción de la fuente como una cadena de texto, por ejemplo, "SansItalic 12"

font-desc Lectura / Escrit-ura

Descripción de la feunte como un objeto PangoFontDescription

family Lectura / Escrit-ura

Nombre de la familia de la fuente, por ejemplo, Sans, Helvetica, Times,Monospace

style Lectura / Escrit-ura

Estilo de la fuente como un PangoStyle, por ejemplo,PANGO_STYLE_ITALIC.

variant Lectura / Escrit-ura

Variante de la fuente como un PangoVariant, por ejemplo,PANGO_VARIANT_SMALL_CAPS.

weight Lectura / Escrit-ura

Peso de la fuente como un entero, mira los valores predefinidos enPangoWeight; por ejemplo, PANGO_WEIGHT_BOLD.

stretch Lectura / Escrit-ura

Estrechamiento de la fuente como un PangoStretch, por ejemplo,PANGO_STRETCH_CONDENSED.

size Lectura / Escrit-ura

Tamaño de fuente en unidades Pango.

size-points Lectura / Escrit-ura

Tamaño de fuente en puntos

scale Lectura / Escrit-ura

Tamaño de fuente como un factor de escala relativo al tamaño de fuentepredeterminado. Esta propiedad se adapta a los cambios en el tema, etc,por tanto se recomienda su uso. Pango tiene algunos valores predefinidostales como PANGO_SCALE_X_LARGE.

pixels-above-lines Lectura / Escrit-ura

Píxeles de espacio blanco por encima de los párrafos

pixels-below-lines Lectura / Escrit-ura

Píxeles de espacio blanco por debajo de los párrafos

pixels-inside-wrap Lectura / Escrit-ura

Píxeles de espacio blanco entre las lineas de un párrafo

editable Lectura / Escrit-ura

Si el texto puede modificarse por el usuario

wrap-mode Lectura / Escrit-ura

Si las lineas no se ajustan, se ajustan en limites de palabra o se ajustanen limites de caracteres

justification Lectura / Escrit-ura

Justificación izquierda, derecha o central

direction Lectura / Escrit-ura

Dirección del Texto, por ejemplo, derecha a izquierda o izquierda aderecha

left-margin Lectura / Escrit-ura

Ancho del márgen izquierdo en píxeles

indent Lectura / Escrit-ura

Cantidad de indentado para los párrafos, en píxeles

strikethrough Lectura / Escrit-ura

Si hay que tachar el texto

right-margin Lectura / Escrit-ura

Ancho del márgen derecho en píxeles

underline Lectura / Escrit-ura

Estilo de subrayado para este texto

rise Lectura / Escrit-ura

Desplazamiento del texto por encima de la linea base (por debajo de lalinea base si es negativo) en píxeles

background-full-height Lectura / Escrit-ura

Si el color de fondo rellena la altura completa de la linea o sólo la alturade los caracteres marcados

language Lectura / Escrit-ura

El idioma en el que está el texto, como un código ISO. Pango puedeusar esto como una ayuda para visualizar el texto. Si no entiendes esteparámetro, probablemente no lo necesitas.

tabs Lectura / Escrit-ura

Tabulaciones personalizadas para el texto

invisible Lectura / Escrit-ura

Si el texto está oculto. No implementado en GTK 2.0

188

Page 197: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

Se pueden establecer los atributos con este método:

tag.set_property(name, value)

Dondename es una cadena de texto que contiene el nombre de la propiedad yvalue es el valor que se le va aponer.

De la misma forma, el valor de un atributo se puede recuperar con el método:

value = tag.get_property(name)

Ya que la etiqueta no tiene un valor para cada atributo hay una serie de propiedades booleanas que indican si elatributo ha sido establecido:background-set Lectura / Escrituraforeground-set Lectura / Escriturabackground-stipple-set Lectura / Escrituraforeground-stipple-set Lectura / Escriturafamily-set Lectura / Escriturastyle-set Lectura / Escrituravariant-set Lectura / Escrituraweight-set Lectura / Escriturastretch-set Lectura / Escriturasize-set Lectura / Escriturascale-set Lectura / Escriturapixels-above-lines-set Lectura / Escriturapixels-below-lines-set Lectura / Escriturapixels-inside-wrap-set Lectura / Escrituraeditable-set Lectura / Escriturawrap-mode-set Lectura / Escriturajustification-set Lectura / Escrituradirection-set Lectura / Escrituraleft-margin-set Lectura / Escrituraindent-set Lectura / Escriturastrikethrough-set Lectura / Escrituraright-margin-set Lectura / Escrituraunderline-set Lectura / Escriturarise-set Lectura / Escriturabackground-full-height-set Lectura / Escrituralanguage-set Lectura / Escrituratabs-set Lectura / Escriturainvisible-set Lectura / Escritura

Por tanto, para obtener el atributo de una etiqueta, primero tienes que comprobar si el atributo ha sido establecidoen la etiqueta. Por ejemplo, para obtener un valor válido de justificación 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 añade a laTextTagTable . Las etiquetas conprioridad más alta tienen preferencia si hay múltiples etiquetas para establecer el mismo atributo para un rango detexto. La prioridad se puede obtener y fijar con los métodos:

189

Page 198: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

priority = tag.get_priority()

tag.set_priority( priority )

La prioridad de una etiqueta debe estar entre 0 y uno menos del tamaño de laTextTagTable .

13.6.2. Tablas de Etiquetas de Texto

UnaTextTagTable (Tabla de Etiquetas de Texto) se crea al crear unTextBuffer . También se puede crear unaTextTagTable con la función:

table = TextTagTable()

Se puede añadir unaTextTag (Etiqueta de Texto) a unaTextTagTable usando el método:

table.add( tag )

La etiquetatag no debe estar ya en la tabla y no debe tener el mismo nombre que otra etiqueta en la tabla.

Puedes buscar una etiqueta en una tabla usando el método:

tag = table.lookup( name)

El método devuelve la etiquetatag en la tabla que tenga el nombrename o None si no hay ninguna etiqueta conese nombre.

Se puede borrar unaTextTag de unaTextTagTable con el método:

table.remove( tag )

El tamaño de laTextTagTable se puede consultar con el método:

size = table.get_size()

13.7. Un ejemplo de Vista de TextoEl programa de ejemplotesttext.py [examples/testtext.py] (que se deriva del programatesttext.c incluidoen la distribución GTK+ 2.0.x) demuestra el uso del controlTextView y sus objetos asociados:TextBuffer s,TextIter s, TextMark s, TextTag s, TextTagTable s. La figura Figura 13.2. Ejemplo de Vista de Texto ilustrasu funcionamiento:

190

Page 199: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

Figura 13.2. Ejemplo de Vista de Texto

El programatesttext.py[examples/testtext.py] define unas cuantas clases además de la clase principalTestText :

• La claseBuffer , en las lineas 99-496, es una subclase de la clasegtk.TextBuffer . Proporciona lascapacidades de edición del buffer que usan los objetosView .

191

Page 200: Tutorial Pygtk

Capítulo 13. Control de Vista de Texto

• La claseView , lineas 498-1126, es una subclase de la clasegtk.Window y contiene un objetogtk.TextView

que usa un objetoBuffer en lugar de un objetogtk.TextBuffer . Proporciona una ventana y visualiza loscontenidos de un objetoBuffer además de una barra de menú.

• La claseFileSel , lineas 73-97, es una subclase de la clasegtk.FileSelection para proporcionar unaselección de ficheros para los contenidos de la claseBuffer .

• La claseStack proporciona objetos de pilas simples.

La ventana de ciclo de color se implementa usando etiquetas de texto que se aplican a una sección del textoen un buffer. Las lineas 109-115 (en el método__init__ () ) crean estas etiquetas y las lineas 763-784 (en elmétododo_apply_colors () ) aplican las etiquetas de colores a una sección del texto de dos en dos caracteres.Las lienas 202-239 proporcionan los métodos (color_cycle_timeout (), set_colors () y cycle_colors ())que producen el ciclo de color cuando está activado. El ciclo de color se activa estableciendo (linea 220) lapropiedadforeground_gdk de las etiquetas individualescolor_tags (que también establecen la propiedadforeground_set ). El ciclo de color se desactiva poniendo la propiedadforeground_set a FALSE (linea222). Los colores se cambian periodicamente al desplazar el tono del color (start_hue (line 237))

Un nuevoBuffer se rellena con un contenido de ejemplo cuando se selecciona el menú Test->Example (el métodofill_example_buffer () en las lineas 302-372). El buffer de ejemplo contien texto de varios colores, estilos,idiomas y pixbuffers. El métodoinit_tags () (lineas 260-300) establece una variedad deTextTag s para usarlascon el texto de ejemplo. La señal de evento de estas etiquetas se conecta al métodotag_event_handler ()(lineas 241-256) para ilustrar la captura de los eventos de botón y movimiento.

El modo de ajuste del controlTextView está puesto a WRAP_WORD (linea 580) y las ventanas de borde delTextView se visualizan al establecer su tamaño en las lineas 587-588 y las lineas 596-597. Las ventanas del bordederecho e izquierdo se usan para mostrar los números de linea y las ventanas de borde superior e inferior muestranlas posiciones de tabulación cuando se utilizan tabulaciones personalizadas. Las ventanas de borde se actualizancuando se recibe la señal "expose-event" en elTextView (lineas 590 y 599) El métodoline_numbers_expose ()(lineas 1079-1116) determina si las ventanas de borde izquierdo o derecho tienen un evento de exposición y si esasí calculan el tamaño del área de exposición. Entonces la localización del principio de la linea y el número delinea para cada linea en el área de exposición se calcula en el métodoget_lines () (lineas 1057-1077). Losnúmeros de linea se dibujan en la ventana de borde en la posición (transformada por la linea 1109).

Las posiciones personalizadas de tabulación se visualizan en las ventanas de borde superior e inferior de unaforma similar (lineas 1013-1055). Sólo se visualizan cuando el cursor se mueve dentro de un rango de textoque tiene el atributo de tabulaciones propias. Esto se detecta manejando la señal "mark-set" en el métodocursor_set_handler () (lineas 999-1011) e invalidando las ventanas de borde superior e inferior si la marca aactivar es la marcainsert .

Los controles movibles se añaden al objetoView con el métododo_add_children () (lineas 892-899) el cualllama al métodoadd_movable_children () (lines 874-890). Los hijos songtk.Label s (Etiquetas) que puedenarrastrarse por las ventanas que forman parte de un controlTextView .

De la misma forma, los controles se añaden a las ventanas delTextView de unaView y el Buffer usando elmétododo_add_focus_children () (lineas 901-949).

192

Page 201: Tutorial Pygtk

Capítulo 14. Control de Vista de ÁrbolEste capítulo estará disponible en una fecha futura.

193

Page 202: Tutorial Pygtk

Capítulo 15. Controles sin documentar¡Todos estos controles necesitan voluntarios! :) Por favor, considerad contribuir a nuestro tutorial.

Si tienes que usar alguno de estos controles que están sin documentar, te recomiendo encarecidamente que leeches un vistazo a los archivos *.c en la distribución de PyGTK. Los nombres de los métodos de PyGTK son muydescriptivos. Una vez que entiendes como funcionan las cosas, no es muy difícil averiguar como usar un controlsimplemente mirando la definición de sus métodos. Esto, además de algunos ejemplos del código de otros, nodebería ser un problema.

Cuando realmente entiendas todos los métodos de un nuevo control sin documentar, por favor piensa en escribirun tutorial sobre él para que otros puedan beneficiarse de tu tiempo.

15.1. Etiqueta de Aceleración

15.2. Menú de Opciones

15.3. Elementos de Menú

15.3.1. Elemento de Menú de Activación

15.3.2. Elemento de Menú de Exclusión Mútua

15.3.3. Elemento de Menú de Separación

15.3.4. Elemento de Menú de Cascada

15.4. Curvas

15.5. Diálogo de Mensaje

15.6. Curva Gamma

15.7. Enchufes y Clavijas

194

Page 203: Tutorial Pygtk

Capítulo 16. Cambiar Atributos deControlesEste capítulo describe los métodos usados para modificar controles (y en objetos). Esto puede ser usado paracambiar el estilo, el espaciamiento, el tamaño, etc.

El método:

widget.activate()

hace que el control emita la señal "activate".

El método:

widget.set_sensitive( sensitive )

cambia la sensitivadd del control (es decir, si reacciona a eventos). Sisensitive es TRUE el control recibiráeventos; si es FALSE el control no recibirá eventos. Un control que está insensitivo normalmente se visualiza enun tono gris.

El método:

widget.set_size_request( width , height )

establece el tamaño del control para que tenga el ancho dado por el parámetrowidth y el alto dado por elparámetroheight .

16.1. Métodos de Banderas de ControlesLos métodos:

widget.set_flags( flags )

widget.unset_flags( flags )

flags = widget.flags()

ponen, quitan y leen las banderas de los objetosgtk.Object y gtk.Widget . Lasflags (banderas) pueden sercualquiera de las banderas estandar:

IN_DESTRUCTION # en destrucciónFLOATING # flotandoRESERVED_1 # reservada 1RESERVED_2 # reservada 2TOPLEVEL # nivel más altoNO_WINDOW # no ventanaREALIZED # realizado

195

Page 204: Tutorial Pygtk

Capítulo 16. Cambiar Atributos deControles

MAPPED # mapeadoVISIBLE # visibleSENSITIVE # sensitivoPARENT_SENSITIVE # padre sensitivoCAN_FOCUS # puede recibir el focoHAS_FOCUS # tiene el focoCAN_DEFAULT # puede ser el control predeterminadoHAS_DEFAULT # es el control predeterminadoHAS_GRAB # tiene la exclusividad de los eventosRC_STYLE # estilo rcCOMPOSITE_CHILD # hijo compuestoNO_REPARENT # no reparentadoAPP_PAINTABLE # aplicación pintableRECEIVES_DEFAULT # recibe predeterminadoDOUBLE_BUFFERED # tiene doble buffer

El método:

widget.grab_focus()

permite a un control adquirir el foco asumiendo que tiene la bandera CAN_FOCUS activada.

16.2. Métodos de Visualización de ControlesLos métodos:

widget.show()

widget.show_all()

widget.hide()

widget.hide_all()

widget.realize()

widget.unrealize()

widget.map()

widget.unmap()

controlan la visualización de los controles.

El métodoshow() (mostrar) hace que el control se visualice al llamar a los métodosrealize () y map() .

El métodohide () (ocultar) hace que el control se oculte y también lo desmapea usando el métodounmap() si esnecesario.

Los métodosshow_all () (mostrar todos) yhide_all () (ocultar todos) hacen que el control y todos sus hijos semuestren o se oculten.

El métodorealize () (realizar) hace que se reserven los recursos que necesita el control incluyendo su ventana.

196

Page 205: Tutorial Pygtk

Capítulo 16. Cambiar Atributos deControles

El métodounrealize () (desrealizar) libera la ventana del control y otros recursos. Desrealizar un control tambiénlo oculta y desmapea.

El métodomap() (mapear) hace que se reserve espacio en la pantalla para el control; esto sólo se aplica a loscontroles que necesitan ser manipulados por el manejador de ventanas. Mapear un control también lo realiza si esnecesario.

El métodounmap() (desmapear) elimina un control de la pantalla y también lo oculta si es necesario.

16.3. Atajos de Teclador de ControlesLos siguientes métodos:

widget.add_accelerator( accel_signal , accel_group , accel_key , accel_mods , accel_flags )

widget.remove_accelerator( accel_group , accel_key , accel_mods )

añaden y eliminan atajos de tecla a ungtk.AcceleratorGroup (Grupo de Atajos de Teclado) que debenasociarse al control de nivel más alto para manejar los atajos de teclado.

El parámetroaccel_signal es una señal que el control puede emitir.

El parámetroaccel_key es una tecla del teclado para usarla como atajo de teclado.

El parámetroaccel_mods es un grupo de modificadores que se añaden a la teclaaccel_key (por ejemploShift(mayúsculas),Control , etc.):

SHIFT_MASKLOCK_MASKCONTROL_MASKMOD1_MASKMOD2_MASKMOD3_MASKMOD4_MASKMOD5_MASKBUTTON1_MASKBUTTON2_MASKBUTTON3_MASKBUTTON4_MASKBUTTON5_MASKRELEASE_MASK

El parámetroaccel_flags es un conjunto de opciones sobre cómo se muestra la información del atajo de teclado.Los valores válidos son:

ACCEL_VISIBLE # mostrar la tecla de aceleración en el control

ACCEL_LOCKED # no permitir que la ventana del atajo de teclado cambie

Un grupo de atajos de teclado se crea con la función:

197

Page 206: Tutorial Pygtk

Capítulo 16. Cambiar Atributos deControles

accel_group = gtk.AccelGroup()

El grupoaccel_group se añade una ventana de alto nivel con el siguiente método:

window.add_accel_group( accel_group )

Ejemplo de cómo añadir un atajo de teclado:

menu_item.add_accelerator("activate", accel_group,ord(’Q’), gtk.gdk.CONTROL_MASK, gtk.ACCEL_VISIBLE)

16.4. Métodos para el Nombre de los ControlesLos siguientes métodos de controles cambian y leen el nombre de un control:

widget.set_name( name)

name = widget.get_name()

El parámetroname es la cadena de caracteres que se asocia al control. Esto es útil para especificar estilos quese usan en controles específicos dentro de una aplicación. El nombre del control puede usarse para limitar laaplicación del estilo a diferencia de usar la clase del control. Mira el capítulo sobre cómo usar ficheros rc de GTKpara más información.

16.5. Estilos de ControlesLos siguientes métodos cambian y leen el estilo asociado a un control:

widget.set_style( style )

style = widget.get_style()

La siguiente función:

style = get_default_style()

obtiene el estilo predeterminado.

Un estilo contiene la información gráfica que necesita un control para dibujarse a sí mismo en sus diferentesestados:

STATE_NORMAL # El estado durante la operación normalSTATE_ACTIVE # El control está activado, como cuando se pulsa un botónSTATE_PRELIGHT # El puntero del ratón está sobre el controlSTATE_SELECTED # El control está seleccionado

198

Page 207: Tutorial Pygtk

Capítulo 16. Cambiar Atributos deControles

STATE_INSENSITIVE # El control está desactivado

Un estilo contiene los siguientes atributos:

fg # una lista de 5 colores de frente, uno para cada estadobg # una lista de 5 colores de fondolight # una lista de 5 colores, creados en el método set_style()dark # una lista de 5 colores, creados en el método set_style()mid # una lista de 5 colores, creados en el método set_style()text # una lista de 5 coloresbase # una lista de 5 colorestext_aa # una lista de 5 colores a medio camino entre text/base

black # el color negrowhite # el color blancofont_desc # la descripción de fuente pango predeterminada

xthickness #ythickness #

fg_gc # una lista de 5 contextos gráficos - creados en el método set_style()bg_gc # una lista de 5 contextos gráficos - creados en el método set_style()light_gc # una lista de 5 contextos gráficos - creados en el método set_style()dark_gc # una lista de 5 contextos gráficos - creados en el método set_style()mid_gc # una lista de 5 contextos gráficos - creados en el método set_style()text_gc # una lista de 5 contextos gráficos - creados en el método set_style()base_gc # una lista de 5 contextos gráficos - creados en el método set_style()black_gc # una lista de 5 contextos gráficos - creados en el método set_style()white_gc # una lista de 5 contextos gráficos - creados en el método set_style()

bg_pixmap # una lista de 5 pixmaps

Cada atributo se puede leer directamente comostyle.black y style.fg_gc[gtk.STATE_NORMAL] . Todoslos atributos son de solo lectura exceptostyle.black , style.white , style.black_gc y style.white_gc .

Un estilo existente se puede copiar para una modificación posterior usando el método:

new_style = style.copy()

lo cual copia los atributos del estilo excepto las listas de contextos gráficos y las listas de colores light, dark y mid.

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 frente), los siguientes métodos de loscontroles deben usarse:

widget.modify_fg(state, color)widget.modify_bg(state, color)widget.modify_text(state, color)

199

Page 208: Tutorial Pygtk

Capítulo 16. Cambiar Atributos deControles

widget.modify_base(state, color)widget.modify_font(font_desc)widget.set_style(style)

Al fijar el estilo se reservarán los colores del estilo y crearán los contextos gráficos. La mayoría de los controles seredibujarán automáticamente después de cambiar el estilo. Si el estilo es None entonces el control volverá a usarsu estilo predeterminado.

No todos los cambios al estilo afectarán al control. Por ejemplo, cambiar el color de fondo de unaLabel nocambiará el color de fondo porque el controlLabel no tiene su propia ventanagtk.gdk.WIndow . El fondo deuna etiqueta depende del color de fondo del padre de la etiqueta. El uso de unaEventBox para contener unaEtiqueta permite cambiar el color de fondo de la etiqueta. Mira la secciónEventBox para ver un ejemplo.

200

Page 209: Tutorial Pygtk

Capítulo 17. Temporizadores, Entrada/Saliday Funciones de Inactividad17.1. TemporizadoresPuede que estes preguntándote cómo hacer que GTK haga algo útil cuando está dentro de la funciónmain (). Bien,tienes varias opciones. Usando la siguiente función puedes crear un temporizador que será llamado en intervalosregulares.

timeout_handler_id = timeout_add(interval, function, ...)

El argumentointerval es el número de milisegundos entre llamadas sucesivas a tu función. El argumentofunction es la operación que deseas que se llame. Cualquier argumento después del segundo se pasará a tufunción como datos. El valor de retorno es un entero, "timeout_handler_id" que se puede utilizar para eliminar eltemporizador llamando a:

timeout_remove( timeout_handler_id )

También puedes parar el temporizador devolviendo cero o FALSE desde tu función. Obviamente esto significaque si quieres que el temporizador se siga llamando, debes devolver un valor distinto de cero, como TRUE.

Tu función será algo parecido a:

def timeout_callback(...):

El número de argumentos a tu función debe coincidir con el número de argumentos de datos especificados entimeout_add ().

17.2. Monitorizar la Entrada/SalidaPuedes comprobar si hay algo para leer o escribir a un fichero (bien a un fichero Python o a un fichero de más bajonivel del Sistema Operativo) y automáticamente invocar una función. Esto es especialmente útil para aplicacionesde red. La función:

imput_handler_id = input_add_full( source , condition , callback )

Donde el primer argumento (source ) es el fichero abierto (objeto de fichero de Python o descriptor de fichero demás bajo nivel) que quieres monitorizar. El métodoinput_add_full () usa el descriptor de fichero internamenteasi que lo sacará del objeto de fichero Python si es necesario. El segundo argumento (condition ) especifica quées lo que quieres monitorizar. Puede ser cualquiera de:

INPUT_READ - Llama a tu función cuando hay datos disponibles para leer de tufichero.

INPUT_WRITE - Llama a tu función cuando el fichero está listo para escritura.

201

Page 210: Tutorial Pygtk

Capítulo 17. Temporizadores,Entrada/Salida y Funciones de

Inactividad

INPUT_EXCEPTION - Llama a tu función cuando se produce una excepción en tu fichero

Estas constantes se definen en el módulo gtk.gdk module. Estoy seguro de que ya te has dado cuenta que el tercerargumento,callback , es la función que quieres que se llame cuando las condiciones anteriores se cumplen.

El valor de retorno,input_handler_id se puede usar para parar de monitorizar el fichero usando la siguientefunción:

input_remove( imput_handler_id )

Tu función será algo parecido a:

def input_callback(source, condition):

Dondesource y condition son los que especificastes antes. El valor de source será el descriptor de fichero debajo nivel y no el objeto fichero Python (es decir, el valor que devuelve el método de fichero de Pythonfileno ()).

17.3. Funciones de Inactividad¿Qué pasa si quieres que se llame a una función cuando no esté pasando nada? Usa la función:

idle_handler_id = idle_add(callback, ...)

Cualquier argumento más allá del primero (se indican con ...) se pasan a la funcióncallback en orden. El valorde retornoidle_handler_id se utiliza como una referencia al manejador.

Esto hace que GTK llame a la función especificada cuando no está pasando nada más.

idle_remove( idle_handler_id )

No explicaré el significado de los argumentos ya que son iguales a los explicados anteriormente. La función quese le pasa como primer argumento aidle_add () será llamada siempre que se presente la oportunidad. Como conlas otras, devolviendo FALSE hará que ya no se llame más.

Tu función debe ser algo similar a:

def callback(...):

Los argumentos que se le pasen a tu función son los mismos que especificastes en la funciónidle_add () .

202

Page 211: Tutorial Pygtk

Capítulo 18. Procesamiento Avanzadode Eventos y Señales18.1. Métodos de SeñalesLos métodos de señales son métodos de la clase Gobject que se heredan en los GtkObjects incluyendo todos loscontroles GTK.

18.1.1. Conectar y Desconectar Manejadores de Señales

handler_id = object.connect(name, cb, cb_args)

handler_id = object.connect_after(name, cb, cb_args)

handler_id = object.connect_object(name, cb, slot_object, cb_args)

handler_id = object.connect_object_after(name, cb, slot_object, cb_args)

object.disconnect(handler_id)

Los primeros cuatro métodos conectan un manejador de señales (cb ) a un objetoGtkObject (object ) parala señal especificada por name, y devuelven un valorhandler_id que identifica la conexión. cb_args

son cero o más argumentos que serán pasados al manejadorcb . Los métodosconnect_after () yconnect_object_after () hará que se llame a sus manejadores después de todos los demás manejadores(incluyendo los manejadores predeterminados) que estén conectados a esa señal. Cada manejador de señales deun objeto tiene su propio conjunto de argumentos. Debes referirte a la documentación GTK para averiguar quéargumentos deben usarse por un manejador de señales aunque hay información para los controles comunes en elapéndice GTK Signals. El manejador de señales general es algo así como:

def signal_handler(object, ...., cb_args):

Los manejadores de señales que se definen como un método de una clase Python (especificados en los métodosconnect () comoself.cb ) tendrán un argumento adicional como primer argumento, la instancia del objetoself :

signal_handler(self, object, ...., cb_args)

Los métodosconnect_object () y connect_object_after () llaman al manejador de señales con elslot_object en lugar delobject como primer argumento:

def signal_handler(slot_object, ...., func_args):

def signal_handler(self, slot_object, ...., func_args):

El métododisconnect () elimina la conexión entre un manejador de señal y un objeto señal. El argumentohandler_id especifica la conexión a eliminar.

203

Page 212: Tutorial Pygtk

Capítulo 18. Procesamiento Avanzadode Eventos y Señales

18.1.2. Bloquear y Desbloquear Manejadores de SeñalesLos siguientes métodos:

object.handler_block(handler_id)

object.handler_unblock(handler_id)

bloquean y desbloquean el manejador de señales especificado por el argumentohandler_id . Cuando unmanejador de señales está bloqueado no se invocará cuando la señal se produzca.

18.1.3. Emitir y Parar SeñalesLos siguientes métodos:

object.emit(name, ...)

object.emit_stop_by_name(name)

emiten y paran la señal especificada por el argumentoname respectivamente. Emitir la señal hace que el manejadorpredeterminado y los manejadores de usuario se ejecuten. El métodoemit_stop_by_name () abortará la emisiónde señales actual.

18.2. Emisión y Propagación de SeñalesLa emisión de señales es el proceso por el cual GTK ejectua todos los manejadores para una señal y objetoespecíficos.

Ten en cuenta que el valor de retorno de una emisión de señales es el valor de retorno del último manejadorejecutado. Como las señales de eventos son todas del tipo RUN_LAST, este valor será el del manejadorpredeterminado (dado por GTK), a menos que uses el métodoconnect_after ().

La forma en la que un evento se trata (tal como "button_press_event") es:

• Empieza en el control donde se produjo el evento.

• Emite la señal genérica "event". Si ese manejador devuelve un valor de TRUE, para todo el procesamiento.

• En caso contrario, emite una señal específica "button_press_event". Si eso devuelve TRUE, para todo elprocesamiento.

• En caso contrario, ve al padre del control y repite los dos pasos anteriores.

• Continua hasta que algún manejador devuelva TRUE, o hasta que se llegue al control de más alto nivel.

Algunas consecuencias de lo anterior son:

• El valor de retorno de tu manejador no tendrá efecto si hay un manejador predeterminado, a menos que loconectes conconnect_after ().

• Para evitar que el manejador predeterminado se llame, tienes que usar el métodoconnect () y usaremit_stop_by_name () - el valor de retorno sólo afecta a la propagación de la señal, no a la emisión ac-tual.

204

Page 213: Tutorial Pygtk

Capítulo 19. Tratamiento de Selecciones19.1. Perspectiva General de la SelecciónUno de los mecanismos de comunicación entre procesos que hay disponibles en X y GTK es las selecciones.Una selección identifica una porción de datos, por ejemplo, un trozo de texto, seleccionado por el usuario dealguna manera, por ejemplo, arrastrando con el ratón. Sólo una aplicación en un display (Nota del traductor:esto es terminología X) (el dueño) puede poseer una selección particular en un momento dado, por lo quecuando una aplicación solicita una selección, el dueño anterior debe indicarle al usuario que la selección ha sidoRELINQUISHED (!). Otras aplicaciones pueden solicitar el contenido de una selección de diferentes maneras,llamadas objetivos. Pueden haber muchas selecciones, pero la mayoría de las aplicaciones X sólo tratarán una, laselección primaria.

En la mayoría de los casos, no es necesario que una aplicación PyGTK trate directamente las selecciones. Loscontroles estándares, como el controlEntry , ya tienen la capacidad de solicitar la selección cuando sea necesario(por ejemplo, cuando el usuario arrastra el ratón sobre el texto), y de recuperar el contenido de la selección de otroscontroles o aplicaciones (por ejemplo, cuando el usuario hace clic en el segundo botón del ratón). Sin embargo,puede haber casos en los que quieras darle a los controles la capacidad de proporcionar la selección, o puede quequieras recuperar objetivos no disponibles en principio.

A fundamental concept needed to understand selection handling is that of the atom. An atom is an integer thatuniquely identifies a string (on a certain display). Certain atoms are predefined by the X server, GTK.

19.2. Recuperar la SelecciónRecuperar la selección es un proceso asíncrono. Para empezar el proceso hay que llamar a:

result = widget.selection_convert( selection , target , time =0)

Esto convierte la selecciónselection en el formato especificado por el objetivotarget . La selecciónselection es un átomo correspondiente al tipo de selección; las selecciones comunes son las cadenas de texto:

PRIMARY # primaria

SECONDARY # secundaria

Si es posible, el campo tiempotime debería ser el momento en el que se produjo el evento que ocasionó laselección. Esto ayuda a cerciorarse que los eventos ocurren en el orden en el que el usuario los solicita. Sinembargo, si no está disponible (por ejemplo, si la conversión se produjo en una señal "clicked"), entonces puedesutilizar 0, lo que significa el momento actual. El resultado será TRUE si la conversión tuvo éxito o FALSE en casocontrario.

Cuando el dueño de la selección responde a la petición, una señal "selection_received" (selección_recibida) seenvia a tu aplicación. El manejador de esta señal recibe un objeto de tipogtk.SelectionData , que tiene lossiguientes atributos:

selection # seleccióntarget # objetivotype # tipoformat # formato

205

Page 214: Tutorial Pygtk

Capítulo 19. Tratamiento deSelecciones

data # datos

selection y target son los valores que tu diste en el métodoselection_convert () .

type es un átomo que identifica el tipo de datos que ha devuelto el dueño de la selección. Algunos valoresposibles son "STRING", una cadena de caracteres latin-1, "ATOM", una serie de átomos, "INTEGER", un entero,"image/x-xpixmap", etc. La mayoría de los objetivos sólo pueden devolver un tipo.

La lista de átomos estándares en X y GTK es:

PRIMARYSECONDARYARCATOMBITMAPCARDINALCOLORMAPCURSORCUT_BUFFER0CUT_BUFFER1CUT_BUFFER2CUT_BUFFER3CUT_BUFFER4CUT_BUFFER5CUT_BUFFER6CUT_BUFFER7DRAWABLEFONTINTEGERPIXMAPPOINTRECTANGLERESOURCE_MANAGERRGB_COLOR_MAPRGB_BEST_MAPRGB_BLUE_MAPRGB_DEFAULT_MAPRGB_GRAY_MAPRGB_GREEN_MAPRGB_RED_MAPSTRINGVISUALIDWINDOWWM_COMMANDWM_HINTSWM_CLIENT_MACHINEWM_ICON_NAMEWM_ICON_SIZEWM_NAMEWM_NORMAL_HINTSWM_SIZE_HINTSWM_ZOOM_HINTSMIN_SPACENORM_SPACEMAX_SPACE END_SPACE,SUPERSCRIPT_XSUPERSCRIPT_Y

206

Page 215: Tutorial Pygtk

Capítulo 19. Tratamiento deSelecciones

SUBSCRIPT_XSUBSCRIPT_YUNDERLINE_POSITIONUNDERLINE_THICKNESSSTRIKEOUT_ASCENTSTRIKEOUT_DESCENTITALIC_ANGLEX_HEIGHTQUAD_WIDTHWEIGHTPOINT_SIZERESOLUTIONCOPYRIGHTNOTICEFONT_NAMEFAMILY_NAMEFULL_NAMECAP_HEIGHTWM_CLASSWM_TRANSIENT_FORCLIPBOARD

format da la longitud de las unidades (por ejemplo caracteres) en bits. Normalmente puedes olvidar esto cuandorecibes datos.

data son los datos devueltos en forma de cadena de texto.

PyGTK empaqueta todos los datos devueltos en una cadena de texto. Esto hace que sea fácil manipular objetivosde cadenas de texto. Para obetener objetivos de otros tipos (como ATOM o INTEGER) el programa debe extraerla información de la cadena de texto devuelta. PyGTK proporciona dos métodos para obtener texto y una lista deobjetivos a partir de los datos devueltos:

text = selection_data.get_text()

targets = selection_data.get_targets()

dondetext es una cadena de texto que contiene el texto de la selección ytargets es una lista de los objetivosque acepta la selección.

Dado ungtk.SelectionData que contiene una lista de objetivos el método:

has_text = selection_data.targets_include_text()

devolverá TRUE si uno o más de los objetivos pueden tener texto.

El programa de ejemplogetselection.py[examples/getselection.py] enseña cómo recibir un objetivo "STRING"o "TARGETS" desde la selección primaria y luego imprimir los datos correspondientes en la consola cuando sehace clic en el botón asociado. La figura Figura 19.1. Ejemplo de Obtención de la Selección muestra la ventanadel programa:

207

Page 216: Tutorial Pygtk

Capítulo 19. Tratamiento deSelecciones

Figura 19.1. Ejemplo de Obtención de la Selección

El código fuente degetselection.py[examples/getselection.py] es:

1 #!/usr/bin/env python23 # example getselection.py45 import gtk67 class GetSelectionExample:8 # Signal handler invoked when user clicks on the9 # "Get String Target" button

10 def get_stringtarget(self, widget):11 # And request the "STRING" target for the primary selection12 ret = widget.selection_convert("PRIMARY", "STRING")13 return1415 # Signal handler invoked when user clicks on the "Get Targets" button16 def get_targets(self, widget):17 # And request the "TARGETS" target for the primary selection18 ret = widget.selection_convert("PRIMARY", "TARGETS")19 return2021 # Signal handler called when the selections owner returns the data22 def selection_received(self, widget, selection_data, data):23 # Make sure we got the data in the expected form24 if str(selection_data.type) == "STRING":25 # Print out the string we received26 print "STRING TARGET: %s" % selection_data.get_text()2728 elif str(selection_data.type) == "ATOM":29 # Print out the target list we received30 targets = selection_data.get_targets()31 for target in targets:32 name = str(target)33 if name != None:34 print "%s" % name35 else:36 print "(bad target)"37 else:38 print "Selection was not returned as \"STRING\" or \"ATOM\"!"3940 return gtk.FALSE414243 def __init__(self):44 # Create the toplevel window45 window = gtk.Window(gtk.WINDOW_TOPLEVEL)

208

Page 217: Tutorial Pygtk

Capítulo 19. Tratamiento deSelecciones

46 window.set_title("Get Selection")47 window.set_border_width(10)48 window.connect("destroy", gtk.mainquit)4950 vbox = gtk.VBox(gtk.FALSE, 0)51 window.add(vbox)52 vbox.show()5354 # Create a button the user can click to get the string target55 button = gtk.Button("Get String Target")56 eventbox = gtk.EventBox()57 eventbox.add(button)58 button.connect_object("clicked", self.get_stringtarget, eventbox)59 eventbox.connect("selection_received", self.selection_received)60 vbox.pack_start(eventbox)61 eventbox.show()62 button.show()6364 # Create a button the user can click to get targets65 button = gtk.Button("Get Targets")66 eventbox = gtk.EventBox()67 eventbox.add(button)68 button.connect_object("clicked", self.get_targets, eventbox)69 eventbox.connect("selection_received", self.selection_received)70 vbox.pack_start(eventbox)71 eventbox.show()72 button.show()7374 window.show()7576 def main():77 gtk.main()78 return 07980 if __name__ == "__main__":81 GetSelectionExample()82 main()

Las lineas 28-36 se encargan de la obtención de los datos de selección de "TARGETS" e imprime la lista de losnombres de los objetivos. Los botones están metidos en sus propias cajas de eventos porque una selección debeestar asociada a unagtk.gdkWindow y los botones son controles sin ventana en GTK+2.0.

19.3. Proporcionar la SelecciónProporcionar la selección es un poco más complicado. Tienes que registrar manejadores que se llamaran cuandose solicite tu selección. Para cada par selección-objetivo que controles tienes que hacer una llamada a:

widget.selection_add_target( selection , target , info )

widget (control), selection (selección), ytarget (objetivo) identifican las peticiones que este manejadorgestionará. Cuando llega una petición para una selección, la señal "selection_get" será llamada.info es un enteroque se puede usar como un identificador para el objetivo específico dentro de la retrollamada.

La retrollamada tiene la siguiente signatura:

209

Page 218: Tutorial Pygtk

Capítulo 19. Tratamiento deSelecciones

def selection_get(widget, selection_data, info, time):

El argumentogtk.SelectionData es el mismo que antes, pero esta vez, somos responsables de rellenar loscampostype , format y data . (El campoformat es importante aqui ya que el servidor X lo usa para saber si elcampodata necesita que se le cambie el orden de los bytes o no. Normalemente será 8 - un caracter - o 32 - unentero.) Esto se hace llamando al método:

selection_data.set( type , format , data )

Este método PyGTK sólo puede tratar datos de cadenas de caracteres por lo quedata debe ser cargado en unacadena Python peroformat será cualquier cosa que tenga el tamaño apropiado (por ejemplo 32 para átomos yenteros, 8 para cadenas). Los módulos Pythonstruct o StringIO pueden servir para convertir datos que no soncadenas de caracteres a cadenas de caracteres. Por ejemplo, puedes convertir una lista de enteros en una cadena yponerlos en el camposelection_data de esta forma:

ilist = [1, 2, 3, 4, 5]

data = apply(struct.pack, [’%di’%len(ilist)] + ilist)

selection_data.set("INTEGER", 32, data)

El siguiente método fija los datos de la selección a partir de esa cadena:

selection_data.set_text( str , len )

Cuando lo solicite el usuario, debes reclamar la posesión de la selección llamando a:

result = widget.selection_owner_set( selection , time =0L)

result será TRUE si el programa reclama laselection con éxito. Si otra aplicación reclama la posesión deselection , te llegará un eventoa "selection_clear_event".

Como ejemplo de proporcionar la selección, el programasetselection.py[examples/setselection.py] le añadefuncionalidad de selección a un botón biestado que está dentro de unagtk.EventBox . (Necesitamos lagtk.Eventbox porque la selección debe asociarse a unagtk.gdk.Window y un gtk.Button es un control sinventana en GTK+ 2.0.). Cuando el botón biestado se pulsa, el programa reclama la selección primaria. El únicoobjetivo soportado (aparte de los objetivos que proporciona la propia GTK), es el objetivo "STRING". Cuando sesolicita este objetivo, se devuelve una representación en cadena de caracteres de la hora actual. La figura Figura19.2. Ejemplo de Fijar la Selección ilustra la ventana del programa cuando el programa ha conseguido la posesiónde la selección primaria:

Figura 19.2. Ejemplo de Fijar la Selección

210

Page 219: Tutorial Pygtk

Capítulo 19. Tratamiento deSelecciones

El código fuente desetselection.py[examples/setselection.py] es:

1 #!/usr/bin/env python23 # example setselection.py45 import gtk6 import time78 class SetSelectionExample:9 # Callback when the user toggles the selection

10 def selection_toggled(self, widget, window):11 if widget.get_active():12 self.have_selection = window.selection_owner_set("PRIMARY")13 # if claiming the selection failed, we return the button to14 # the out state15 if not self.have_selection:16 widget.set_active(gtk.FALSE)17 else:18 if self.have_selection:19 # Not possible to release the selection in PyGTK20 # just mark that we don’t have it21 self.have_selection = gtk.FALSE22 return2324 # Called when another application claims the selection25 def selection_clear(self, widget, event):26 self.have_selection = gtk.FALSE27 widget.set_active(gtk.FALSE)28 return gtk.TRUE2930 # Supplies the current time as the selection.31 def selection_handle(self, widget, selection_data, info, time_stamp):32 current_time = time.time()33 timestr = time.asctime(time.localtime(current_time))3435 # When we return a single string, it should not be null terminated.36 # That will be done for us37 selection_data.set_text(timestr, len(timestr))38 return3940 def __init__(self):41 self.have_selection = gtk.FALSE42 # Create the toplevel window43 window = gtk.Window(gtk.WINDOW_TOPLEVEL)44 window.set_title("Set Selection")45 window.set_border_width(10)46 window.connect("destroy", gtk.mainquit)47 self.window = window48 # Create an eventbox to hold the button since it no longer has49 # a GdkWindow50 eventbox = gtk.EventBox()51 eventbox.show()52 window.add(eventbox)5354 # Create a toggle button to act as the selection55 selection_button = gtk.ToggleButton("Claim Selection")56 eventbox.add(selection_button)

211

Page 220: Tutorial Pygtk

Capítulo 19. Tratamiento deSelecciones

5758 selection_button.connect("toggled", self.selection_toggled, eventbox)59 eventbox.connect_object("selection_clear_event", self.selection_clear,60 selection_button)6162 eventbox.selection_add_target("PRIMARY", "STRING", 1)63 eventbox.selection_add_target("PRIMARY", "COMPOUND_TEXT", 1)64 eventbox.connect("selection_get", self.selection_handle)65 selection_button.show()66 window.show()6768 def main():69 gtk.main()70 return 07172 if __name__ == "__main__":73 SetSelectionExample()74 main()

212

Page 221: Tutorial Pygtk

Capítulo 20. Arrastrar y SoltarPyGTK tiene un conjunto de funciones de alto nivel para comunicación entre procesos a través del sistemaarrastrar-y-soltar. PyGTK puede realizar arrastrar-y-soltar sobre los protocoles de bajo nivel Xdnd y arrastrar-y-soltar Motif.

20.1. Perspectiva General de Arrastrar y SoltarUna aplicación con la capacidad de arrastrar-y-soltar primero define y configura el/los control/es para arrastrar-y-soltar. Cada control puede ser una fuente y/o destino para arrastrar-y-soltar. Te en cuenta que estos controlesdeben tener asociada una ventana X.

Los controles fuente pueden enviar datos de arrastrar, permitiendo así al usuario arrastrar cosas desde ellos,mientras que controles destino pueden recibir datos de arrastrar. Destino de arrastrar-y-soltar pueden limitarquiénes pueden enviarles datos, por ejemplo, la misma aplicación o cualquier aplicación (incluyéndose a símisma).

Para enviar y recibir datos se utilizan señales. Soltar un elemento en un control destino requiere una petición dedatos (para el control fuente) y un manejador de datos (para el control destino). Se pueden conectar manejadoresde señal adicionales si quieres saber cuándo empieza el usuario a arrastrar (en el mismo momento en el queempieza), cuándo se realiza el soltar, y cuando finaliza el proceso completo de arrastrar-y-soltar (con éxito o no).

Tu aplicación tiene que proporcionar los datos a los controles orígen cuando se solicite, lo cual implica tener unmanejador de señal para la solicitud de datos. Para controles destino, tienen que tener un manejador de señales dedatos recibidos.

Por tanto un ciclo habitual de arrastrar-y-soltar sería así:

• Se empieza a arrastrar.

• Se solicita los datos de arrastrar (cuando se suelta el botón).

• Se reciben los datos (puede ser en la misma o en otra aplicación).

• Se borran los datos de arrastrar (si el arrastre fue un movimiento).

• El proceso arrastrar-y-soltar termina.

Hay algunos pasos intermedios adicionales pero los veremos en detalle un poco más tarde.

20.2. Propiedades de Arrastrar y SoltarLos datos de arrastrar tienen las siguientes propiedades:

• Tipo de acción de arrastrar (por ejemplo ACTION_COPY (acción copiar), ACTION_MOVE (acción mover)).

• Tipo de arrastrar-y-soltar específico del cliente (un par de nombre y número).

• Tipo de formato de los datos enviados y recibidos.

213

Page 222: Tutorial Pygtk

Capítulo 20. Arrastrar y Soltar

Las acciones de arrastrar son bastante obvias, especifican si el control puede arrastrar con la/s acción/es especifi-cada, por ejemplo gtk.gdk.ACTION_COPY y/o gtk.gdk.ACTION_MOVE. Una acción gtk.gdk.ACTION_COPYsería el típico arrastrar y soltar sin que el origen se elimine mientras que una acción gtk.gdk.ACTION_MOVEsería exactamente igual pero se ’sugiere’ que se borren los datos orígen despueés de que la señal de recepción sellame. Hay más acciones como gtk.gdk.ACTION_LINK que puedes investigar cuando adquieras un poco más depráctica con arrastrar-y-soltar.

El tipo de arrastrar-y-soltar especificado por el cliente es mucho más flexible, porque será tu aplicación la que lodefina y compruebe. Tendrás que configurar tus controles destino para recibir ciertos tipos de arrastrar-y-soltarespecificando un nombre y/o un número. Es más fiable usar un nombre ya que otra aplicación puede estar usandoel mismo número con un significado completamente diferente.

Los tipos de emisión y recepción de datos (objetivo de selección) entran en juego sólo en tus manejadores dedatos solicitados y recibidos. El términoobjetivo de selecciónes un poco confuso. Es un término adaptado dela selección GTK (cortar/copiar y pegar). Lo queselection targetrealmente significa es el tipo de formato dedatos (por ejemploGdkAtom, entero, o cadena de caracteres) que se está enviando o recibiendo. Tu manejador dedatos solicitados tiene que especificar el tipo (selection target) de datos que está enviando y tu manejador de datosrecibidos tiene que manejar el tipo de datos recibidos (selection target).

20.3. Métodos de Arrastrar y Soltar

20.3.1. Configuración del Control Orígen

El métododrag_source_set () especifica un conjunto de tipos objetivo para una operación de arrastrar en uncontrol.

widget.drag_source_set( start_button_mask , targets , actions )

Los parámetros significan lo siguiente:

• widget especifica el control orígen

• start_button_mask especifica una máscara de bits de los botones que pueden empezar a arrastrar (porejemplo BUTTON1_MASK)

• targets especifica una lista de los tipos de datos objetivos que se manejarán.

• actions especifica un máscara de bits de las acciones posibles para arrastrar desde esta ventana.

214

Page 223: Tutorial Pygtk

Capítulo 20. Arrastrar y Soltar

El parámetrotargets es una lista de tuplas similar a:

(target, flags, info)

target especifica una cadena de caracteres que representa el tipo de arrastre.

flags restringen la aplicación del arrastre.flags puede ser 0 o las siguientes constantes:

gtk.TARGET_SAME_APP # El objetivo sólo se puede seleccionar para arrastresdentro de una sola aplicación.

gtk.TARGET_SAME_WIDGET # El objetivo sólo se puede seleccionar para arrastresdentro del mismo control.

info es un identificador entero asignado por la aplicación.

Si no es necesario que un control siga siendo el orígen de operaciones arrastrar-y-soltar, el métododrag_source_unset () se puede usar para eliminar un conjunto de tipos de objetivos arrastrar-y-soltar.

widget.drag_source_unset()

20.3.2. Señales en el Control Orígen

Las siguientes señales se le envian al control orígen durante una operación de arrastrar-y-soltar.

Tabla 20.1. Señales del Control de Orígen

drag_begin (comienzo de arrastre)def drag_begin_cb(widget, drag_context, data):drag_motion (movimiento de arras-tre)

def drag_motion_cb(widget, drag_context, x, y, time, data):

drag_data_get (obtención de datosde arrastre)

def drag_data_get_cb(widget, drag_context, selection_data, info, time,data):

drag_data_delete (eliminación dedatos de arrastre)

def drag_data_delete_cb(widget, drag_context, data):

drag_drop (arrastre soltado) def drag_drop_cb(widget, drag_context, x, y, time, data):drag_end (fin de arrastre) def drag_end_cb(widget, drag_context, data):

20.3.3. Configuración de un Control Destino

drag_dest_set () especifica que este control puede ser destino de operaciones arrastrar-y-soltar y dice los tiposque permite.

drag_dest_unset () especifica que el control no puede recibir más operaciones arrastrar-y-soltar.

widget.drag_dest_set( flags , targets , actions )

widget.drag_dest_unset()

215

Page 224: Tutorial Pygtk

Capítulo 20. Arrastrar y Soltar

flags especifican qué acciones GTK debe realizar de parte del control cuando se suelte algo en él. Los valoresposibles son:

gtk.DEST_DEFAULT_MOTION Si está activado para este control, GTK comprobará si el arrastre se corre-sponde con algún objetivo y acción de este control cuando se arrastre por encima deeste control. Entonces GTK llamará adrag_status () según corresponda.

gtk.DEST_DEFAULT_HIGHLIGHT Si está activado para este control, GTK resaltará el control siempre queel arrastre esté encima de este control y el formato y la acción sean aceptables.

gtk.DEST_DEFAULT_DROP Si está activado para este control, GTK comprobará si el arrastre se correspondecon algún objetivo y acción del control. Si es así, GTK llamará adrag_data_get ()de parte del control. No importa si el soltar tiene éxito o no, GTK llamará adrag_finish (). Si la acción fue mover, entonces si el arrastre tuvo éxito, se lepasará TRUE como argumento ’delete’ (borrar) adrag_finish ().

gtk.DEST_DEFAULT_ALL Si está activo, especifica que todas las acciones anteriores deben ejecutarse.

actions es una máscara de bits de las posibles acciones a realizar cuando se arrastre sobre este control. Losvalores posibles se pueden componer con la operación OR y son los siguientes:

gtk.gdk.ACTION_DEFAULT # acción predeterminadagtk.gdk.ACTION_COPY # acción copiargtk.gdk.ACTION_MOVE # acción movergtk.gdk.ACTION_LINK # acción enlazargtk.gdk.ACTION_PRIVATE # acción privadagtk.gdk.ACTION_ASK # acción preguntar

20.3.4. Señales en el Control Destino

Durante una operación arrastrar-y-soltar se le envia la siguiente señal al control destino.

Tabla 20.2. Señales del Control Destinodrag_data_received (datosrecibidos)

def drag_data_received_cb(widget, drag_context, x, y, selection_data,info, time, data):

El programa de ejemplodragndrop.py [examples/dragndrop.py] demuestra el uso de arrastrar y soltar en unaaplicación. Un botón con un icono xpm (engtkxpm.py [examples/gtkxpm.py]) es el orígen para el arrastre;proporciona tanto texto como datos xpm. Un control de disposición es el destino para soltar el xpm mientras queun botón es el destino para soltar el texto. La figura Figura 20.1. Ejemplo de Arrastrar y Soltar ilustra la ventanadel programa después de soltar el xpm en el control de disposición y el texto en el botón:

216

Page 225: Tutorial Pygtk

Capítulo 20. Arrastrar y Soltar

Figura 20.1. Ejemplo de Arrastrar y Soltar

El código fuente dedragndrop.py [examples/dragndrop.py] es:

1 #!/usr/bin/env python23 # example dragndrop.py45 import gtk6 import string, time78 import gtkxpm9

10 class DragNDropExample:11 HEIGHT = 60012 WIDTH = 60013 TARGET_TYPE_TEXT = 8014 TARGET_TYPE_PIXMAP = 8115 fromImage = [ ( "text/plain", 0, TARGET_TYPE_TEXT ),16 ( "image/x-xpixmap", 0, TARGET_TYPE_PIXMAP ) ]17 toButton = [ ( "text/plain", 0, TARGET_TYPE_TEXT ) ]18 toCanvas = [ ( "image/x-xpixmap", 0, TARGET_TYPE_PIXMAP ) ]1920 def layout_resize(self, widget, event):21 x, y, width, height = widget.get_allocation()22 if width > self.lwidth or height > self.lheight:23 self.lwidth = max(width, self.lwidth)

217

Page 226: Tutorial Pygtk

Capítulo 20. Arrastrar y Soltar

24 self.lheight = max(height, self.lheight)25 widget.set_size(self.lwidth, self.lheight)2627 def makeLayout(self):28 self.lwidth = self.WIDTH29 self.lheight = self.HEIGHT30 box = gtk.VBox(gtk.FALSE,0)31 box.show()32 table = gtk.Table(2, 2, gtk.FALSE)33 table.show()34 box.pack_start(table, gtk.TRUE, gtk.TRUE, 0)35 layout = gtk.Layout()36 self.layout = layout37 layout.set_size(self.lwidth, self.lheight)38 layout.connect("size-allocate", self.layout_resize)39 layout.show()40 table.attach(layout, 0, 1, 0, 1, gtk.FILL|gtk.EXPAND,41 gtk.FILL|gtk.EXPAND, 0, 0)42 # create the scrollbars and pack into the table43 vScrollbar = gtk.VScrollbar(None)44 vScrollbar.show()45 table.attach(vScrollbar, 1, 2, 0, 1, gtk.FILL|gtk.SHRINK,46 gtk.FILL|gtk.SHRINK, 0, 0)47 hScrollbar = gtk.HScrollbar(None)48 hScrollbar.show()49 table.attach(hScrollbar, 0, 1, 1, 2, gtk.FILL|gtk.SHRINK,50 gtk.FILL|gtk.SHRINK,51 0, 0)52 # tell the scrollbars to use the layout widget’s adjustments53 vAdjust = layout.get_vadjustment()54 vScrollbar.set_adjustment(vAdjust)55 hAdjust = layout.get_hadjustment()56 hScrollbar.set_adjustment(hAdjust)57 layout.connect("drag_data_received", self.receiveCallback)58 layout.drag_dest_set(gtk.DEST_DEFAULT_MOTION |59 gtk.DEST_DEFAULT_HIGHLIGHT |60 gtk.DEST_DEFAULT_DROP,61 self.toCanvas, gtk.gdk.ACTION_COPY)62 self.addImage(gtkxpm.gtk_xpm, 0, 0)63 button = gtk.Button("Text Target")64 button.show()65 button.connect("drag_data_received", self.receiveCallback)66 button.drag_dest_set(gtk.DEST_DEFAULT_MOTION |67 gtk.DEST_DEFAULT_HIGHLIGHT |68 gtk.DEST_DEFAULT_DROP,69 self.toButton, gtk.gdk.ACTION_COPY)70 box.pack_start(button, gtk.FALSE, gtk.FALSE, 0)71 return box7273 def addImage(self, xpm, xd, yd):74 hadj = self.layout.get_hadjustment()75 vadj = self.layout.get_vadjustment()76 style = self.window.get_style()77 pixmap, mask = gtk.gdk.pixmap_create_from_xpm_d(78 self.window.window, style.bg[gtk.STATE_NORMAL], xpm)79 image = gtk.Image()80 image.set_from_pixmap(pixmap, mask)81 button = gtk.Button()82 button.add(image)

218

Page 227: Tutorial Pygtk

Capítulo 20. Arrastrar y Soltar

83 button.connect("drag_data_get", self.sendCallback)84 button.drag_source_set(gtk.gdk.BUTTON1_MASK, self.fromImage,85 gtk.gdk.ACTION_COPY)86 button.show_all()87 # have to adjust for the scrolling of the layout - event location88 # is relative to the viewable not the layout size89 self.layout.put(button, int(xd+hadj.value), int(yd+vadj.value))90 return9192 def sendCallback(self, widget, context, selection, targetType, eventTime):93 if targetType == self.TARGET_TYPE_TEXT:94 now = time.time()95 str = time.ctime(now)96 selection.set(selection.target, 8, str)97 elif targetType == self.TARGET_TYPE_PIXMAP:98 selection.set(selection.target, 8,99 string.join(gtkxpm.gtk_xpm, ’\n’))

100101 def receiveCallback(self, widget, context, x, y, selection, targetType,102 time):103 if targetType == self.TARGET_TYPE_TEXT:104 label = widget.get_children()[0]105 label.set_text(selection.data)106 elif targetType == self.TARGET_TYPE_PIXMAP:107 self.addImage(string.split(selection.data, ’\n’), x, y)108109 def __init__(self):110 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)111 self.window.set_default_size(300, 300)112 self.window.connect("destroy", gtk.mainquit)113 self.window.show()114 layout = self.makeLayout()115 self.window.add(layout)116117 def main():118 gtk.main()119120 if __name__ == "__main__":121 DragNDropExample()122 main()

219

Page 228: Tutorial Pygtk

Capítulo 21. Ficheros rc de GTKGTK tiene su propia forma de tratar las opciones predeterminadas de las aplicaciones, usando ficheros rc. Conesto puedes cambiar los colores de cualquier control, y también se pueden usar para poner un dibujo de fondo enalgunos controles.

21.1. Funciones para Ficheros rcCuando comience tu aplicación, debes incluir una llamada a:

rc_parse( filename )

Pasándole el nombre de tu fichero rc enfilename . Esto hace que GTK analice este fichero, y use los valores deestilo para los tipos de controles que definas alli.

Si quieres tener un conjunto especial de controles que puedan tener un estilo diferente a los demás, o cualquierotra división lógica de controles, usa una llamada a:

widget.set_name( name)

El nombre que especifiques se le asignará a tu control recién creado. Esto te permitirá cambiar los atributos deeste control en el fichero rc.

Si usamos una llamada parecida a:

button = gtk.Button("Special Button")

button.set_name("special button")

Entonces se le dará el nombre "special button" a este botón y se podrá localizar en el fichero rc como "specialbutton.GtkButton".

El ejemplo de fichero rc que hay más abajo, cambia las propiedades de la ventana principal, y deja a todos sushijos que hereden el estilo descrito para el "main button" (botón principal). El código usado por la aplicación es:

window = gtk.Window(gtk.WINDOW_TOPLEVEL)

window.set_name("main window")

Y el estilo se define en el fichero rc usando:

widget "main window.*GtkButton*" style "main_button"

Lo cual le pone a todos los controlesButton en la "main window" el estilo "main_buttons" tal y como se defineen el fichero rc.

220

Page 229: Tutorial Pygtk

Capítulo 21. Ficheros rc de GTK

Como puedes ver, este es un sistema bastante potente y flexible. Usa tu imaginación para sacarle el mejorprovecho.

21.2. Formato de los Ficheros rc de GTKEl formato del fichero rc se muestra en el siguiente ejemplo . Este es el ficherotestgtkrc de la distribuciónGTK, pero le he añadido unos cuantos comentarios y cosas. Puedes incluir esta explicación en tu programa parapermitirle al usuario afinar su aplicación.

Hay varias directivas para cambiar los atributos de un control.

• fg - Cambia el color de frente de un control.

• bg - Cambia el color de fondo de un control.

• bg_pixmap - Cambia el fondo de un control para que sea un mosaico con el pixmap dado.

• font - Cambia la fuente que usará el control.

Además de esto, hay varios estados en los que un control puede estar, y puedes cambiar los diferentes colores,pixmaps y fuentes para cada estado. Los estados son:

NORMAL (Normal) El estado normal de un control, sin que el ratón esté encima de él, y sin que hayasido pulsado, etc.

PRELIGHT (Preiluminado) Cuando el ratón está encima del control, los colores definidos en este estadotendrán efecto.

ACTIVE (Activo) Cuando el control está presionado o se ha hecho clic en él estará activo y losatributos asignados con este valor tendrán efecto.

INSENSITIVE (Insensitivo) Cuando un control está insensitivo, y no puede ser activado, tendrá estos atribu-tos.

SELECTED (Seleccionado) Cuando un objeto está seleccionado, se usan estos atributos.

221

Page 230: Tutorial Pygtk

Capítulo 21. Ficheros rc de GTK

Cuando se usan las palabras "fg" y "bg" para cambiar los colores de los controles, el formato es:

fg[<STATE>] = { Rojo, Verde, Azul }

DondeSTATEes uno de los estados anteriores (PRELIGHT, ACTIVE, etc), y elRojo , Verde y Azul son valoresen el rango de 0 - 1.0, siendo { 1.0, 1.0, 1.0 } el blanco. Deben estar en formato float, o se les asignará 0, por tantoun simple "1" no funcionará, debe ser "1.0". Un simple "0" esta bien ya que da igual si no se reconoce porque losvalores no reconocidos se ponen a 0.

bg_pixmap es muy similar a lo anterior excepto que los colores se sustituyen por un nombre de fichero.

pixmap_path es una lista de caminos separados por ":" . Cuando especifiques un pixmap, se buscará en esta lista.

La directiva "font" es simple:

font = "<font name>"

Lo único dificil es saber la cadena de la fuente. El programaxfontselo una utilidad similar ayuda.

La directiva "widget_class" fija el estilo para una clase de controles. Estas clases se listan en la perspectiva generalde controles en la jerarquía de clases.

La directiva "widget" fija el estilo de un conjunto especifico de controles con un nombre, reemplazando cualquierestilo de la clase del control en cuestión. Estos controles se registran en la aplicación usando el métodoset_name () . Esto te permite cambiar los atributos de un control de una forma mucho más concreta, en vezde especificar los atributos de toda su clase. Te recomiendo que documentes todos estos controles especiales paraque los usuarios puedan personalizarlos.

Cuando se usa la palabraparent (padre) en un atributo, el control usará los atributos de su padre en la aplicación.

Al definir un estilo, puedes asiganr 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" simplemente cambiando la fuente y elcolor de fondo del estado preiluminado del estilo "button".

Por supuesto, muchos de estos atributos no funcionan con todos los controles. Solo es cuestión de sentido común.Todo lo que podría aplicarse, se debe aplicar.

21.3. Ejemplo de fichero rc

# pixmap_path "<dir 1>:<dir 2>:<dir 3>:..."#pixmap_path "/usr/include/X11R6/pixmaps:/home/imain/pixmaps"## style <name> [= <name>]

222

Page 231: Tutorial Pygtk

Capítulo 21. Ficheros rc de GTK

# {# <option># }## widget <widget_set> style <style_name># widget_class <widget_class_set> style <style_name>

# Esto crea un estilo llamado "window". El nombre no es importante ya que# se le asigna a los controles reales al final del fichero.

style "window"{

#Esto le pone el pixmap especificado como margén alrededor de la ventana.#bg_pixmap[<STATE>] = "<pixmap filename>"bg_pixmap[NORMAL] = "warning.xpm"

}

style "scale"{

#Pone el color de frente (el color de la fuente) a rojo en el estado#"NORMAL".

fg[NORMAL] = { 1.0, 0, 0 }

#Pone el pixmap de fondo de este control al mismo de su padre.bg_pixmap[NORMAL] = "<parent>"

}

style "button"{

# Esto muestra los posibles estados de un botón. El único que no tiene es# el estado SELECTED .

fg[PRELIGHT] = { 0, 1.0, 1.0 }bg[PRELIGHT] = { 0, 0, 1.0 }bg[ACTIVE] = { 1.0, 0, 0 }fg[ACTIVE] = { 0, 1.0, 0 }bg[NORMAL] = { 1.0, 1.0, 0 }fg[NORMAL] = { .99, 0, .99 }bg[INSENSITIVE] = { 1.0, 1.0, 1.0 }fg[INSENSITIVE] = { 1.0, 0, 1.0 }

}

# En este ejemplo, heredamos los atributos del estilo "button" y reemplazamos# el color de fondo y la fuente del estado PRELIGHT para crear el estilo# "main_button" .

style "main_button" = "button"{

font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"bg[PRELIGHT] = { 0.75, 0, 0 }

}

style "toggle_button" = "button"{

fg[NORMAL] = { 1.0, 0, 0 }fg[ACTIVE] = { 1.0, 0, 0 }

# Esto le pone el pixmap de fondo al botón biestado al que esté usando el

223

Page 232: Tutorial Pygtk

Capítulo 21. Ficheros rc de GTK

# padre (tal y como se defina en la aplicación)bg_pixmap[NORMAL] = "<parent>"

}

style "text"{

bg_pixmap[NORMAL] = "marble.xpm"fg[NORMAL] = { 1.0, 1.0, 1.0 }

}

style "ruler"{

font = "-adobe-helvetica-medium-r-normal--*-80-*-*-*-*-*-*"}

# pixmap_path "~/.pixmaps"

# Le decimos que estos tipos de controles usen los estilos definidos arriba.# Los tipos de controles se listan en la jerarquía de controles, pero# probablemente se podrían listar en este documento para referencia del usuario

widget_class "GtkWindow" style "window"widget_class "GtkDialog" style "window"widget_class "GtkFileSelection" style "window"widget_class "*Gtk*Scale" style "scale"widget_class "*GtkCheckButton*" style "toggle_button"widget_class "*GtkRadioButton*" style "toggle_button"widget_class "*GtkButton*" style "button"widget_class "*Ruler" style "ruler"widget_class "*GtkText" style "text"

# Le decimos que todos los botones que sean hijos de la ventana "main window"# tengan el estilo "main_button". Esto debe ser documentado para que se le# saque provecho.widget "main window.*GtkButton*" style "main_button"

224

Page 233: Tutorial Pygtk

Capítulo 22. Scribble, Un Ejemplo dePrograma de Dibujo Simple22.1. Perspectiva General de ScribbleEn esta sección, construiremos un programa de dibujo simple. En el proceso, examinaremos como manejar eventosde ratón, como dibujar en una ventana, y como dibujar mejor usando un pixmap de fondo.

Figura 22.1. Ejemplo de Programa de Dibujo Scribble

22.2. Manejo de EventosLas señales GTK que hemos explicado son para acciones de alto nivel, tal como seleccionar un elemento demenú. Sin embargo, a veces es útil aprender sobre situaciones de más bajo nivel, como cuando se mueve unratón o se pulsa una tecla. También hay señales para estos eventos de bajo nivel. Los manejadores de estasseñales tienen un parámetro extra que es un objetoGdkEvent que contiene información sobre el evento. Porejemplo, a los manejadores de eventos de movimiento se les pasa un objetoGdkEvent que contiene informaciónde EventMotion, la cual tiene atributos como:

type # tipowindow # ventanatime # tiempoxy

...state # estado

...

225

Page 234: Tutorial Pygtk

Capítulo 22. Scribble, Un Ejemplo dePrograma de Dibujo Simple

window es la ventana en la que ha ocurrido el evento.

x w y dan las coordenadas del evento.

type es el tipo de evento, en este caso MOTION_NOTIFY. Estos tipos (en el módulo gtk.gdk) son:

NOTHING código especial para indica un evento nulo.

DELETE el manejador de ventanas ha pedido que se oculte o destruya la ←↩ventana de más alto nivel, normalemente cuando el usuario hace clic en un icono ←↩especial de la barra de título.

DESTROY la ventana ha sido destruida.

EXPOSE todo o parte de la ventana se ha hecho visible y necesita ←↩redibujarse.

MOTION_NOTIFY el puntero (normalmente el ratón) se ha movido.

BUTTON_PRESS se ha presionado un botón del ratón

_2BUTTON_PRESS se ha hecho doble clic en un botón del ratón. Fíjate quecada clic también genera un evento BUTTON_PRESS.

_3BUTTON_PRESS se ha hecho clic 3 veces seguidas dentro de un corto período de ←↩tiempo en un botón del ratón. Fíjate que cada clic también genera un evento ←↩BUTTON_PRESS.

BUTTON_RELEASE se ha soltado un botón del ratón.

KEY_PRESS se ha pulsado una tecla.

KEY_RELEASE se ha soltado una tecla.

ENTER_NOTIFY el puntero ha entrado en la ventana.

LEAVE_NOTIFY el puntero ha salido de la ventana.

FOCUS_CHANGE el foco del teclado ha entrado o dejado la ventana.

CONFIGURE el tamaño, posición u orden de apilamiento de la ventana ha ←↩cambiado. Fíjate que GTK no usa estos eventos para ventanas hijas (GDK_WINDOW_CHILD).

MAP se han reservado recursos para la ventana.

UNMAP se han liberado recursos para la ventana.

PROPERTY_NOTIFY se ha borrado o cambiado una propiedad de la ventana.

SELECTION_CLEAR la aplicación ha perdido la propiedad de una selección.

SELECTION_REQUEST otra aplicación ha solicitado la selección.

SELECTION_NOTIFY se ha recibido una selección.

PROXIMITY_IN se ha hecho contacto en una superficie sensible de un dispositivo ←↩de entrada (por ejemplo, una tableta gráfica o pantalla sensible al tacto).

226

Page 235: Tutorial Pygtk

Capítulo 22. Scribble, Un Ejemplo dePrograma de Dibujo Simple

PROXIMITY_OUT se ha perdido el contacto de una superfice sensible.

DRAG_ENTER el ratón ha entrado en la ventana mientras se estaba arrastrando ←↩algo.

DRAG_LEAVE el ratón ha salido de la ventana mientras se estaba arrastrando ←↩algo.

DRAG_MOTION el ratón se ha movido por la ventana mientras se estaba ←↩arrastrando algo.

DRAG_STATUS el estado de la operación de arrastre iniciada por la ventana ha ←↩cambiado.

DROP_START se ha iniciado una operación de soltar en la ventana.

DROP_FINISHED la operación de soltar iniciada por la ventana ha terminado.

CLIENT_EVENT a message has been received from another application.

VISIBILITY_NOTIFY la visibilidad de la ventana ha cambiado.

NO_EXPOSE indica que la región orígen estaba completamente disponible ←↩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 ybotones del ratón estaban presionados). Es una combinación con el operador OR de algunoas de las siguientesconstantes (en el módulo gtk.gdk):

SHIFT_MASK # máscara de mayúsculasLOCK_MASK # máscara de bloqueoCONTROL_MASK # máscara de controlMOD1_MASK # máscara del modificador 1MOD2_MASK # máscara del modificador 2MOD3_MASK # máscara del modificador 3MOD4_MASK # máscara del modificador 4MOD5_MASK # máscara del modificador 5BUTTON1_MASK # máscara del botón 1BUTTON2_MASK # máscara del botón 2BUTTON3_MASK # máscara del botón 3BUTTON4_MASK # máscara del botón 4BUTTON5_MASK # máscara del botón 5

Como con las demás señales, para determinar qué pasa cuando ocurre un evento, llamamos al métodoconnect (). Pero también tenemos que hacerle saber a GTK qué eventos queremos tratar. Para eso, llamamos al método:

widget.set_events(events)

227

Page 236: Tutorial Pygtk

Capítulo 22. Scribble, Un Ejemplo dePrograma de Dibujo Simple

El argumentoevents especifica los eventos en los que estamos interesados. Es una combinación con el operadorOR de constantes que especifican diferentes tipos de eventos. Los tipos de eventos (en el módulo gtk.gdk) son:

EXPOSURE_MASKPOINTER_MOTION_MASKPOINTER_MOTION_HINT_MASKBUTTON_MOTION_MASKBUTTON1_MOTION_MASKBUTTON2_MOTION_MASKBUTTON3_MOTION_MASKBUTTON_PRESS_MASKBUTTON_RELEASE_MASKKEY_PRESS_MASKKEY_RELEASE_MASKENTER_NOTIFY_MASKLEAVE_NOTIFY_MASKFOCUS_CHANGE_MASKSTRUCTURE_MASKPROPERTY_CHANGE_MASKVISIBILITY_NOTIFY_MASKPROXIMITY_IN_MASKPROXIMITY_OUT_MASKSUBSTRUCTURE_MASK

Hay unas cuantas cosas que hay que tener en cuenta al llamar al métodoset_events () . Lo primero es que tieneque llamarse antes de que se cree la ventana X para el control PyGTK. En la práctica, esto significa que tienes quellamarlo inmediatamente después de crear el control. Lo segundo es que el control tiene que tener una ventana Xasociada. Por eficiencia, la mayoria de los controles no tienen su propia ventana si no que se dibujan en la ventanapadre. Estos controles incluyen a:

gtk.Alignmentgtk.Arrowgtk.Bingtk.Boxgtk.Imagegtk.Itemgtk.Labelgtk.Layoutgtk.Pixmapgtk.ScrolledWindowgtk.Separatorgtk.Tablegtk.AspectFramegtk.Framegtk.VBoxgtk.HBoxgtk.VSeparatorgtk.HSeparator

Para capturar eventos en estos controles, tienes que usar un controlEventBox . Mira la sección del controlEventBox para más detalles.

Los atributos de los eventos que PyGTK usa para cada tipo de evento son:

228

Page 237: Tutorial Pygtk

Capítulo 22. Scribble, Un Ejemplo dePrograma de Dibujo Simple

todos los eventos type # tipowindow # ventanasend_event # evento enviado

NOTHINGDELETEDESTROY # sin atributos adicionales

EXPOSE area # áreacount # cuenta

MOTION_NOTIFY time # tiempox # xy # ypressure # presiónxtiltytiltstate # estadois_hint # es pistasource # fuentedeviceid # identificador de dispositivox_root # x raizy_root # y raiz

BUTTON_PRESS_2BUTTON_PRESS_3BUTTON_PRESSBUTTON_RELEASE time # tiempo

x # xy # ypressure # presiónxtiltytiltstate # estadobutton # botónsource # fuentedeviceid # identificador de dispositivox_root # x raizy_root # y raiz

KEY_PRESSKEY_RELEASE time # tiempo

state # estadokeyval # valor de teclastring # cadena de caracteres

ENTER_NOTIFYLEAVE_NOTIFY subwindow # subventana

time # tiempox # xy # yx_root # x raizy_root # y raizmode # mododetail # detallefocus # focostate # estado

229

Page 238: Tutorial Pygtk

Capítulo 22. Scribble, Un Ejemplo dePrograma de Dibujo Simple

FOCUS_CHANGE _in # dentro

CONFIGURE x # xy # ywidth # anchoheight # alto

MAPUNMAP # sin atributos adicionales

PROPERTY_NOTIFY atom # átomotime # tiempostate # estado

SELECTION_CLEARSELECTION_REQUESTSELECTION_NOTIFY selection # selección

target # objetivoproperty # propiedadrequestor # solicitantetime # tiempo

PROXIMITY_INPROXIMITY_OUT time # tiempo

source # orígendeviceid # identificador de dispositivo

DRAG_ENTERDRAG_LEAVEDRAG_MOTIONDRAG_STATUSDROP_STARTDROP_FINISHED context # contexto

time # tiempox_root # x raizy_root # y raiz

CLIENT_EVENT message_type # tipo de mensajedata_format # formato de los datosdata # datos

VISIBILTY_NOTIFY state # estado

NO_EXPOSE # sin atributos adicionales

22.2.1. Scribble - Manejo de Eventos

Para nuestro programa de dibujo, queremos saber cuándo se pulsa el botón del ratón y cuando se mueve, portanto especificamos POINTER_MOTION_MASK y BUTTON_PRESS_MASK. También queremos saber cuándotenemos que redibujar la ventana, por tanto especificamos EXPOSURE_MASK. Aunque queremos que nosavisen con un evento de configuración cuando el tamaño de la ventana cambie, no tenemos que especificar elcorrespondiente STRUCTURE_MASK, porque eso se hace automáticamente para todas las ventanas.

Sin embargo, hay un problema si sólo se especifica POINTER_MOTION_MASK. El problema es que esto haceque el servidor añada un nuevo evento de moviento a la cola de eventos cada vez que el usuario mueve el ratón.Imagina que tardamos 0.1 segundos en tratar un evento de movimiento, pero el servidor X encola un nuevoevento de movimiento cada 0.05 segundos. Pronto nos encontraremos muy por detrás de lo que el usuario está

230

Page 239: Tutorial Pygtk

Capítulo 22. Scribble, Un Ejemplo dePrograma de Dibujo Simple

dibujando. Si el usuario está dibujando durante 5 segundos, ¡tardaremos otros 5 segundos en recuperarnos despuésde que suelte el botón del ratón! Lo que podemos hacer es tratar un evento de movimiento por cada evento queprocesemos. La forma de hacer esto es especificar POINTER_MOTION_HINT_MASK.

Cuando especificamos POINTER_MOTION_HINT_MASK, el servidor nos manda un evento de movimiento laprimera vez que el puntero se mueve después de entrar en la ventana, o después de que se pulse o suelte el botón.Los movimientos posteriores se suprimiran hasta que los pidamos explícitamente usando el siguiente método deGdkWindow :

x, y, mask = window.get_pointer()

window es un objetogtk.gdk.Window . x e y son las coordenadas del puntero ymask es la máscara demodificación para detectar qué teclas están pulsadas. (Hay un método degtk.Widget , get_pointer () quedevuelve la misma información que el métodogtk.gdk.Window.get_pointer () pero sin la información de lamáscara).

El programa de ejemploscribblesimple.py [examples/scribblesimple.py] demuestra el uso básico de eventos ymanejadores de eventos. La figura Figura 22.2. Ejemplo simple de Scribble muestra el programa en acción:

Figura 22.2. Ejemplo simple de Scribble

Los manejadores de eventos se conectan a el drawing_area (área de dibujo) en las siguientes lineas:

90 # Signals used to handle backing pixmap91 drawing_area.connect("expose_event", expose_event)92 drawing_area.connect("configure_event", configure_event)9394 # Event signals95 drawing_area.connect("motion_notify_event", motion_notify_event)96 drawing_area.connect("button_press_event", button_press_event)97

231

Page 240: Tutorial Pygtk

Capítulo 22. Scribble, Un Ejemplo dePrograma de Dibujo Simple

98 drawing_area.set_events(gtk.gdk.EXPOSURE_MASK99 | gtk.gdk.LEAVE_NOTIFY_MASK

100 | gtk.gdk.BUTTON_PRESS_MASK101 | gtk.gdk.POINTER_MOTION_MASK102 | gtk.gdk.POINTER_MOTION_HINT_MASK)

Los manejadores de eventosbutton_press_event () y motion_notify_event () en scribblesimple.py [ex-amples/scribblesimple.py] son:

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.TRUE5960 def motion_notify_event(widget, event):61 if event.is_hint:62 x, y, state = event.window.get_pointer()63 else:64 x = event.x65 y = event.y66 state = event.state6768 if state & gtk.gdk.BUTTON1_MASK and pixmap != None:69 draw_brush(widget, x, y)7071 return gtk.TRUE

Los manejadoresexpose_event () y configure_event () se describirán después.

22.3. El Control del Área de Dibujo, y DibujarAhora nos toca dibujar en la pantalla. El control que usamos para eso es laDrawingArea (Área de dibujo). Uncontrol de área de dibujo es esencialmente una ventana X y nada más. Es un lienzo en blanco en el que podemospintar lo que queramos. Un área de dibujo se crea usando la llamada:

darea = gtk.DrawingArea()

Se puede especificar un tamaño predeterminado par ael control usando:

darea.set_size_request( width , height )

Este tamaño predeterminado se puede cambiar, como en todos los controles, llamando al métodoset_size_request () , y esto también se puede modificar si el usuario cambia el tamaño de la ventanaque contiene el área de dibujo manualmente.

Hay que decir que cuando creamos un controlDrawingArea , tenemos toda la responsabilidad de dibujar sucontenido. Si nuestra ventana se tapa y luego se muestra, recibimos un evento de exposición y debemos redibujarlo que antes se oculto.

Tener que recordar lo que había dibujado en la pantalla para que podamos redibujarlo es, cuanto menos, un latazo.Además, puede distraer visualmente el hecho de que partes de la ventana se limpien, y luego se dibujen paso

232

Page 241: Tutorial Pygtk

Capítulo 22. Scribble, Un Ejemplo dePrograma de Dibujo Simple

a paso. La solución a este problema es usar un pixmap oculto. En vez de dibujar directamente en la pantalla,dibujamos en la imagen almacenada en la memoria del servidor que no se muestra, y luego copiamos las partesrelevantes a la pantalla.

Para crear un pixmap fuera de pantalla, usamos esta función:

pixmap = gtk.gdk.Pixmap( window , width , height , depth =-1)

El parámetrowindow especifica una ventana GDK de la que este pixmap tomará algunas propiedades.width

(ancho) yheight (alto) especifican el tamaño delpixmap . depth especifica la profundidad del color, es decir elnúmero de bits por píxel, de la nueva ventana. Sidepth es -1 o se omite, será la profundidad de la ventana.

Creamos el pixmap en nuestro manejador del evento "configure_event". Este evento se genera cada vez que laventana cambia de tamaño, incluyendo cuando se crea por primera vez.

30 # Create a new backing pixmap of the appropriate size31 def configure_event(widget, event):32 global pixmap3334 x, y, width, height = widget.get_allocation()35 pixmap = gtk.gdk.Pixmap(widget.get_window(), width, height)36 gtk.draw_rectangle(pixmap, widget.get_style().white_gc,37 gtk.TRUE, 0, 0, width, height)3839 return gtk.TRUE

La llamada adraw_rectangle () limpia el pixmap con blanco inicialmente. Comentaremos esto un poco másdentro de un momento.

Nuestro manejador del evento de exposición simplemente copia las partes correspondientes del pixmap en el áreade dibujo usando el métododraw_pixmap () . (Sabemos el área que tenemos que redibujar usando el atributoevent.area attribute del evento de exposición):

41 # Redraw the screen from the backing pixmap42 def expose_event(widget, event):43 x , y, width, height = event.area44 widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL],45 pixmap, x, y, x, y, width, height)46 return gtk.FALSE

Ya hemos visto cómo manetener la pantalla sincronizada con nuestro pixmap, pero ¿cómo pintamos cosasinteresantes en nuestro pixmap? Hay un gran número de llamadas en PyGTK para dibujar en objetos dibujables.Un objeto dibujable es simplemente algo en lo que puedes dibujar. Puede ser una ventana, un pixmap, o unbitmap (una imagen en blanco y negro). Ya hemos visto dos de esas llamadas antes,draw_rectangle () ydraw_pixmap (). La lista completa es:

# dibuja puntodrawable.draw_point( gc , x , y)

# dibuja lineadrawable.draw_line( gc , x1 , y1 , x2 , y2 )

233

Page 242: Tutorial Pygtk

Capítulo 22. Scribble, Un Ejemplo dePrograma de Dibujo Simple

# dibuja rectángulodrawable.draw_rectangle( gc , fill , x , y , width , height )

# dibuja arcodrawable.draw_arc( gc , fill , x , y , width , height , angle1 , angle2 )

# dibuja polígonodrawable.draw_polygon( gc , fill , points )

# dibuja dibujabledrawable.draw_drawable( gc , src , xsrc , ysrc , xdest , ydest , width , height )

# dibuja puntosdrawable.draw_points( gc , points )

# dibuja lineasdrawable.draw_lines( gc , points )

# dibuja segmentosdrawable.draw_segments( gc , segments )

# dibuja imagen rgbdrawable.draw_rgb_image( gc , x , y , width , height , dither , buffer , rowstride )

# dibuja imagen rgb 32drawable.draw_rgb_32_image( gc , x , y , width , height , dither , buffer , rowstride )

# dibuja imagen grisdrawable.draw_gray_image( gc , x , y , width , height , dither , buffer , rowstride )

Los métodos del área de dibujo son los mismos que los métodos de dibujos de los objetos dibujables por loque puedes consultar la sección Métodos de Dibujo para más detalles de estas funciones. Todas estas funcionescomparten los primeros argumentos. El primer argumento es un contexto gráfico (gc ).

Un contexto gráfico encapsula información sobre cosas como los colores de fondo y frente y el ancho de linea.PyGTk tiene un conjunto completo de funciones para crear y modificar contextos gráficos, pero para dejar lascosas fáciles simplemente usaremos contextos gráficos predefinidos. Mira la sección Contexto Gráfico , para másinformación sobre contextos gráficos. Cada control tiene un estilo asociado (que puede modificarse en un ficherogtkrc , mira la sección Ficheros rc de GTK.) Esto, entre otras cosas, almacena unos cuantos contextos gráficos.Algunos ejemplos para acceder a estos contextos gráficos 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 camposfg_gc , bg_gc , dark_gc , y light_gc se indexan con un parámetro que puede tomar los siguientesvalores:

STATE_NORMAL, # El estado durante la operación normal

234

Page 243: Tutorial Pygtk

Capítulo 22. Scribble, Un Ejemplo dePrograma de Dibujo Simple

STATE_ACTIVE, # El control está activado, como cuando se pulsa un botónSTATE_PRELIGHT, # El puntero del ratón está sobre el controlSTATE_SELECTED, # El control está seleccionadoSTATE_INSENSITIVE # El control está desactivado

Por ejemplo, para STATE_SELECTED el color de frente predeterminado es blanco y el el color de fondopredeterminado es, azul oscuro.

Nuestra funcióndraw_brush () (pinta trazo), que es la que realmente dibuja en el pixmap, es la siguiente:

48 # Draw a rectangle on the screen49 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 apply(widget.queue_draw_area, rect)

Después de dibujar el rectángulo que representa el trazo en el pixmap llamamos a la función:

widget.queue_draw_area( x , y , width , height )

que notifica a X que ese área tiene que sera actualizada. En algún momento X generará un evento de exposición(posiblemente combinando las áreas que se le pasan en varias llamadas adraw ()) lo que hará qeu se llame anuestro manejador del evento de exposición para copiar las partes relevantes en la pantalla.

Ya hemos tratado el programa de dibujo completamente excepto por algunos detalles mundanos como crear laventana principal.

235

Page 244: Tutorial Pygtk

Capítulo 23. Trucos para EscribirAplicaciones PyGTKEsta sección es simplemente una recolección de conocimientos, guías de estilo generales y trucos para crearbuenas aplicaciones PyGTK. Actualmente esta sección es muy corta, pero espero que se vaya haciendo más largaen futuras ediciones de este tutorial.

236

Page 245: Tutorial Pygtk

Capítulo 24. ContribuirEste documento, al igual que mucho software genial existente, fue creado gratuitamente por voluntarios. Si tienesalgún conocimiento sobre algún aspecto de PyGTK que este documento no trate, por favor considera el contribuira este documento.

Si decides contribuir, por favor envia tu texto por correo electrónico a John Finlay ([email protected][mailto:[email protected]]). Además, ten en cuenta que este documento en su conjunto es libre, y cualquieradición que proporciones también debe ser libre. Esto es, la gente puede usar cualquier parte de tus ejemplos ensus programas, y se pueden distribuir copias de este documento.

Gracias.

Nota del traductor:Para cualquier contribución relacionada con la traducción de este documento, por favor escribeun correo a [email protected] [mailto:[email protected]].

237

Page 246: Tutorial Pygtk

Apéndice A. Señales GTKComo PyGTk es un conjunto de controles orientado a objetos, tiene una jerarquía de herencia. Este mecanismo deherencia se aplica a las señales. Por tanto, debes utilizar la jerarquía de controles cuando uses las señales listadasen esta sección.

A.1. GtkObject

destroy(object, data)

A.2. GtkWidget

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)

size-request(widget, requisition, data)

size-allocate(widget, allocation, data)

state-changed(widget, state, data)

parent-set(widget, object, data)

style-set(widget, style, data)

add-accelerator(widget, accel_signal_id, accel_group, accel_key, accel_mods,accel_flags, data)

remove-accelerator(widget, accel_group, accel_key, accel_mods, data)

bool = event(widget, event, data)

bool = button-press-event(widget, event, data)

bool = button-release-event(widget, event, data)

bool = motion-notify-event(widget, event, data)

bool = delete-event(widget, event, data)

238

Page 247: Tutorial Pygtk

Apéndice A. Señales GTK

bool = destroy-event(widget, event, data)

bool = expose-event(widget, event, data)

bool = key-press-event(widget, event, data)

bool = key-release-event(widget, event, data)

bool = enter-notify-event(widget, event, data)

bool = leave-notify-event(widget, event, data)

bool = configure-event(widget, event, data)

bool = focus-in-event(widget, event, data)

bool = focus-out-event(widget, event, data)

bool = map-event(widget, event, data)

bool = unmap-event(widget, event, data)

bool = property-notify-event(widget, event, data)

bool = selection-clear-event(widget, event, data)

bool = selection-request-event(widget, event, data)

bool = selection-notify-event(widget, event, data)

selection-get(widget, selection_data, info, time, data)

selection-received(widget, selection_data, time, data)

bool = proximity-in-event(widget, event, data)

bool = proximity-out-event(widget, event, data)

drag-begin(widget, context, data)

drag-end(widget, context, data)

drag-data-delete(widget, context, data)

drag-leave(widget, context, time, data)

bool = drag-motion(widget, context, x, y, time, data)

bool = drag-drop(widget, context, x, y, time, data)

drag-data-get(widget, context, selection_data, info, time, data)

drag-data-received(widget, context, info, time, selection_data,info, time, data)

bool = client-event(widget, event, data)

bool = no-expose-event(widget, event, data)

239

Page 248: Tutorial Pygtk

Apéndice A. Señales GTK

bool = visibility-notify-event(widget, event, data)

debug-msg(widget, string, data)

A.3. GtkData

disconnect(data_obj, data)

A.4. GtkContainer

add(container, widget, data)

remove(container, widget, data)

check-resize(container, data)

direction = focus(container, direction, data)

set-focus-child(container, widget, data)

A.5. GtkCalendar

month-changed(calendar, data)

day-selected(calendar, data)

day-selected-double-click(calendar, data)

prev-month(calendar, data)

next-month(calendar, data)

prev-year(calendar, data)

next-year(calendar, data)

A.6. GtkEditable

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)

240

Page 249: Tutorial Pygtk

Apéndice A. Señales GTK

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

switch-page(noteboook, page, page_num, data)

A.8. GtkList

selection-changed(list, data)

select-child(list, widget, data)

unselect-child(list, widget, data)

A.9. GtkMenuShell

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)

A.10. GtkToolbar

orientation-changed(toolbar, orientation, data)

241

Page 250: Tutorial Pygtk

Apéndice A. Señales GTK

style-changed(toolbar, toolbar_style, data)

A.11. GtkButton

pressed(button, data)

released(button, data)

clicked(button, data)

enter(button, data)

leave(button, data)

A.12. GtkItem

select(item, data)

deselect(item, data)

toggle(item, data)

A.13. GtkWindow

set-focus(window, widget, data)

A.14. GtkHandleBox

child-attached(handle_box, widget, data)

child-detached(handle_box, widget, data)

A.15. GtkToggleButton

toggled(toggle_button, data)

A.16. GtkMenuItem

activate(menu_item, data)

activate-item(menu_item, data)

242

Page 251: Tutorial Pygtk

Apéndice A. Señales GTK

A.17. GtkCheckMenuItem

toggled(check_menu_item, data)

A.18. GtkInputDialog

enable-device(input_dialog, deviceid, data)

disable-device(input_dialog, deviceid, data)

A.19. GtkColorSelection

color-changed(color_selection, data)

A.20. GtkStatusBar

text-pushed(statusbar, context_id, text, data)

text-popped(statusbar, context_id, text, data)

A.21. GtkCurve

curve-type-changed(curve, data)

A.22. GtkAdjustment

changed(adjustment, data)

value-changed(adjustment, data)

243

Page 252: Tutorial Pygtk

Apéndice B. Ejemplos de CódigoB.1. scribblesimple.py

1 #!/usr/bin/env python23 # example scribblesimple.py45 # GTK - The GIMP Toolkit6 # Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald7 # Copyright (C) 2001-2002 John Finlay8 #9 # This library is free software; you can redistribute it and/or

10 # modify it under the terms of the GNU Library General Public11 # License as published by the Free Software Foundation; either12 # 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 of16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU17 # Library General Public License for more details.18 #19 # You should have received a copy of the GNU Library General Public20 # License along with this library; if not, write to the21 # Free Software Foundation, Inc., 59 Temple Place - Suite 330,22 # Boston, MA 02111-1307, USA.232425 import gtk2627 # Backing pixmap for drawing area28 pixmap = None2930 # Create a new backing pixmap of the appropriate size31 def configure_event(widget, event):32 global pixmap3334 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)3839 return gtk.TRUE4041 # Redraw the screen from the backing pixmap42 def expose_event(widget, event):43 x , y, width, height = event.area44 widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL],45 pixmap, x, y, x, y, width, height)46 return gtk.FALSE4748 # Draw a rectangle on the screen49 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])

244

Page 253: Tutorial Pygtk

Apéndice B. Ejemplos de Código

5455 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.TRUE5960 def motion_notify_event(widget, event):61 if event.is_hint:62 x, y, state = event.window.get_pointer()63 else:64 x = event.x65 y = event.y66 state = event.state6768 if state & gtk.gdk.BUTTON1_MASK and pixmap != None:69 draw_brush(widget, x, y)7071 return gtk.TRUE7273 def main():74 window = gtk.Window(gtk.WINDOW_TOPLEVEL)75 window.set_name ("Test Input")7677 vbox = gtk.VBox(gtk.FALSE, 0)78 window.add(vbox)79 vbox.show()8081 window.connect("destroy", gtk.mainquit)8283 # Create the drawing area84 drawing_area = gtk.DrawingArea()85 drawing_area.set_size_request(200, 200)86 vbox.pack_start(drawing_area, gtk.TRUE, gtk.TRUE, 0)8788 drawing_area.show()8990 # Signals used to handle backing pixmap91 drawing_area.connect("expose_event", expose_event)92 drawing_area.connect("configure_event", configure_event)9394 # Event signals95 drawing_area.connect("motion_notify_event", motion_notify_event)96 drawing_area.connect("button_press_event", button_press_event)9798 drawing_area.set_events(gtk.gdk.EXPOSURE_MASK99 | gtk.gdk.LEAVE_NOTIFY_MASK

100 | gtk.gdk.BUTTON_PRESS_MASK101 | gtk.gdk.POINTER_MOTION_MASK102 | gtk.gdk.POINTER_MOTION_HINT_MASK)103104 # .. And a quit button105 button = gtk.Button("Quit")106 vbox.pack_start(button, gtk.FALSE, gtk.FALSE, 0)107108 button.connect_object("clicked", lambda w: w.destroy(), window)109 button.show()110111 window.show()112

245

Page 254: Tutorial Pygtk

Apéndice B. Ejemplos de Código

113 gtk.main()114115 return 0116117 if __name__ == "__main__":118 main()

246