9.6. Images

Images are data structures that contain pictures. These pictures can be used in various places.

Images can be created from Pixbufs, Pixmaps, image files (e.g. XPM, PNG, JPEG, TIFF, etc.) and even animation files.

Images are created using the function:

  image = gtk.Image()

The image is then loaded using one of the following methods:

  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)

Where pixbuf is a gtk.gdk.Pixbuf; pixmap and mask are gtk.gdk.Pixmaps; image is a gtk.gdk.Image; stock_id is the name of a gtk.StockItem; icon_set is a gtk.IconSet; and, animation is a gtk.gdk.PixbufAnimation. the size argument is one of:

 ICON_SIZE_MENU
 ICON_SIZE_SMALL_TOOLBAR
 ICON_SIZE_LARGE_TOOLBAR
 ICON_SIZE_BUTTON
 ICON_SIZE_DND
 ICON_SIZE_DIALOG

The easiest way to create an image is using the set_from_file() method which automatically determines the image type and loads it.

The program images.py illustrates loading various image types (goalie.gif, apple-red.png, chaos.jpg, important.tif, soccerball.gif) into images which are then put into buttons:

Figure 9.5. Example Images in Buttons

Example Images in Buttons

The source code is:

    1	#!/usr/bin/env python
    2	
    3	# example images.py
    4	
    5	import pygtk
    6	pygtk.require('2.0')
    7	import gtk
    8	
    9	class ImagesExample:
   10	    # when invoked (via signal delete_event), terminates the application.
   11	    def close_application(self, widget, event, data=None):
   12	        gtk.main_quit()
   13	        return False
   14	
   15	    # is invoked when the button is clicked.  It just prints a message.
   16	    def button_clicked(self, widget, data=None):
   17	        print "button %s clicked" % data
   18	
   19	    def __init__(self):
   20	        # create the main window, and attach delete_event signal to terminating
   21	        # the application
   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	        # a horizontal box to hold the buttons
   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	        # a button to contain the image widget
   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	        # create several images with data from files and load images into
   44	        # buttons
   45	        image = gtk.Image()
   46	        image.set_from_file("apple-red.png")
   47	        image.show()
   48	        # a button to contain the image widget
   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	        # a button to contain the image widget
   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	        # a button to contain the image widget
   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	        # a button to contain the image widget
   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

Pixmaps are data structures that contain pictures. These pictures can be used in various places, but most commonly as icons on the X desktop, or as cursors.

A pixmap which only has 2 colors is called a bitmap, and there are a few additional routines for handling this common special case.

To understand pixmaps, it would help to understand how X window system works. Under X, applications do not need to be running on the same computer that is interacting with the user. Instead, the various applications, called "clients", all communicate with a program which displays the graphics and handles the keyboard and mouse. This program which interacts directly with the user is called a "display server" or "X server." Since the communication might take place over a network, it's important to keep some information with the X server. Pixmaps, for example, are stored in the memory of the X server. This means that once pixmap values are set, they don't need to keep getting transmitted over the network; instead a command is sent to "display pixmap number XYZ here." Even if you aren't using X with GTK+ currently, using constructs such as Pixmaps will make your programs work acceptably under X.

To use pixmaps in PyGTK, we must first build a gtk.gdk.Pixmap using gtk.gdk functions in PyGTK. Pixmaps can either be created from in-memory data, or from data read from a file. We'll go through each of the calls to create a pixmap.

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

This routine is used to create a pixmap from data in memory with the color depth given by depth. If depth is -1 the color depth is derived from the depth of window. Each pixel uses depth bits of data to represent the color. Width and height are in pixels. The window argument must refer to a realized gtk.gdk.Window, since a pixmap's resources are meaningful only in the context of the screen where it is to be displayed. fg and bg are the foreground and background colors of the pixmap.

Pixmaps can be created from XPM files using:

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

XPM format is a readable pixmap representation for the X Window System. It is widely used and many different utilities are available for creating image files in this format. In the pixmap_create_from_xpm() function the first argument is a gtk.gdk.Window type. (Most GTK+ widgets have an underlying gtk.gdk.Window which can be retrieved by using the widget's window attribute.) The file, specified by filename, must contain an image in the XPM format and the image is loaded into the pixmap structure. The mask is a bitmap that specifies which bits of pixmap are opaque; it is created by the function. All other pixels are colored using the color specified by transparent_color. An example using this function is below.

Pixmaps can also be created from data in memory using the function:

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

Small images can be incorporated into a program as data in the XPM format using the above function. A pixmap is created using this data, instead of reading it from a file. An example of such data is:

  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.    ",
  "   .........    ",
  "                ",
  "                "
  ]

The final way to create a blank pixmap suitable for drawing operations is:

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

window is either a gtk.gdk.Window. or None. If window is a gtk.gdk.Window then depth can be -1 to indicate that the depth should be determined from the window. If window is None then the depth must be specified.

The pixmap.py program is an example of using a pixmap in a button. Figure 9.6, “Pixmap in a Button Example” shows the result:

Figure 9.6. Pixmap in a Button Example

Pixmap in a Button Example

The source code is:

    1	#!/usr/bin/env python
    2	
    3	# example 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	    # when invoked (via signal delete_event), terminates the application.
   35	    def close_application(self, widget, event, data=None):
   36	        gtk.main_quit()
   37	        return False
   38	
   39	    # is invoked when the button is clicked.  It just prints a message.
   40	    def button_clicked(self, widget, data=None):
   41	        print "button clicked"
   42	
   43	    def __init__(self):
   44	        # create the main window, and attach delete_event signal to terminating
   45	        # the application
   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	        # now for the pixmap from XPM data
   52	        pixmap, mask = gtk.gdk.pixmap_create_from_xpm_d(window.window,
   53	                                                        None,
   54	                                                        xpm_data)
   55	
   56	        # an image widget to contain the pixmap
   57	        image = gtk.Image()
   58	        image.set_from_pixmap(pixmap, mask)
   59	        image.show()
   60	
   61	        # a button to contain the image widget
   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()

A disadvantage of using pixmaps is that the displayed object is always rectangular, regardless of the image. We would like to create desktops and applications with icons that have more natural shapes. For example, for a game interface, we would like to have round buttons to push. The way to do this is using shaped windows.

A shaped window is simply a pixmap where the background pixels are transparent. This way, when the background image is multi-colored, we don't overwrite it with a rectangular, non-matching border around our icon. The wheelbarrow.py example program displays a full wheelbarrow image on the desktop. Figure 9.7, “Wheelbarrow Example Shaped Window” shows the wheelbarrow over a terminal window:

Figure 9.7. Wheelbarrow Example Shaped Window

Wheelbarrow Example Shaped Window

The source code for wheelbarrow.py is:

    1	#!/usr/bin/env python
    2	
    3	# example 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	    # When invoked (via signal delete_event), terminates the application
  128	    def close_application(self, widget, event, data=None):
  129	        gtk.main_quit()
  130	        return False
  131	
  132	    def __init__(self):
  133	        # Create the main window, and attach delete_event signal to terminate
  134	        # the application.  Note that the main window will not have a titlebar
  135	        # since we're making it a 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	        # Now for the pixmap and the image widget
  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	        # To display the image, we use a fixed widget to place the image
  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	        # This masks out everything except for the image itself
  157	        window.shape_combine_mask(mask, 0, 0)
  158	    
  159	        # show the window
  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()

To make the wheelbarrow image sensitive, we attached the "button_press_event" signal to make the program exit. Lines 138-139 make the picture sensitive to a mouse button being pressed and connect the close_application() method.