Extending wxPython with C++ or Wrapping wxWidgets C++ Classes with wxPython

Either way you look at it, it's the same thing, you have some c++ code that you want to be made available to your wxPython programs.

This example uses the wxPython build mechanism and includes your sources. For examples of stand-alone build methods, using Visual C++ projects instead of distutils, see the VisualStudioExtensions page, and the same thing using Scons on Mac and Linux on the SconsExtensions page.

Let's look at a ridiculously simple example:

We have a class written in c++ that is just a button with the word "foo" on it. We'll call it FooButton (because we're very creative).

The c++ portion of this class consists of a single .cpp and a single .h file.

foobutton.h:

#ifndef FOOBUTTON_H
#define FOOBUTTON_H
 
class FooButton : public wxButton
{
public:
    FooButton(wxWindow *parent,
              wxWindowID id,
              const wxString& label,
              const wxPoint& pos = wxDefaultPosition,
              const wxSize& size = wxDefaultSize,
              long style = 0,
              const wxValidator& validator = wxDefaultValidator,
              const wxString& name = "foobutton");
    ~FooButton();
private:
    DECLARE_CLASS(FooButton);
};
 
#endif

foobutton.cpp:

#include <wx/wx.h>
#include "foobuton.h"

IMPLEMENT_CLASS(FooButton, wxButton);

FooButton::FooButton(wxWindow *parent,
          wxWindowID id,
          const wxString& label,
          const wxPoint& pos,
          const wxSize& size,
          long style,
          const wxValidator& validator,
          const wxString& name)
    : wxButton(parent, id, label, pos, size, style, validator, name)
{
    SetLabel("foo");
}
 
FooButton::~FooButton()
{
 
};

Now what we need to do is create a swig wrapper for the class so that we can let swig (wxswig actually) handle the boring details of writing all the interface code. This will be pretty simple. For the most part, it is going to be a copy of the .h file with some swig directives to pull in all the wxPython code. So here's what foobutton_.i will look like (named foobutton_ because we don't want swig to create another file named foobutton.cpp and clobber our code).

foobutton_.i

%module foobutton_
 
%{
#include "wxPython.h"
#include "foobutton.h"
%}
 
//---------------------------------------------------------------------------
 
%include typemaps.i
%include my_typemaps.i
 
%extern wx.i
%extern _defs.i
%extern controls.i
 
%pragma(python) code = "import wx"
 
//----------------------------------------------------------------------
 
%{
    // Put some wx default wxChar* values into wxStrings.
    static const wxString wxPyFooButtonStr(wxT("FooButton"));
    static const wxString wxPyEmptyString(wxT(""));
%}
 
//---------------------------------------------------------------------------
 
class FooButton : public wxButton
{
public:
    FooButton(wxWindow *parent,
              wxWindowID id,
              const wxString& label,
              const wxPoint& pos = wxDefaultPosition,
              const wxSize& size = wxDefaultSize,
              long style = 0,
              const wxValidator& validator = wxDefaultValidator,
              const wxString& name = wxPyFooButtonStr);
    %pragma(python) addtomethod = "__init__:self._setOORInfo(self)"
 
};
 
//---------------------------------------------------------------------------
 
%init %{
    wxClassInfo::CleanUpClasses();
    wxClassInfo::InitializeClasses();
%}

Now that we have the necessary source files in place, you will also want to acquire wxSWIG. Instructions on that already exist at http://www.wxpython.org/download.php#build just pick your OS and follow the steps.

The next step is modify setup.py to tell it to build foobutton

We'll put the line:

   1 BUILD_FOOBUTTON = 1

near the top of the file with all the other BUILD_XXX = X lines

and then add this section right above the "if BUILD_XRC:" line

   1 if BUILD_FOOBUTTON:
   2     msg('Preparing FooButton...')
   3     location = 'contrib/foobutton'
   4     swig_files = ['foobutton_.i']
   5     other_sources = ['contrib/foobutton/foobutton.cpp']
   6                                                                                 
   7     other_includes = [ ] # if you had some other include dirs
   8                          # they would go here
   9                                                                                 
  10     other_libs = [ ] # additional libs would go here
  11                                                                                 
  12     swig_sources = run_swig(swig_files, location, "", PKGDIR,
  13                             USE_SWIG, swig_force, swig_args, swig_deps)
  14                                                                                 
  15     ext = Extension('foobutton_c',
  16                     swig_sources + other_sources,
  17                                                                                 
  18                     include_dirs = includes + other_includes,
  19                     define_macros = defines,
  20                                                                                 
  21                     library_dirs = libdirs,
  22                     libraries = libs + other_libs,
  23                                                                                 
  24                     extra_compile_args = cflags,
  25                     extra_link_args = lflags,
  26                     )
  27                                                                                 
  28     wxpExtensions.append(ext)

swig_files of course contains the list of swig files, in our case only the one. other_sources contains the relative path to foobutton.cpp. If you had libs or include paths that needed to be passed to the compiler, you could put them in other_includes and other_libs

Now we can run setup.py like so: ./setup.py USE_SWIG=1 build

and then, I usually install into a temporary path for testing like so: ./setup.py install --root=/home/mrroach/tmp/

now we can test all that effort with the following, very simple wxPython script:

   1 from wxPython.wx import *
   2 from wxPython.foobutton_ import FooButton
   3 
   4 class TestFrame(wxFrame):
   5     def __init__(self):
   6         wxFrame.__init__(self, None, -1, "FooButton Test",
   7                          wxDefaultPosition,
   8                          style=wxDEFAULT_FRAME_STYLE)
   9 
  10         self.fb = FooButton(self, -1, "something other than foo...")
  11 
  12         EVT_CLOSE(self, self.OnCloseWindow)
  13  
  14     def OnCloseWindow(self, evt):
  15         self.Destroy()
  16 
  17 app = wxPySimpleApp()
  18 frame = TestFrame()
  19 frame.Show(True)
  20 app.MainLoop()

Which will give you an amazingly unimpressive window with a button that, as promised, says "foo"

A new attempt with wxPython 2.8.4.2

The code above contains several constructs that appear to be out of date.

The code in the following attachment is a fairly minimal attempt to wrap a wxWindow subclass:

scrollwindow_extension.zip

Update: 2nd try scrollwindow_extension2.zip

When it's working, directions will go here.

C++Extensions (last edited 2010-07-29 16:09:05 by fl-67-235-186-192)

NOTE: To edit pages in this wiki you must be a member of the TrustedEditorsGroup.