Roses
The wxPython Usage within Roses

As I discussed in my main Roses page, one of my motivations was to learn something about wxPython, a Python interface to the wxWidgets graphics package. My interest was piqued sometime after writing my first Python Roses variant that used the Tkinter package when I read praise like "Tkinter is dead, Java is dead, wxPython rules! That's all there is to say."

A few years later I had time to check it out and realized that much of the praise was due to the number of tools available in wxPython and not so much for the ease of use or the mindset of someone who spent most of his career in Unix kernel networking protocols.

Using wxPython was not easy, was not intuitive, and not all that pleasant, at least at first. This it shares with every other graphics package I've used except for pre-raster, pre-windowing systems when I could write my own display lists. Eventually I began to figure out the system, I'm sure people more experienced than I would have had much less trouble. This is especially true for applications that have the typical menu and status bar design instead of the uncommon layout I use in Roses.

One thing that is very useful is the demo system manifested by wxPython-src-2.8.1.1/wxPython/demo/demo.py. It serves as a good "card catalog" of the various widgets that are available and as a source of boilerplate code to adapt into your application. A number of demo programs have bugs or crash, I think the problems are more with the demos than the library as the demos tend to demonstrate the widget with as little code as possible. Some of the failures may work okay on other platforms.

The book wxPython in Action is well regarded with the publisher's marketing page saying it's "a complete guide to the wxPython toolkit." It's not. If it were, it would be three times longer. However, it is a good introduction and fits well with the demo programs and online documentation.

General Structure of a wxPython (or any wxWidgets Application)

Graphical toolkits generally produce tree structures of nested widgets, and that's what we have here. The uppermost object is called a frame. While you can put various objects that you can see directly on the frame, that normally isn't done. Instead, you create panel objects and put things on them. When in doubt, making a new panel seems to help.

Widgets and other objects used in wxRoses

The visible widgets used in wxRoses include:

Sizers

The placement of the controls is generally done by "sizers." It can be done manually, and I do that with in SpinCtrl. For anything but the simplest and most complex cases, sizers make things easy. They also deal with resizing the windows and other hassles. A sizer isn't a widget, but is some object that you create, tell what widgets are to be tracked, and then attach to a a panel widget. Then you just forget about it and the right stuff seems to happen. There are five sizers with multiple options and wxroses uses three of them:

Events and callbacks

Any interactive graphical application is event driven. This means you never really feel in contol of the system. Most of your code is called from C++ code you've never seen and really don't want to. Fortunately, I haven't really needed to except to see if I could intercept alphabetic characters typed into SpinCtrl widgets. (Answer: I don't think so.)

There are a lot of events scattered through wxPython, wxRoses only uses these:

You bind an event to a callback method and when the event happens, that method executes within the context of the object that did the binding.

All together

All these bits and pieces fit into a tree-like hierarchy: --- New ---

Oh, yeah, drawing vectors

Ultimately, the whole point of roses is to draw patterns. wxPython seems a little odd in that you do that with the aid of an ephemeral object called a device context. As always, there are several types that do things as extreme as drawing on the raw screen, printer spooler, or bitmap buffers. wxRoses just uses ClientDC which draws on the main area of a widget, in this case the top_panel.

The only two things it needs to do is clear the widget and draw lines on the widget. The former uses a "Brush" on the "Background" to paint the area with "Clear". The latter uses a "Pen" and a connect-the-dots method called "DrawLines".

A ClientDC can't be used within EVT_SIZE and EVT_PAINT handlers (I think PaintDC is for that), but wxRoses just arranges for a new pattern to be drawn through the usual time-based event.

I had some trouble getting resize and repaint events handled properly. I may be missing something simple, or sizers don't let me have the hooks I need. The vectors are drawn on my top_panel widget. The FlexGridSizer there apparently uses EVT_SIZE to position the control panels and I can't figure out how to share that event with both the sizer and the vector redraw code. Binding both EVT_SIZE and EVT_PAINT on the se_panel mostly worked, but EVT_PAINT events were only generated when part of the se_panel was exposed. By triggering a redraw on EVT_PAINT events on top_panel and EVT_SIZE on se_panel, I seem to have things working. Ugly and kludgy, but it works.

So, does wxPython rule?

In playing around with the wxPython demo.py tool, it's clear that there are a lot of applications built with wxWidgets. Since that's in C++, wxPython provides some low-overhead hooks to get into native code. WxRoses needs about a page and a half of code to set up the display window and add all the hooks it needs, and about as much to interact with the roses engine after that. For all the tiny little details that need to be handled, I can't complain about that.

Speedwise, it may be as fast or faster than my PDP-11 version of roses running on a PDP-11/45 back in 1973. That's not really a criticism - raster graphics have far more overhead than using a vector drawing engine, windowing systems have much more overhead than when I was the only application drawing on the display, and the pipe between the application and X11 has far more overhead than the PDP-11 system. That needed merely two 16 bit memory writes to add a vector to the pattern being displayed.

I'm also calling multiple trig functions for each vector, the PDP-11 used a table lookup with interpolation and fixed point (integer) arithmetic. So keeping up with a PDP-11 is actually quite a good accomplishment!

Perhaps the best test to answer "Does it rule?" is to ask "What's next?" I'd love to see a Python program that display GPS tracks and deals with waypoints, maps, etc. I have a Python code to talk to a serial port, experience dealing with one serial line protocol, C code for dealing with Garmin GPS receivers that can be converted to Python, MySQL, and wxPython. Way to go! I'd also love to see a wxPython version of Banshee. Banshee started out nice, but doesn't handle large libraries well and has several annoyances. While it uses Mono, it sure likes like wxWidgets is behind it. I don't care about Mono or .net, and I think I could write a Banshee-like thing that executes various Linux apps to play or rip CDs.

So, wxPython has me enthusiastic about writing new applications, and that seems as good a test as any. If only there were a wxPython widget to add a few hours to the day....


Contact Ric Werme or return to his home page.

Last updated 2007 June 13, originally written May 2007.