Las señales GTK+ que hemos explicado sirven para acciones de alto nivel,
tal como la selección de un elemento de menú. Sin embargo, a veces es útil tener información
sobre eventos de más bajo nivel, como cuándo se mueve un ratón o se pulsa
una tecla. También hay señales para estos eventos de bajo nivel. Los
manejadores de estas señales tienen un parámetro adicional, que es un objeto
gtk.gdk.Event, y contiene información sobre el evento. Por
ejemplo, a los manejadores de eventos de movimiento se les pasa un objeto
gtk.gdk.Event que contiene información de EventMotion, que (parcialmente)
contiene atributos como:
type # tipo
window # ventana
time # tiempo
x
y
...
state # estado
...
window es la ventana en la que ha ocurrido el
evento.
x e y proporcionan las coordenadas del
evento.
type informa del tipo de evento, en este caso
MOTION_NOTIFY. Estos tipos (incluidos en el módulo gtk.gdk) son:
NOTHING código especial para indicar un evento nulo.
DELETE el manejador de ventanas ha pedido que se oculte o destruya la ventana de más alto nivel, normalmente cuando el usuario hace clic en un icono especial de la barra de título.
DESTROY la ventana ha sido destruida.
EXPOSE toda 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 (2 veces dentro de un corto periodo de tiempo). Nótese que cada clic genera también un evento BUTTON_PRESS.
_3BUTTON_PRESS se ha hecho clic 3 veces seguidas dentro de un corto periodo de tiempo en un botón del ratón. Nótese que cada clic genera también 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. Obsérvese que GTK+ no usa estos eventos en 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 una 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).
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 se ha recibido un mensaje desde otra aplicación.
VISIBILITY_NOTIFY la visibilidad de la ventana ha cambiado.
NO_EXPOSE indica que la región fuente estaba disponible completamente cuando partes de un dibujable fueron copiadas. No es muy útil.
SCROLL ?
WINDOW_STATE ?
SETTING ?
state especifica el modificador de estado cuando
ocurre el evento (es decir, especifica que teclas auxiliares y botones del
ratón estaban presionados). Es una combinación con el operador OR de algunas
de las siguientes constantes (incluidas en el módulo gtk.gdk):
SHIFT_MASK # máscara de mayúsculas
LOCK_MASK # máscara de bloqueo
CONTROL_MASK # máscara de control
MOD1_MASK # máscara del modificador 1
MOD2_MASK # máscara del modificador 2
MOD3_MASK # máscara del modificador 3
MOD4_MASK # máscara del modificador 4
MOD5_MASK # máscara del modificador 5
BUTTON1_MASK # máscara del botón 1
BUTTON2_MASK # máscara del botón 2
BUTTON3_MASK # máscara del botón 3
BUTTON4_MASK # máscara del botón 4
BUTTON5_MASK # máscara del botón 5
Como con las demás señales, para determinar qué sucede cuando ocurre un
evento se llama al método connect(). Pero también
es necesario indicar a GTK+ qué eventos queremos tratar. Para ello llamamos
al método:
widget.set_events(events)
El argumento events especifica los eventos en los
que se está interesado. Es una combinación con el operador OR de
constantes que especifican los distintos tipos de eventos. Los tipos de eventos (del módulo
gtk.gdk) son:
EXPOSURE_MASK
POINTER_MOTION_MASK
POINTER_MOTION_HINT_MASK
BUTTON_MOTION_MASK
BUTTON1_MOTION_MASK
BUTTON2_MOTION_MASK
BUTTON3_MOTION_MASK
BUTTON_PRESS_MASK
BUTTON_RELEASE_MASK
KEY_PRESS_MASK
KEY_RELEASE_MASK
ENTER_NOTIFY_MASK
LEAVE_NOTIFY_MASK
FOCUS_CHANGE_MASK
STRUCTURE_MASK
PROPERTY_CHANGE_MASK
VISIBILITY_NOTIFY_MASK
PROXIMITY_IN_MASK
PROXIMITY_OUT_MASK
SUBSTRUCTURE_MASK
Hay un par de cuestiones que es necesario tener en cuenta al llamar al
método set_events() . En primer lugar, que debe ser llamado
antes de que se cree la ventana X del control PyGTK (en la práctica esto
significa que es preciso llamarlo inmediatamente después de crear el control).
En segundo lugar, que el control debe tener una ventana X asociada. Por
eficiencia, la mayoria de los controles no tienen su propia ventana sino que
se dibujan en la ventana del control padre. Entre estos controles se incluyen:
gtk.Alignment
gtk.Arrow
gtk.Bin
gtk.Box
gtk.Image
gtk.Item
gtk.Label
gtk.Layout
gtk.Pixmap
gtk.ScrolledWindow
gtk.Separator
gtk.Table
gtk.AspectFrame
gtk.Frame
gtk.VBox
gtk.HBox
gtk.VSeparator
gtk.HSeparator
Para capturar eventos en estos controles se debe usar un control
EventBox. Véase la
sección del control EventBox para más
detalles.
Los atributos de los eventos que PyGTK usa para cada tipo de evento son:
todos los eventos type # tipo
window # ventana
send_event # evento enviado
NOTHING
DELETE
DESTROY # sin atributos adicionales
EXPOSE area # área
count # cuenta
MOTION_NOTIFY time # tiempo
x # x
y # y
pressure # presión
xtilt # inclinación x
ytilt # inclinación y
state # estado
is_hint # es pista
source # fuente
deviceid # identificador de dispositivo
x_root # x raíz
y_root # y raíz
BUTTON_PRESS
_2BUTTON_PRESS
_3BUTTON_PRESS
BUTTON_RELEASE time # tiempo
x # x
y # y
pressure # presión
xtilt # inclinación x
ytilt # inclinación y
state # estado
button # botón
source # fuente
deviceid # identificador de dispositivo
x_root # x raíz
y_root # y raíz
KEY_PRESS
KEY_RELEASE time # tiempo
state # estado
keyval # valor de tecla
string # cadena de caracteres
ENTER_NOTIFY
LEAVE_NOTIFY subwindow # subventana
time # tiempo
x # x
y # y
x_root # x raíz
y_root # y raíz
mode # modo
detail # detalle
focus # foco
state # estado
FOCUS_CHANGE _in # dentro
CONFIGURE x # x
y # y
width # ancho
height # alto
MAP
UNMAP # sin atributos adicionales
PROPERTY_NOTIFY atom # átomo
time # tiempo
state # estado
SELECTION_CLEAR
SELECTION_REQUEST
SELECTION_NOTIFY selection # selección
target # objetivo
property # propiedad
requestor # solicitante
time # tiempo
PROXIMITY_IN
PROXIMITY_OUT time # tiempo
source # fuente
deviceid # identificador de dispositivo
DRAG_ENTER
DRAG_LEAVE
DRAG_MOTION
DRAG_STATUS
DROP_START
DROP_FINISHED context # contexto
time # tiempo
x_root # x raíz
y_root # y raíz
CLIENT_EVENT message_type # tipo de mensaje
data_format # formato de los datos
data # datos
VISIBILTY_NOTIFY state # estado
NO_EXPOSE # sin atributos adicionales
En nuestro programa de dibujo querremos saber en qué momento se pulsa el
botón del ratón y cuándo se mueve. Por tanto especificamos
POINTER_MOTION_MASK y BUTTON_PRESS_MASK.
También querremos saber cuándo hay que redibujar la ventana, y por ello especificamos
EXPOSURE_MASK. Aunque deseamos que se nos notifique
con un evento de configuración cuando el tamaño de la ventana cambie, no
es necesario especificar el correspondiente STRUCTURE_MASK,
puesto que se hace automáticamente para todas las ventanas.
Sin embargo, resulta problemático especificar solamente
POINTER_MOTION_MASK, puesto que haría que el servidor añadiese un nuevo
evento de moviento a la cola de eventos cada vez que el usuario mueve el
ratón. Imaginemos que se tardan 0.1 segundos en tratar un evento de movimiento,
pero el servidor X encola un nuevo evento de movimiento cada 0.05 segundos.
Pronto nos encontraremos muy por detrás de lo que el usuario está dibujando. Si
el usuario está dibujando durante 5 segundos, ¡se tardarían otros 5 segundos en
recuperarse después de que suelte el botón del ratón!. Lo que se puede hacer
es tratar un evento de movimiento por cada evento que procesemos. Para hacer esto
debemos especificar POINTER_MOTION_HINT_MASK.
Cuando se especifica POINTER_MOTION_HINT_MASK, el servidor
manda un evento de movimiento la primera vez que el puntero se mueve tras
entrar en la ventana o después de que se pulse o suelte el botón. Los
movimientos posteriores se suprimen hasta que se solicite explícitamente la posición del puntero
con el siguiente método de gtk.gdk.Window:
x, y, mask = window.get_pointer()
window es un objeto
gtk.gdk.Window. x e
y son las coordenadas del puntero y
mask es la máscara de modificación para detectar qué teclas
están pulsadas. (Hay un método de gtk.Widget,
get_pointer(), que devuelve la misma información que el
método gtk.gdk.Window.get_pointer() pero sin la
información de máscara).
El programa de ejemplo scribblesimple.py demuestra el uso básico de eventos y manejadores de eventos. La figura Figura 24.2, “Ejemplo sencillo - Scribble” muestra el programa en acción:
Los manejadores de eventos se conectan a la drawing_area (área de dibujo) en las siguientes líneas:
92 # Señales para gestionar el mapa de píxeles de respaldo
93 drawing_area.connect("expose_event", expose_event)
94 drawing_area.connect("configure_event", configure_event)
95
96 # señales de evento
97 drawing_area.connect("motion_notify_event", motion_notify_event)
98 drawing_area.connect("button_press_event", button_press_event)
99
100 drawing_area.set_events(gtk.gdk.EXPOSURE_MASK
101 | gtk.gdk.LEAVE_NOTIFY_MASK
102 | gtk.gdk.BUTTON_PRESS_MASK
103 | gtk.gdk.POINTER_MOTION_MASK
104 | gtk.gdk.POINTER_MOTION_HINT_MASK)
Los manejadores de eventos button_press_event()
y motion_notify_event() en scribblesimple.py
son:
57 def button_press_event(widget, event):
58 if event.button == 1 and pixmap != None:
59 draw_brush(widget, event.x, event.y)
60 return gtk.TRUE
61
62 def motion_notify_event(widget, event):
63 if event.is_hint:
64 x, y, state = event.window.get_pointer()
65 else:
66 x = event.x
67 y = event.y
68 state = event.state
69
70 if state & gtk.gdk.BUTTON1_MASK and pixmap != None:
71 draw_brush(widget, x, y)
72
73 return gtk.TRUE
Los manejadores expose_event() y
configure_event() se describirán más adelante.