Introduction
wxPython provides limited tools for doing customized drawings. Still, these tools should be good enough for most purposes. The tools provided work with images in a variety of formats, simple line drawing, simple polygon shapes, and text objects. wxPython provides a variety of textures and colors that can be used as well.
What Objects are Involved
wxDC -- the DeviceContext
- Drawing can be accomplished on virtually any wxWindow. However, there are certain limitations to should consider before using just any of them. Specifically, obtain a "device context" (inherited from wxDC) upon which to do any drawing. Conveniently, the same techniques work for printing, so code generalized to work with a generic device context will be able to be used for a variety of purposes. I'll cover the topic of which device contexts to select and how to select them later.
- wxPen, wxBrush, wxColour -- the tools for the job
- Appropriate drawing tools are a must. A pen is used for line drawings, and a brush to fill in shapes. Use colours to add interest and variety to drawings.
- wxBitmap -- the bitmap class
- Drawing directly onto the visible window turns out to cause a tremendous amount of flicker if the drawing involves recreating shapes. A program that simply adds new things on top of existing without needing to remember what's underneath probably doesn't have to worry about this. I had to, because my program re-drew the entire window while it was being resized. This led to the need to do "double buffering". This technique is accomplished via the wxBitmap class.
Process Overview
- Create a drawing class. This should be something that inherits from wxWindow. For my application, I inherited from a wxPanel. Don't plan on using this class for anything besides the drawing.
Decide how you'll trigger an update to your class. I respond to size events (EVT_SIZE) and paint events (EVT_PAINT). In addition, I design the methods that respond to both of these so they can run if no event is passed--just define your handler as
def OnSize(self, event=None):
to allow a call of self.OnSize().
For paint event handlers you can do something similar but you need to be able to use a different DC since wxPaintDC can only be used from within an EVT_PAINT event. For example:
- Plan on responding to paint events. If the window gets hidden (for instance, a dialog pops up indicating that "You have mail"...), even briefly, the screen gets blanked. The operating system sends a "paint" event to the window, which is supposed to take care of redrawing itself.
- Decide which device contexts you intend to support. Possible contexts are:
- wxPaintDC -- Plan to support this one. It's a device context that's only appropriate to use within the client area of the widget, and only when called from a paint event. The OS may call this one, so it should be supported.
- wxClientDC -- Just like a paint context, this alternate device context only allows drawing in the client area of the widget. However, this one is only called when NOT in a paint event. This means that it's NOT called by the OS, but it MAY be called by you when you don't particularly want to wait for a paint event.
- wxScreenDC -- a device context that allows you to draw anywhere on the screen -- inside a window or not. This could potentially be useful for doing full-screen drawing, such as displaying movies, games, or presentations. I haven't played with this one, so you'll have to decide if it's appropriate and useful for your purposes on your own.
- wxPrintDC -- a device context corresponding to a printer page. This is part of the printing framework. If you plan to support printing, this is the way to do it. As long as everything you intend to print is drawn to a device context anyway, adding support for printing is easy. Make your print event get a wxPrintDC, then draw to that. You'll get printout virtually for free.
- wxMemoryDC -- a device context with no visible display. This is useful to provide an off-screen location for image preparation. When the image is ready, you simply copy from the memory DC to the other DC that you're using, and the image is updated instantly and without flicker.
- Define your event routines to select the appropriate device context. Once it's selected, call your drawing routines, passing in the selected context.
Obtaining an Appropriate Device Context
As I get time, I'll update this to cover all of these topics.
Loading an initial image
Selecting drawing tools
Adding text to your drawings
Flicker-Free Drawing
Use info from this article: http://freespace.virgin.net/james.brown7/tuts/flicker.htm (see web archive)
See also FlickerFreeDrawing at wxWidgets.
Avoiding unneeded redraws
Double-buffering to eliminate flicker
Printing the results
Adding a print preview
Saving the resulting image
Special Concerns
Transparency and Images/Pens/Brushes
Code Sample
from wxPython.wx import *