9.6. Imágenes

Las Images (Imágenes) son estructuras de datos que contienen dibujos. Estos dibujos se pueden usar en varios sitios.

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

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

  image = gtk.Image()

Después se carga la imagen 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)

Donde pixbuf es un GdkPixbuf; pixmap y mask son gtk.gdk.Pixmaps; image es una gtk.gdk.Image; stock_id es el nombre de un gtk.StockItem; icon_set es un gtk.IconSet; y, animation es una gtk.gdk.PixbufAnimation. el argumento size (tamaño) es uno de:

 ICON_SIZE_MENU
 ICON_SIZE_SMALL_TOOLBAR
 ICON_SIZE_LARGE_TOOLBAR
 ICON_SIZE_BUTTON
 ICON_SIZE_DND
 ICON_SIZE_DIALOG

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

El programa images.py muestra cómo cargar varios tipos de imagen (goalie.gif, apple-red.png, chaos.jpg, important.tif, soccerball.gif) en imágenes que se colocan dentro de botones:

Figura 9.5. Ejemplo de Imágenes en Botones

Ejemplo de Imágenes en Botones

El código fuente es:

    1	#!/usr/bin/env python
    2	
    3	# ejemplo images.py
    4	
    5	import pygtk
    6	pygtk.require('2.0')
    7	import gtk
    8	
    9	class ImagesExample:
   10	    # cuando se invoca (con la señal delete_event), finaliza la aplicación
   11	    def close_application(self, widget, event, data=None):
   12	        gtk.main_quit()
   13	        return gtk.FALSE
   14	
   15	    # se invoca cuando el botón es pulsado.  Simplemente imprime un mensaje.
   16	    def button_clicked(self, widget, data=None):
   17	        print "button %s clicked" % data
   18	
   19	    def __init__(self):
   20	        # crea la ventana principal y conecta la señal delete_event signal para finalizar
   21	        # la aplicación
   22	        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
   23	        window.connect("delete_event", self.close_application)
   24	        window.set_border_width(10)
   25	        window.show()
   26	
   27	        # una caja horizontal que contenga los botones
   28	        hbox = gtk.HBox()
   29	        hbox.show()
   30	        window.add(hbox)
   31	
   32	        pixbufanim = gtk.gdk.PixbufAnimation("goalie.gif")
   33	        image = gtk.Image()
   34	        image.set_from_animation(pixbufanim)
   35	        image.show()
   36	        # un botón que contenga el control de imagen
   37	        button = gtk.Button()
   38	        button.add(image)
   39	        button.show()
   40	        hbox.pack_start(button)
   41	        button.connect("clicked", self.button_clicked, "1")
   42	        
   43	        # crear varias imágenes con datos de archivos y cargarlos
   44	        # en botones
   45	        image = gtk.Image()
   46	        image.set_from_file("apple-red.png")
   47	        image.show()
   48	        # un botón que cotenga el control de imagen
   49	        button = gtk.Button()
   50	        button.add(image)
   51	        button.show()
   52	        hbox.pack_start(button)
   53	        button.connect("clicked", self.button_clicked, "2")
   54	
   55	        image = gtk.Image()
   56	        image.set_from_file("chaos.jpg")
   57	        image.show()
   58	        # un botón que cotenga el control de imagen
   59	        button = gtk.Button()
   60	        button.add(image)
   61	        button.show()
   62	        hbox.pack_start(button)
   63	        button.connect("clicked", self.button_clicked, "3")
   64	
   65	        image = gtk.Image()
   66	        image.set_from_file("important.tif")
   67	        image.show()
   68	        # un botón que cotenga el control de imagen
   69	        button = gtk.Button()
   70	        button.add(image)
   71	        button.show()
   72	        hbox.pack_start(button)
   73	        button.connect("clicked", self.button_clicked, "4")
   74	
   75	        image = gtk.Image()
   76	        image.set_from_file("soccerball.gif")
   77	        image.show()
   78	        # un botón que cotenga el control de imagen
   79	        button = gtk.Button()
   80	        button.add(image)
   81	        button.show()
   82	        hbox.pack_start(button)
   83	        button.connect("clicked", self.button_clicked, "5")
   84	
   85	
   86	def main():
   87	    gtk.main()
   88	    return 0
   89	
   90	if __name__ == "__main__":
   91	    ImagesExample()
   92	    main()

9.6.1. Pixmaps

Los Pixmaps son estructuras de datos que contienen dibujos. Estos dibujos se pueden usar en varios sitios, pero lo 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 caso especial.

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

