As my first real foray into GUI programming (not counting Delphi GUIs at university), I have been messing with GTK recently. Again, I repeatedly ran into many things which may be obvious to those in the know, but not so clear to those of us temporarily without Internet, clutching only a copy of the PyGTK class reference and the Python docs.
This post (and probably most future GTK-related posts) will deal with PyGTK (since it is so simple to demonstrate things), but it is equally applicable to vanilla GTK, you’ll just need to fiddle with the function names and so on.
The first problem I had was to present a large (3000+ pixels on a side) TIFF image in a DrawingArea widget. Clearly, I needed scrollbars, so the DrawingArea widget was inside a ScrolledWindow. I then had an on_redraw() function which handled the redrawing of the DrawingArea, which is called whenever the window needs redrawing, or when I wanted to paint on top of the image.
The wrong way
The redrawing code contained this line to load the image from a Pixbuf saved as a variable back into the DrawingArea to “reset” it:
self.drawingArea.window.draw_pixbuf(gc, self.pixbuf, 0,0,0,0) |
You can see the problem this will cause—this code will load a lot more of the image than we need, so much that the program will be significantly slower. At a window size of 300×300, this will load about 100 times more image data than we need. We need to load only that which we need to load.
The right way
The class reference for a gtk.gdk.Drawable says “The draw_pixbuf() method renders a rectangular portion of a gtk.gdk.Pixbuf…” which immediately tells use that this function will do more than paste the whole things into the Pixbuf.
The last four parameters in the draw_pixbuf() function are, respectively, src_x, src_y, dest_x, dest_y, width, height, and these are the parameters we will use to tell GTK which parts of the image we want to copy in.
First, we need to know the horizontal and vertical offset of the top-left corner of the visible part of the image. This will tell us where to start pasting our image from. We can do this by interrogating the values held in ScrolledWindow’s VAdjustment and HAdjustment using get_vadjustment().value and get_hadjustment().value. We need to convert to an int, because we will shortly use this as a pixel index.
vertOffset = int( self.scrolledWindow.get_vadjustment().value ) horzOffset = int( self.scrolledWindow.get_hadjustment().value ) |
The second bit of information we need is the size of the visible window of the ScrolledWindow. We do this by looking at the page_size attribute of the two adjustments:
visibleHeight = int( self.scrolledWindow.get_vadjustment().page_size ) visibleWidth = int( self.scrolledWindow.get_hadjustment().page_size ) |
We now have all the information needed to paste in just enough of the image to cover the whole window, but not a pixel more.
self.drawingArea.window.draw_pixbuf(gc, self.pixbuf, horzOffset, vertOffset, horzOffset, vertOffset, width=int(visibleWidth), height=int(visibleHeight)) |
And that is it! If you only needed to redraw a smaller area (for example, blitting), you can tweak these exact same parameters to do that for you.
Example
This can make the difference between an unusable program and one with no perceivable lag. This sample program demonstrates this. You need to edit the source to point to some large image on your system, then run it with
python scrollable-image.py
You will need Python and PyGTK to run this code.

You should fix the path for your sample program.
it’s http://www.inductiveload.com/files/src/scrollable-image.py
nice piece of code, not very easy to find stuffs about scrollable image…
Thanks for the heads up, I fixed the link.