Table of Contents:

Introduction

As I am somewhat a maniac of new widgets and the wxWidgets community is very active in developing new ideas/controls, following a suggestion of Robin Dunn I wrote this Wiki page to give some guidelines to wxPython users that decide to port a widget/application from C++ to Python. This guide may not be complete or may not cover all the aspects of this conversion: it is based on my experiences.

First Steps

Once you have decided which widget/application you want to port to wxPython, it's time to look at the C++ source code. Before starting to convert a single C++ line to Python, please take a look at the following sub-sections.

Before doing whatever thing on the original source code, read carefully the license that usually comes with the widget/application. Most of them will be based on wxWidgets license, so you won't have any problem in using/converting it. It may happen, however, that some widget source code is released with a more restrictive license. This case is quite rare, but you should always keep an eye on the license statements.

Try The C++ Code

If you have a working demo of the widget/application (an executable, for instance), try to run it and see if it does what you are looking for. Do not rely only on the description of the widget, because nothing beats the feeling you may have in actually trying it on your machine. The same apply if you have a working wxWidgets installation (but not a working demo): try to compile the source and the demo by linking it to wxWidgets and use it. This step will be useful for you at the end of the conversion in order to compare the C++ widget performances/behaviors with the wxPython one.

Walk Through The C++ Code

It is a good practice, for me, to look carefully at the C++ implementation before opening my preferred Pyhon editor. This step is necessary, in my opinion, for two reasons: first of all, you may find that the C++ code uses some platform-dependent instruction, which sometimes can not be easily converted to wxPython. I had big problems in converting wxAUI to PyAUI because of some of these platform specific calls. Secondly, scanning the code with your eyes gives you a general idea on how things are actually implemented and how the code is organized.

Automatic Code Conversion