Para usar pixmaps en PyGTK, primero debemos construir un gtk.gdk.Pixmap usando las funciones de gtk.gdk en PyGTK. Los Pixmaps se pueden crear a partir de datos en memoria, o a partir de datos leídos desde un fichero. Veamos cada una de las llamadas usadas para crear un pixmap.

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

Esta rutina se usa para crear un pixmap con la profundidad de color dada por el argumento depth a partir de los datos data en memoria. Si depth es -1 su valor se deduce de la de la ventana window. Cada pixel usa un número de bits de datos para representar el color que es igual a la profundidad de color. El width(ancho) y el height (alto) son en pixeles. El argumento window (ventana) debe referirse a una gtk.gdk.Window realizada, ya que los recursos de un pixmap sólo tienen sentido en el contexto de la pantalla donde se va a visualizar. fg y bg son los colores de frente y fondo del pixmap.

Se pueden crear pixmaps desde ficheros XPM usando:

  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 ampliamente y hay muchas utilidades disponibles para crear ficheros de imágenes en este formato. En la función pixmap_create_from_xpm() el primer argumento es del tipo gtk.gdk.Window . (La mayoría de los controles GTK tienen una gtk.gdk.Window subyacente que se puede obtener usando el atributo window (ventana) del control.) El fichero se especifica con filename y debe contener una imagen en formato XPM el cual se carga en la estructura del pixmap. La mask (máscara) es un bitmap que especifica qué bits del pixmap son opacos; se crea con la función. Todos los demás pixeles se colorean con el color especificado por transparent_color. Un ejemplo del uso de esta función se recoge 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.    ",
  "   .........    ",
  "                ",
  "                "
  ]

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 una gtk.gdk.Window o None. Si window es una gtk.gdk.Window entonces depth puede ser -1 para indicar que la profundidad de color se obtiene de la ventana. Si window es None entonces depth debe especificarse.

El programa pixmap.py es un ejemplo del uso de un pixmap en un botón. La figura Figura 9.6, “Ejemplo de Pixmap en un Botón” muestra el resultado:

Figura 9.6. Ejemplo de Pixmap en un Botón

Ejemplo de Pixmap en un Botón

El código fuente es:

    1	#!/usr/bin/env python
    2	
    3	# ejemplo pixmap.py
    4	
    5	import pygtk
    6	pygtk.require('2.0')
    7	import gtk
    8	
    9	# XPM data of Open-File icon
   10	xpm_data = [
   11	"16 16 3 1",
   12	"       c None",
   13	".      c #000000000000",
   14	"X      c #FFFFFFFFFFFF",
   15	"                ",
   16	"   ......       ",
   17	"   .XXX.X.      ",
   18	"   .XXX.XX.     ",
   19	"   .XXX.XXX.    ",
   20	"   .XXX.....    ",
   21	"   .XXXXXXX.    ",
   22	"   .XXXXXXX.    ",
   23	"   .XXXXXXX.    ",
   24	"   .XXXXXXX.    ",
   25	"   .XXXXXXX.    ",
   26	"   .XXXXXXX.    ",
   27	"   .XXXXXXX.    ",
   28	"   .........    ",
   29	"                ",
   30	"                "
   31	]
   32	
   33	class PixmapExample:
   34	    # cuando se invoca (con la señal delete_event), finaliza la aplicación.
   35	    def close_application(self, widget, event, data=None):
   36	        gtk.main_quit()
   37	        return gtk.FALSE
   38	
   39	    # se invoca al pulsar el botón.  Simplemente imprime un mensaje
   40	    def button_clicked(self, widget, data=None):
   41	        print "button clicked"
   42	
   43	    def __init__(self):
   44	        # crea la ventana principal y conecta la señal delete_event para finalizar
   45	        # la aplicación
   46	        window = gtk.Window(gtk.WINDOW_TOPLEVEL)
   47	        window.connect("delete_event", self.close_application)
   48	        window.set_border_width(10)
   49	        window.show()
   50	
   51	        # ahora el pixmap desde datos XPM
   52	        pixmap, mask = gtk.gdk.pixmap_create_from_xpm_d(window.window,
   53	                                                        None,
   54	                                                        xpm_data)
   55	
   56	        # un control de imagen que contenga el pixmap
   57	        image = gtk.Image()
   58	        image.set_from_pixmap(pixmap, mask)
   59	        image.show()
   60	
   61	        # un botón que contenga el control de imagen
   62	        button = gtk.Button()
   63	        button.add(image)
   64	        window.add(button)
   65	        button.show()
   66	
   67	        button.connect("clicked", self.button_clicked)
   68	
   69	def main():
   70	    gtk.main()
   71	    return 0
   72	
   73	if __name__ == "__main__":
   74	    PixmapExample()
   75	    main()

