Creating Custom wxPython Widget

Hi all, I am fairly new to using both Python and wxPython, although I
am fairly experienced with C/C++ and wxWidgets. I am trying to use
Python and wxPython to construct a GUI front-end to my application. I
want to make some custom wxPython widgets that will be connected to
some C++ data structures.

One example is that I need to create a graphing widget. It should
contain a pointer to some graph data, and be able to draw it in the
wxDC. If I was implementing this in C++ and wxWidgets, I would just
subclass wxPanel or something, and include a pointer to the data in
the constructor. Then I could just put in the appropriate drawing
code. However, I need such a control to be usable form Python, and
thus I assume I need to somehow convert my wxWidgets widget into a
wxPython widget. I gather that I might need to use SWIG for this, but
I am very confused as to how all this works.

If someone could give me at least a broad overview, and maybe some
updated and accurate links, that would be excellent.

Thanks,
Chris Anderson

You'll need a wrapper (in SWIG or something similiar) to get at your
C++ data from Python. Depending on where your external data is coming
from, this might be really easy or pretty hard. If you already have
the C++ widget written, you can package it up in a library, wrap it
with swig just like all the other wx libs (stc, media, etc) and you'll
be pretty good to go.

Yes, that is exactly what I intend to do. But the devil is in the details....

Robin Dunn wrote:

As others have mentioned, the easiest thing would be to write an
extension module for creating and accessing your C++ data structures,
and (re)write the widget itself in Python. You could even avoid SWIG
altogether this way and just write the data structure extension module
by hand, which will let you end up with a more efficient extension. Or,
if some other tool fits your brain better then you could use that.

Yes, I am kind of flip flopping in my mind between doing it this way,
or trying to wrap a C++ widget. The main issue for me is speed and
efficiency as far as accessing the data is concerned. One widget that
I will be creating will need to take a large array of floating point
data, and based on the values in the array plot colored points in a 2D
image. If I were to create this widget entirely in python, and connect
it to a data structure extension module, I would need some way of
getting fairly direct access to that raw data array (which would
reside on the C++ side of things). This doesn't seem to me to be a
particularly obvious or easy task. I totally understand how to wrap c
functions that pass values back and forth, but I don't want to copy
the entire data array (into a list for example) and pass it, since
that would be slow.

The only thing I have found yet that might do what I want is the NumPy
array object, which appears to allow access to the raw data from both
python and from within a C extension module. So I could create a NumPy
array in the extension module, give it a pointer to my data array, and
then pass it to the wxPython object which would then use it to draw
the pixels. Does this sound like it would work?

Thanks to everyone,
Chris Anderson

Robin Dunn wrote:

However if you do decide to wrap your C++ widget then you will need to
use SWIG. This is because you will want the Python code to know that it
derives from wx.Control (or whatever) and be able to use it like any
other widget, so the SWIG runtime type system will have to be involved.
  Be sure to use the same version of SWIG that wxPython is using,
(currently 1.3.29, plus some patches, which you can get from the
wxPython source tarball, or a pre-patched version is located at
NameBright - Coming Soon)

There is a page in the wiki that talks about this,
http://wiki.wxpython.org/index.cgi/C++Extensions. It's somewhat out of
date, but it should get you pointed in the right direction. You can
also look at the non-core modules in the wxPython source tree for
examples, such as wxPython/src/calendar.i. There are also some 3rd
Party extension modules out there that you could use as an example.

I tried the tutorial out, and it got me close but not enough to
successfully build the example "foobutton". I tried changing my .i
file based on calendar.i, but I still am having troubles. Is there any
chance someone could update the tutorial to a functioning state?

Thanks,
Chris Anderson

Robin Dunn wrote:

If you don't want the additional dependency of NumPy I think you can do the same with the standard array module.

indeed,you could use any object that support the Buffer interface:

http://python.active-venture.com/api/bufferObjects.html

> Although it is a very

rich tool so if you use it you can probably also do any other manipulation of the data that you need to do, with all the heavy lifting being quickly done in the extension module.

exactly. The odds are very good that if you're graphing data, you can use numpy for lots of other stuff.

Maybe sure to check out matplotlib too -- it many not meet your performance requirements, but it is a very nice package, and works well embedded in wxPython.

A plug: at this point, the wxPython has code to optimize reading in a list of 2-tuples of numbers for input into things like:

wx.DC.DrawLines()

when you pass in a numpy array, it uses the generic sequence indexing to get the data -- this is a bit slow, as numpy then has to generate a bunch of little arrays to hold each point, then index those to get the numbers, etc.

It would be great if wxPython understood the numpy "array interface", (http://numpy.scipy.org/array_interface.shtml) so that methods like DrawLines could access the numpy data array directly. This would require some additions to the PointListHelper code in wxPython.

It's on my list to do it some day, but a lot of things are on my list...

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Chris Mellon wrote:

This might be an excellent place to experiment with weave (part of
scipy, from the same source as numpy),

hmmm -- I've never thought to use weave to call wx methods -- how the heck would you get a pointer to the DC?

> I've never used it, but it should be possible to loop over

the internal C++ numarray representation, calling DrawLine() as
needed, all in C code.

Well, there is already DrawXXXList, that does that. In this case, what I'm looking for a is a way to directly pass the pointer to the numpy data block to the constructor for a wxPointList (or to an overloaded Draw* method that takes a pointer to a C array.

That gets Python totally out of the way and
should make for a very large speedup.

Well, maybe. I did some work with DrawXXXList, and the speed-up is only significant for simple objects: Points, Lines. For more complex stuff , including, to my surprise, bitmaps, the drawing time overwhelms the Python-C++ data translation anyway.

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer

Emergency Response Division
NOAA/NOS/OR&R (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Robin Dunn wrote:

What troubles?

I used the foobutton_.i code exactly as in the tutorial. I also added
this section to my setup.py:

···

#----------------------------------------------------------------------
# FooButton Extension Module
#----------------------------------------------------------------------
msg('Preparing FooButton...')
location = 'contrib/foobutton'

swig_files = ['foobutton_.i']
other_sources = ['contrib/foobutton/foobutton.cpp']

other_includes =
other_libs =

swig_sources = run_swig(swig_files, location, GENDIR, PKGDIR,
                        USE_SWIG, swig_force, swig_args, swig_deps)

ext = Extension('_foobutton',
                swig_sources + other_sources,

                include_dirs = includes + CONTRIBS_INC + other_includes,
                define_macros = defines,

                library_dirs = libdirs,
                libraries = libs + other_libs,

                extra_compile_args = cflags,
                extra_link_args = lflags)

wxpExtensions.append(ext)

Now, when I try to run setup, I get these errors in regards to foobutton:

Warning(101): %extern is deprecated. Use %import instead.
Error: Unable to find 'wx.i'

I'm not sure exactly what the right thing to do is here.

Thanks,
Chris Anderson

Christopher Anderson wrote:

Robin Dunn wrote:

What troubles?

I used the foobutton_.i code exactly as in the tutorial. I also added
this section to my setup.py:

Now, when I try to run setup, I get these errors in regards to foobutton:

Warning(101): %extern is deprecated. Use %import instead.
Error: Unable to find 'wx.i'

I'm not sure exactly what the right thing to do is here.

Try using calendar.i as a template instead of starting with what is in the wiki. In other words, rename calendar.i and replace the calendar stuff with the foobutton declarations.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!