Even if some existing applications (the Leo editor, for example, http://webpages.charter.net/edreamleo/front.html) claim to have a partial working code converter from C++ to Python, my suggestion is: don't use it. First of all, these converters are quite complex to set up; they do only a small portion of the job; they may mess up things and you won't be able to notice it because all the work has been done in automatic by the converter. I usually do all the conversion by hand, looking at the C++ instruction and converting them to Python/wxPython commands. The only automatic replacements that I usually do are related to characters used in C++ but somewhat extraneous to Python:

I usually eliminate in automatic the characters ";", "{" and "}" and I convert the characters "->" to ".".

Code Organization

In C++, a class that implements a new widget is usually (but not always) splitted in 2 files: a source file, with extension *.cpp (for example, mynewwidget.cpp) and a header file, with extension *.h (for example, mynewwidget.h). If the widget/application you are porting is complex, you may find that the code is composed by many classes that are splitted in several source files and header files.

CPP and H Files

In theory, source files should contain method/function implementations, while header files should contain only method/function declarations. However, in almost all wxWidgets classes, you will find that header files contain also some method implementation, that you will miss if you don't examine carefully the header file. I don't see any practical reason to insert function implementation in header files (other than confusing the user), but I am not a C++ guru and I am surely missing something. In any case, you should open both the source and the header files in your preferred C++ editor to convert your widget to wxPython.

Don't Reivent The Wheel

Python is a high level language, and it is much more friendly than C++. It happens sometimes that the C++ developer is forced to write an entire method (with hundreds lines of code) to perform a particular action not related to wxWidgets (i.e. starting the browser, reading xml files and so on). Before converting these methods, you should check if something similar has already been done in the standard Python distribution: as for the previous examples, you could use the webbrowser module and the xml.dom module already present in Python. In other word, try to use existing Python modules/classes instead of rewriting everything from scratch.

Code Conversion

If the C++ code is composed by different classes, my suggestion is always to start converting the base/simpler subclasses. Sometimes, this small classes are used just to store information/styles of the main class, so they are somewhat independent from wxPython widgets (wx.Frame, wx.Panel and so on).

Simple Example

As for widget dependent classes, their construction (input parameters and base class) are contained in the header file. As an example, the first simpler class initialization of FoldPanelBar is implemented in this way in C++:

captionbar.h:

class WXDLLIMPEXP_FOLDBAR wxCaptionBar: public wxWindow

captiobar.cpp

wxCaptionBar::wxCaptionBar(wxWindow* parent, const wxString &caption, 
                           wxImageList *images, wxWindowID id,
                           const wxCaptionBarStyle &cbstyle, const wxPoint& pos, 
                           const wxSize& size, long style)
    : wxWindow(parent, id, pos, size, style)
    , m_caption(caption)
    , m_foldIcons(images)
    , m_rightIndent(wxFPB_BMP_RIGHTSPACE)
    , m_iconWidth(16)
    , m_iconHeight(16)
    , m_collapsed(false)

The header file is saying that wxCaptionBar is a subclass of wxWindow. The source file contains input parameters and the initialization code is showing you which attributes are set at the beginning. In Python, the code is converted as follows:

class CaptionBar(wx.Window):    
    def __init__(self, parent, id, pos, size, caption="",
                 foldIcons=None, cbstyle=EmptyCaptionBarStyle,
                 rightIndent=FPB_BMP_RIGHTSPACE,
                 iconWidth=16, iconHeight=16, collapsed=False):
        
        wx.Window.__init__(self, parent, wx.ID_ANY, pos=wx.DefaultPosition,
                           size=(20,20), style=wx.NO_BORDER)

        self._collapsed = collapsed
        self._caption = caption
        self._foldIcons = foldIcons
        self._style = cbstyle
        self._rightIndent = rightIndent
        self._iconWidth = iconWidth
        self._iconHeight = iconHeight

Once you have finished the initialization code conversion, you may start converting all the methods of this class to Python code. Always keep in mind that in C++ class methods are simply called by specifying the method name, as in:

wxRect wndRect = GetRect();
bool vertical = IsVertical();
DrawVerticalGradient(dc, GetRect());

While in Python, you should always refer to the instance "self" to call a class method, like:

wndRect = self.GetRect()
vertical = self.IsVertical()
self.DrawVerticalGradient(dc, self.GetRect())

Methods and functions are declared in C++ in different ways, depending if they return something or not and if they are class methods or simple functions. For example, you may find these declarations in C++:

// ''This belongs to class wxFrameManager, and it doesn't return anything''
void wxFrameManager::SetFlags(unsigned int flags)

// ''This belongs to class wxFrameManager, and it returns an integer''
unsigned int wxFrameManager::GetFlags() const

// ''This belongs to class wxFrameManager, and it returns a wxDockArt instance''
wxDockArt* wxFrameManager::GetArtProvider() const

// ''This is an external function, and it doesn't return anything''
static void DrawResizeHint(wxDC& dc, const wxRect& rect)

In Python, you simply have:

def SetFlags(self, flags):
def GetFlags(self):
def GetArtProvider(self):
def DrawResizeHint(dc, rect):

With the appropriate return values (when needed).

Loops And Lists

C++ developers love for/while loops on lists; moreover, C++ does not have the same power of Python in treating lists. So, you may find a loop like this one in C++:

for(i=0; i<=m_length; i++)
    m_userlist[i] = 0;

While, using Python, you can simply do:

self._userlist = [0]*self._length 

Moreover, in Python it is usually better to iterate over the elements of a list instead of creating a loop variable as in C++. As an example, in C++ you may find:

int i, j, k, dock_count;
for (i = 0, dock_count = dest_docks.GetCount(); i < dock_count; ++i)
{
    wxDockInfo& dock = dest_docks.Item(i);
    // Do something with dock

While in Python you should simply do:

for dock in dest_docks:
    # Do something with dock

Moreover, Python has many other variable types you may consider: lists, dictionaries, tuples and so on.

Events

Events in C++ are usually specified using an "event table", like in this example:

BEGIN_EVENT_TABLE(wxCaptionBar, wxWindow)
    EVT_PAINT(wxCaptionBar::OnPaint)
    EVT_CHAR(wxCaptionBar::OnChar)
    EVT_MOUSE_EVENTS(wxCaptionBar::OnMouseEvent)
    EVT_SIZE(wxCaptionBar::OnSize)
END_EVENT_TABLE()

It is not particularly complex to convert this table to wxPython Bind() calls: you should usually include the event binding declarations inside the init method of the Python class, unless a clearly different implementation exists in the wxWidgets code. In Python, you will end up in having:

class CaptionBar(wx.Window):    
    def __init__(self, parent, id, pos, size, caption="",
                 foldIcons=None, cbstyle=EmptyCaptionBarStyle,
                 rightIndent=FPB_BMP_RIGHTSPACE,
                 iconWidth=16, iconHeight=16, collapsed=False):

        # ... snip other initialization instructions...

        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_MOUSE_EVENTS, self.OnMouseEvent)
        self.Bind(wx.EVT_CHAR, self.OnChar)

Custom events are implemented in different ways in wxWidgets; using wxPython, I am always happy to declare my custom events as follows:

wxEVT_CAPTIONBAR = wx.NewEventType()
EVT_CAPTIONBAR = wx.PyEventBinder(wxEVT_CAPTIONBAR, 0)

# --------------------------------------------------------- #
# class CaptionBarEvent
# --------------------------------------------------------- #

class CaptionBarEvent(wx.PyCommandEvent):
    def __init__(self, evtType):
        wx.PyCommandEvent.__init__(self, evtType)

Is The Code Pythonic?

Very often I read that widgets/applications converted from wxWidgets to wxPython are not "Pythonic", for example because they use too often Set*/Get* methods. I really don't understand such criticism: wxPython is built on wxWidgets, so it is somewhat normal that it may be "Un-Pythonic". You may try to change your converted code to make it more Pythonic, but be aware that drastic changes may lead to difficulties in maintain the code when the C++ widget is updated with a new release and you want to keep your wxPython widget up-to-date.

The Demo

Once you reach the code conversion of the demo, you are almost done. This is usually the simplest part, which involves a wx.Frame with some embellishment (wx.Menu, wx.StatusBar etc...), some buttons and your new and fresh wxPython widget as main protagonist. You may easily personalize the demo to include more functionalities with respect to the wxWidgets one without worrying about code maintainability. In any case, in writing the demo, always include an "About Dialog" in which you explicitely refer to the original creator of the C++ widget. In my opinion, also your source code should contain a reference to the original author.

Testing Your Work

When all the work of converting the source code and the demo is finished, you may want to test your work using Python. Unless you have been maniacally careful during the conversion, you will encounter some error raised by Python: they usually are related to missing ":" at the end of function definitions, for loops and if conditions, calling class methods without the "self." instance, wrong code indentation and undefined variables. Once corrected all these simple syntax errors, if the code still doesn't work I usually begin a simple debug by putting some "print" statements in the obvious places: if the problem is more serious, I suggest you to use a debugger to locate and correct it. At the very end, a comparison of the performances/behaviors between the wxWidgets code and the wxPython code will eventually show you where to improve your code to make it faster, nicer or simply easier to use for another user.

Conclusions

These are just some guidelines about the conversion of C++ wxWidgets code to wxPython, based on my past experiences. Please feel free to suggest any improvement, fix or missing ideas. You may also drop me an e-mail to [andrea.gavana@gmail.com] or [andrea_gavana@tin.it].

Enjoy!

Andrea.

Porting Widgets From C++ (last edited 2008-03-11 10:50:17 by localhost)

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