Ahora toca dibujar en la pantalla. El control que se usa para
éso es la
DrawingArea (Área
de dibujo). Un control de área de dibujo es básicamente una ventana X; sin
más. Es un lienzo en blanco en el que se puede pintar lo que se desee. Un área
de dibujo se crea usando la llamada:
darea = gtk.DrawingArea()
Se puede especificar un tamaño predeterminado para el control usando:
darea.set_size_request(width,height)
Este tamaño predeterminado se puede cambiar, como en todos los
controles, llamando al método set_size_request(), y
también se puede modificar si el usuario cambia manualmente el tamaño de
la ventana que contiene el área de dibujo.
Hay que aclarar que, cuando creamos un control DrawingArea, se adquiere
toda la responsabilidad de dibujar su contenido. Si la ventana se tapa y
luego se muestra, se recibe un evento de exposición y se debe redibujar lo que
antes se ocultó.
Tener que recordar lo que había dibujado en la pantalla para que podamos redibujarlo es, cuanto menos, una inconveniencia. Además, puede resultar molesto visualmente el que partes de la ventana se limpien para luego dibujarse paso a paso. La solución a este problema es el uso de un pixmap oculto. En vez de dibujar directamente en la pantalla se dibuja en la imagen almacenada en la memoria del servidor (que no se muestra), y luego se copian las partes relevantes a la pantalla.
Para crear un pixmap fuera de pantalla, se usa esta función:
pixmap = gtk.gdk.Pixmap(window,width,height,depth=-1)
El parámetro window especifica una ventana
gtk.gdk.Window de la que este pixmap tomará algunas propiedades.
width (ancho) y height (alto) especifican
el tamaño del pixmap. depth especifica la
profundidad de color, es decir, el número de bits por píxel de la nueva ventana.
Si depth es -1 o se omite, coincidirá con la profundidad de la ventana.
Creamos el pixmap en nuestro manejador del evento "configure_event". Este evento se genera cada vez que la ventana cambia de tamaño, incluso cuando se crea por primera vez.
32 # Creamos un nuevo mapa de píxeles de respaldo con el tamaño adecuado
33 def configure_event(widget, event):
34 global pixmap
35
36 x, y, width, height = widget.get_allocation()
37 pixmap = gtk.gdk.Pixmap(widget.get_window(), width, height)
38 gtk.draw_rectangle(pixmap, widget.get_style().white_gc,
39 gtk.TRUE, 0, 0, width, height)
40
41 return gtk.TRUE
La llamada a draw_rectangle() deja inicialmente el pixmap
en blanco. Se comentará esto un poco más detenidamente en breve.
El manejador del evento de exposición simplemente copia
las partes correspondientes del pixmap en el área de dibujo usando el método
draw_pixmap(). (Se determina el área que se debe redibujar
mediante el atributo event.area del evento de
exposición):
43 # Redibujamos la pantalla a partir del pixmap de respaldo
44 def expose_event(widget, event):
45 x , y, width, height = event.area
46 widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL],
47 pixmap, x, y, x, y, width, height)
48 return gtk.FALSE
Ya se ha visto cómo manetener la pantalla sincronizada con el
pixmap de respaldo, pero ¿cómo se pintan cosas interesantes en el pixmap? Existe un gran
número de llamadas de PyGTK para dibujar en objetos dibujables. Un objeto
dibujable es sencillamente algo en lo que se puede dibujar. Puede ser una ventana,
un pixmap, o un bitmap (imagen en blanco y negro). Ya se han visto dos de
esas llamadas con anterioridad, draw_rectangle() y
draw_pixmap(). La lista completa es:
# dibuja punto drawable.draw_point(gc,x,y) # dibuja línea drawable.draw_line(gc,x1,y1,x2,y2) # dibuja rectángulo drawable.draw_rectangle(gc,fill,x,y,width,height) # dibuja arco drawable.draw_arc(gc,fill,x,y,width,height,angle1,angle2) # dibuja polígono drawable.draw_polygon(gc,fill,points) # dibuja dibujable drawable.draw_drawable(gc,src,xsrc,ysrc,xdest,ydest,width,height) # dibuja puntos drawable.draw_points(gc,points) # dibuja líneas drawable.draw_lines(gc,points) # dibuja segmentos drawable.draw_segments(gc,segments) # dibuja imagen rgb drawable.draw_rgb_image(gc,x,y,width,height,dither,buffer,rowstride) # dibuja imagen rgb 32 bits drawable.draw_rgb_32_image(gc,x,y,width,height,dither,buffer,rowstride) # dibuja imagen escala grises drawable.draw_gray_image(gc,x,y,width,height,dither,buffer,rowstride)
Los métodos del área de dibujo son los mismos que los métodos de
dibujo de los objetos dibujables por lo que se puede consultar la sección
Métodos de Dibujo para más detalles sobre
estas funciones. Todas estas funciones comparten los primeros argumentos. El
primer argumento es un contexto gráfico (gc).
Un contexto gráfico encapsula información sobre cuestiones como los
colores de fondo y frente o el ancho de línea. PyGTK posee un conjunto completo
de funciones para crear y modificar contextos gráficos, pero para mantener las
cosas fáciles aquí simplemente se usarán contextos gráficos predefinidos.
Consúltese la sección Contexto Gráfico del Área de Dibujo
para obtener más información sobre los contextos gráficos. Cada control tiene un estilo
asociado (que se puede modificar en un fichero gtkrc, según se indica en la
sección Ficheros rc de GTK+.) Éste, entre
otras cosas, almacena diversos contextos gráficos. Algunos ejemplos del
acceso 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 campos fg_gc, bg_gc,
dark_gc, y light_gc se indexan con un
parámetro que puede tomar los siguientes valores:
STATE_NORMAL, # El estado durante la operación normal
STATE_ACTIVE, # El control está activado, como cuando se pulsa un botón
STATE_PRELIGHT, # El puntero del ratón está sobre el control
STATE_SELECTED, # El control está seleccionado
STATE_INSENSITIVE # El control está desactivado
Por ejemplo, para STATE_SELECTED el color de frente predeterminado es
el blanco y el color de fondo predeterminado es azul oscuro.
La función draw_brush() (pinta trazo) del ejemplo, que
es la que realmente dibuja en el pixmap, es la siguiente:
50 # Dibujamos un rectángulo en la pantalla
51 def draw_brush(widget, x, y):
52 rect = (int(x - 5), int(y - 5), 10, 10)
53 pixmap.draw_rectangle(widget.get_style().black_gc, gtk.TRUE,
54 rect[0], rect[1], rect[2], rect[3])
55 apply(widget.queue_draw_area, rect)
Tras 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 ha de ser actualizada. En algún
momento X generará un evento de exposición (posiblemente combinando las áreas
que se le pasan en varias llamadas a draw()) que hará
que se llame al manejador del evento de exposición que se creó anteriormente que copiará las
partes relevantes en la pantalla.
Y... ya se ha tratado todo el programa de dibujo, exceptuando algunos detalles mundanos como la creación de la ventana principal.