darea = GtkDrawingArea()
A default size for the widget can be specified
This default size can be overridden, as is true for all widgets, by calling the set_usize() method, and that, in turn, can be overridden if the user manually resizes the the window containing the drawing area.
It should be noted that when we create a DrawingArea widget, we are completely responsible for drawing the contents. If our window is obscured then uncovered, we get an exposure event and must redraw what was previously hidden.
Having to remember everything that was drawn on the screen so we can properly redraw it can, to say the least, be a nuisance. In addition, it can be visually distracting if portions of the window are cleared, then redrawn step by step. The solution to this problem is to use an offscreen backing pixmap. Instead of drawing directly to the screen, we draw to an image stored in server memory but not displayed, then when the image changes or new portions of the image are displayed, we copy the relevant portions onto the screen.
To create an offscreen pixmap, we call
pixmap = pixmap_new(window, width, height, depth)
The window parameter specifies a GDK window that this pixmap takes some of its properties from. width and height specify the size of the pixmap. depth specifies the color depth, that is the number of bits per pixel, for the new window. If the depth is specified as -1, it will match the depth of window.
We create the pixmap in our "configure_event"
handler. This event is generated whenever the window changes size, including
when it is originally created.
30 # Create a new backing pixmap of the appropriate size 31 def configure_event(widget, event): 32 global pixmap 33 34 x, y, width, height = widget.get_allocation() 35 pixmap = gtk.create_pixmap(widget.get_window(), width, height, -1) 36 gtk.draw_rectangle(pixmap, widget.get_style().white_gc, 37 gtk.TRUE, 0, 0, width, height) 38 39 return gtk.TRUE
The call to draw_rectangle() clears the pixmap initially to white. We'll say more about that in a moment.
Our exposure event handler then simply
copies the relevant portion of the pixmap onto the drawing area (widget)
using the draw_pixmap() method.
(We determine the area we need to redraw by using the event.area
attribute of the exposure event):
41 # Redraw the screen from the backing pixmap 42 def expose_event(widget, event): 43 x , y, width, height = event.area 44 widget.draw_pixmap(widget.get_style().fg_gc[gtk.STATE_NORMAL], 45 pixmap, x, y, x, y, width, height) 46 return gtk.FALSE
We've now seen how to keep the screen
up to date with our pixmap, but how do we actually draw interesting stuff
on our pixmap? There are a large number of calls in PyGTK for drawing on
A drawable is simply something that can be drawn upon. It can be a window,
a pixmap, or a bitmap (a black and white image). We've already seen two
such calls above,
and draw_pixmap(). The complete
draw_point(drawable, gc, x, y) draw_line(drawable, gc, x1, y1, x2, y2) draw_rectangle(drawable, gc, fill, x, y, width, height) draw_arc(drawable, gc, fill, x, y, width, height, angle1, angle2) draw_polygon(drawable, gc, fill, points) draw_string(drawable, font, gc, x, y, string) draw_text(drawable, font, gc, x, y, string) draw_pixmap(drawable, gc, src, xsrc, ysrc, xdest, ydest, width, height) draw_bitmap(drawable, gc, src, xsrc, ysrc, xdest, ydest, width, height) draw_points(drawable, gc, points) draw_lines(drawable, gc, points) draw_segments(drawable, gc, segments) draw_rgb_image(drawable, gc, x, y, width, height, dither, buffer, rowstride) draw_rgb_32_image(drawable, gc, x, y, width, height, dither, buffer, rowstride) draw_gray_image(drawable, gc, x, y, width, height, dither, buffer, rowstride)
The drawing area methods have the same functionality as the drawable drawing functions so you can use the Drawing Area Methods section for further details on these functions. These functions all share the same first two arguments. The first argument is the drawable to draw upon, the second argument is a graphics context (gc).
A graphics context encapsulates information
about things such as foreground and background color and line width. PyGTK
has a full set of functions for creating and modifying graphics contexts,
but to keep things simple we'll just use predefined graphics contexts.
See the Drawing Area Graphics Context
for more information on graphics contexts. Each widget has an associated
style. (Which can be modified in a gtkrc file, see the section
rc file.) This, among other things, stores a number of graphics contexts.
Some examples of accessing these graphics contexts are:
widget.get_style().white_gc widget.get_style().black_gc widget.get_style().fg_gc[STATE_NORMAL] widget.get_style().bg_gc[STATE_PRELIGHT]
The fields fg_gc,
light_gc are indexed
by a parameter which can take on the values:
STATE_NORMAL, STATE_ACTIVE, STATE_PRELIGHT, STATE_SELECTED, STATE_INSENSITIVE
For instance, for STATE_SELECTED the default foreground color is white and the default background color, dark blue.
Our function draw_brush(),
which does the actual drawing on the pixmap,
48 # Draw a rectangle on the screen 49 def draw_brush(widget, x, y): 50 rect = (x - 5, y - 5, 10, 10) 51 gtk.draw_rectangle(pixmap, widget.get_style().black_gc, gtk.TRUE, 52 rect, rect, rect, rect) 53 widget.draw(rect)
After we draw the rectangle representing
the brush onto the pixmap,
we call the function:
which notifies X that the area given needs to be updated. X will eventually generate an expose event (possibly combining the areas passed in several calls to draw()) which will cause our expose event handler to copy the relevant portions to the screen.
We have now covered the entire drawing program except for a few mundane details like creating the main window.