Una desventaja de usar pixmaps es que que el objeto mostrado siempre es rectangular, independientemente de la imagen. Nos gustaría crear escritorios y aplicaciones con iconos que tengan formas más naturales. Por ejemplo, para la interfaz de 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 imagen de fondo se colorea, no la sobreescribimos con un borde rectangular y que no encaja, de nuestro icono. El programa de ejemplo wheelbarrow.p muestra una imagen completa en el escritorio. La figura Figura 9.7, “Ejemplo de Ventana con Forma” muestra la imagen sobre una ventana de terminal:

Figura 9.7. Ejemplo de Ventana con Forma

Ejemplo de Ventana con Forma

The source code for wheelbarrow.py is:

    1	#!/usr/bin/env python
    2	
    3	# ejemplo wheelbarrow.py
    4	
    5	import pygtk
    6	pygtk.require('2.0')
    7	import gtk
    8	
    9	# XPM
   10	WheelbarrowFull_xpm = [
   11	"48 48 64 1",
   12	"       c None",
   13	".      c #DF7DCF3CC71B",
   14	"X      c #965875D669A6",
   15	"o      c #71C671C671C6",
   16	"O      c #A699A289A699",
   17	"+      c #965892489658",
   18	"@      c #8E38410330C2",
   19	"#      c #D75C7DF769A6",
   20	"$      c #F7DECF3CC71B",
   21	"%      c #96588A288E38",
   22	"&      c #A69992489E79",
   23	"*      c #8E3886178E38",
   24	"=      c #104008200820",
   25	"-      c #596510401040",
   26	";      c #C71B30C230C2",
   27	":      c #C71B9A699658",
   28	">      c #618561856185",
   29	",      c #20811C712081",
   30	"<      c #104000000000",
   31	"1      c #861720812081",
   32	"2      c #DF7D4D344103",
   33	"3      c #79E769A671C6",
   34	"4      c #861782078617",
   35	"5      c #41033CF34103",
   36	"6      c #000000000000",
   37	"7      c #49241C711040",
   38	"8      c #492445144924",
   39	"9      c #082008200820",
   40	"0      c #69A618611861",
   41	"q      c #B6DA71C65144",
   42	"w      c #410330C238E3",
   43	"e      c #CF3CBAEAB6DA",
   44	"r      c #71C6451430C2",
   45	"t      c #EFBEDB6CD75C",
   46	"y      c #28A208200820",
   47	"u      c #186110401040",
   48	"i      c #596528A21861",
   49	"p      c #71C661855965",
   50	"a      c #A69996589658",
   51	"s      c #30C228A230C2",
   52	"d      c #BEFBA289AEBA",
   53	"f      c #596545145144",
   54	"g      c #30C230C230C2",
   55	"h      c #8E3882078617",
   56	"j      c #208118612081",
   57	"k      c #38E30C300820",
   58	"l      c #30C2208128A2",
   59	"z      c #38E328A238E3",
   60	"x      c #514438E34924",
   61	"c      c #618555555965",
   62	"v      c #30C2208130C2",
   63	"b      c #38E328A230C2",
   64	"n      c #28A228A228A2",
   65	"m      c #41032CB228A2",
   66	"M      c #104010401040",
   67	"N      c #492438E34103",
   68	"B      c #28A2208128A2",
   69	"V      c #A699596538E3",
   70	"C      c #30C21C711040",
   71	"Z      c #30C218611040",
   72	"A      c #965865955965",
   73	"S      c #618534D32081",
   74	"D      c #38E31C711040",
   75	"F      c #082000000820",
   76	"                                                ",
   77	"          .XoO                                  ",
   78	"         +@#$%o&                                ",
   79	"         *=-;#::o+                              ",
   80	"           >,<12#:34                            ",
   81	"             45671#:X3                          ",
   82	"               +89<02qwo                        ",
   83	"e*                >,67;ro                       ",
   84	"ty>                 459@>+&&                    ",
   85	"$2u+                  ><ipas8*                  ",
   86	"%$;=*                *3:.Xa.dfg>                ",
   87	"Oh$;ya             *3d.a8j,Xe.d3g8+             ",
   88	" Oh$;ka          *3d$a8lz,,xxc:.e3g54           ",
   89	"  Oh$;kO       *pd$%svbzz,sxxxxfX..&wn>         ",
   90	"   Oh$@mO    *3dthwlsslszjzxxxxxxx3:td8M4       ",
   91	"    Oh$@g& *3d$XNlvvvlllm,mNwxxxxxxxfa.:,B*     ",
   92	"     Oh$@,Od.czlllllzlmmqV@V#V@fxxxxxxxf:%j5&   ",
   93	"      Oh$1hd5lllslllCCZrV#r#:#2AxxxxxxxxxcdwM*  ",
   94	"       OXq6c.%8vvvllZZiqqApA:mq:Xxcpcxxxxxfdc9* ",
   95	"        2r<6gde3bllZZrVi7S@SV77A::qApxxxxxxfdcM ",
   96	"        :,q-6MN.dfmZZrrSS:#riirDSAX@Af5xxxxxfevo",
   97	"         +A26jguXtAZZZC7iDiCCrVVii7Cmmmxxxxxx%3g",
   98	"          *#16jszN..3DZZZZrCVSA2rZrV7Dmmwxxxx&en",
   99	"           p2yFvzssXe:fCZZCiiD7iiZDiDSSZwwxx8e*>",
  100	"           OA1<jzxwwc:$d%NDZZZZCCCZCCZZCmxxfd.B ",
  101	"            3206Bwxxszx%et.eaAp77m77mmmf3&eeeg* ",
  102	"             @26MvzxNzvlbwfpdettttttttttt.c,n&  ",
  103	"             *;16=lsNwwNwgsvslbwwvccc3pcfu<o    ",
  104	"              p;<69BvwwsszslllbBlllllllu<5+     ",
  105	"              OS0y6FBlvvvzvzss,u=Blllj=54       ",
  106	"               c1-699Blvlllllu7k96MMMg4         ",
  107	"               *10y8n6FjvllllB<166668           ",
  108	"                S-kg+>666<M<996-y6n<8*          ",
  109	"                p71=4 m69996kD8Z-66698&&        ",
  110	"                &i0ycm6n4 ogk17,0<6666g         ",
  111	"                 N-k-<>     >=01-kuu666>        ",
  112	"                 ,6ky&      &46-10ul,66,        ",
  113	"                 Ou0<>       o66y<ulw<66&       ",
  114	"                  *kk5       >66By7=xu664       ",
  115	"                   <<M4      466lj<Mxu66o       ",
  116	"                   *>>       +66uv,zN666*       ",
  117	"                              566,xxj669        ",
  118	"                              4666FF666>        ",
  119	"                               >966666M         ",
  120	"                                oM6668+         ",
  121	"                                  *4            ",
  122	"                                                ",
  123	"                                                "
  124	]
  125	
  126	class WheelbarrowExample:
  127	    # Cuando se invoca (con la señal delete_event), finaliza la aplicación
  128	    def close_application(self, widget, event, data=None):
  129	        gtk.main_quit()
  130	        return gtk.FALSE
  131	
  132	    def __init__(self):
  133	        # Crea la ventana principal y conecta la señal delete_event para finalizar
  134	        # la aplicación.  Obsérvese que la ventana principal no tendrá título
  135	        # ya que vamos a hacer una ventana emergente (popup).
  136	        window = gtk.Window(gtk.WINDOW_POPUP)
  137	        window.connect("delete_event", self.close_application)
  138	        window.set_events(window.get_events() | gtk.gdk.BUTTON_PRESS_MASK)
  139	        window.connect("button_press_event", self.close_application)
  140	        window.show()
  141	
  142	        # ahora para el pixmap y el control de imagen
  143	        pixmap, mask = gtk.gdk.pixmap_create_from_xpm_d(
  144	            window.window, None, WheelbarrowFull_xpm)
  145	        image = gtk.Image()
  146	        image.set_from_pixmap(pixmap, mask)
  147	        image.show()
  148	
  149	        # Para mostrar la imagen usamos un control fijo para situarla
  150	        fixed = gtk.Fixed()
  151	        fixed.set_size_request(200, 200)
  152	        fixed.put(image, 0, 0)
  153	        window.add(fixed)
  154	        fixed.show()
  155	
  156	        # Esto enmascara todo salvo la imagen misma
  157	        window.shape_combine_mask(mask, 0, 0)
  158	    
  159	        # mostramos la ventana
  160	        window.set_position(gtk.WIN_POS_CENTER_ALWAYS)
  161	        window.show()
  162	
  163	def main():
  164	    gtk.main()
  165	    return 0
  166	
  167	if __name__ == "__main__":
  168	    WheelbarrowExample()
  169	    main()

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