The Design of Software (CLOSED)

A public forum for discussing the design of software, from the user interface to the code architecture. Now closed.

The "Design of Software" discussion group has been merged with the main Joel on Software discussion group.

The archives will remain online indefinitely.

wxPython positive experience so far

I thought I'd put this out there to give some closure to all those who participated in the "GUI dilemma" thread. I decided to bite the bullet & try Python & wxPython, and my initial impression is positive.

  Several people have said that wx is intuitive enough that you don't really need a graphical GUI editor, and this bizarre claim turns out to be true. The biggest obstacle you will encounter is that the documentation is spotty & the class references are for C++. You will initially spend a lot of time searching the mailing list archives at wxPython.org

  I also recommend Wingware's Wing IDE (I'm not affiliated in any way). It's not nearly as full-featured as Borland's or Microsoft's IDEs, but its useable and intuitive (IMHO Boa Constructor is neither). Wing is well worth the measly $175

  I haven't yet started to integrate my C code. In Windows, the standard method relies on the Microsoft VC++ compiler, but there is a way to do it using the free mingw compiler, which is explained here:
  http://sebsauvage.net/python/mingw.html 

  As for Python itself, it's a very powerful language that's actually not painful to use, for a change. The Lisp-like runtime introspection & metaclass-hacking capabilities offer the ability to write a function that can operate on objects of any type by interrogating the object at run-time. This ability can be incredibly useful. In C++ or Java, you'd have to implement a separate version of the function for each class as a member function of that class.

  Many of the problems I've encountered have to do with the bleeding-edge factor, so they should improve as the language matures. If any of our friends at O'Reilly are reading this, a wxPython book would be just right, and make sure you include a fat section on packaging aplications for deployment.

  Keep on hackin...
    JF
John Foxx
Thursday, April 14, 2005
 
 
The other thread made me try wxRuby, which is wxWidgets for Ruby in the same way that wxPython is for Python. I'm very happy with it. I have started my own wrapper based on it, and so far, so good.

While I was reading some documentation, I browsed wxPython's documentation as well (very handy), but when I saw all that Python code, I was reminded of the ugliness of the Python code, that most people just can't see or just don't care.

It's good that you are using an IDE though, because I wouldn't want to manually handle that code. :-)

The library that I'm creating that is based on wxRuby, actually follows the experience that I've had with another library that I also develop which is based on Ruby-GNOME2 (GTK+).

See these code snippets:

require 'gr/gtk_rules'
GR.app{|w| w.hpack GR::GRButton.new('Sair').sc{ GR.quit } }

require 'wxr/wx_rules'
WXR.app{|w| w.hpack WXR::WXRButton.new(w.panel, 'Sair').evt{ WXR.quit } }

I would say that I really like wxWidgets so far.
DeNiro
Thursday, April 14, 2005
 
 
> GR.app{|w| w.hpack GR::GRButton.new('Sair').sc{ GR.quit } }

Not uggly at all. Oh my.
son of parnas
Thursday, April 14, 2005
 
 
Yep, that's not very neat, but this one is:

require 'gr/gr'
app{|w| w << button('Oi'){ quit } }

It is quite possible:
http://ruby.com.br/~pedrosa/speedy.png

I just don't worry about an API that's easier for the user, because I'm the user anyway. :-) But 6 months to an year from now, I could create a generic interface that could use wxRuby or Ruby-GNOME2, depending if the user is on Windows or on Linux. These two toolkits are cross-platform by the way.
DeNiro
Thursday, April 14, 2005
 
 
John, I also had a good initial experience with wxPython. But on a recent commercial wxPython project my euphoria wore off when trying to turn it into a polished, packaged application that had the correct look & feel across platforms. Some specific issues included:

Sluggish UI
Slow program start up
Excessive flickering in some controls
Non-native listview/treeview controls on OS-X 
Bloated distribution

I find Python's dynamic typing an irritant when developing large programs with wxPython. Python is great for the back-end code, where I can run unit tests to exercise the code. But in the GUI, the typos and type-related problems can be missed until you load the application (wait!) and click through the GUI.

On my current cross-platform project I'm using Python as the back-end (with some C-based extension modules) and developing the UI using the native C/C++/Objective-C toolkits on the target platforms. This is working out really well. There's more up-front work to get the UI running on each platform and I'm having to get more deeply involved with the Python C API but the end results are far superior and I'm not losing time working around shortcomings in wxPython.

Incidentally, for cross-platform wx work, make sure you use and understand wxSizers instead of using absolute placement of widgets. wxDesigner is a workable sizer-based GUI layout tool (I don't think Boa uses sizers, or at least it didn't when I last looked).

Keep us up-to-date with your progress!
_ Send private email
Friday, April 15, 2005
 
 
"Excessive flickering in some controls"

I can't comment on your other issues, but binding the wx.ERASE_BACKGROUND event of the offending control with an empty event handler (that is, lambda: None) solved this problem for me on Windows. There's still a wee bit of flickering during resizing, but it's comparable to what I see on some native apps.
masharpe
Friday, April 15, 2005
 
 
I have a (very) skeletal GUI written & am trying to wrap my C code as a Python extension module (that's what they call a native-code module in Python-land).

  I haven't had much luck with Swig, because if you're doing anything more complicated than see-Dick-run, it doesn't understand what you want. For example, it will not understand that you want this:
      int SomeFunc(int param, char* ps);
to be implemented as a Python function that takes a parameter and returns an (integer,string) tuple. So you have to write it all yourself.

  Fortunately, there's an excellent tutorial for doing just that here: http://starship.python.net/crew/arcege/extwriting/pyext.html

  As it turns out, a Python extension module is nothing more than a dynamic-link library (.so in Linux, .dll in windows) that has some global symbols defined: an init function and a table of pointers to callable API functions. So you can just add a module to your project called pywrap.c and exclude it from your non-python-related builds.

  As Mr. Underscore observed, controls should be placed using sizers, which are objects that know how to evenly space controls in a window. That way, if the user re-sizes the window, the sizers will take care of shuffling the controls around. This also allows you to create dialogs w/o a graphical editor (w/o losing your mind).

  My app is not large enough yet for sluggishness to be a problem. Here's what I'm hoping to get out of Python, though. In C++ and Java, the first phase of any large project is the construction of a huge class hierarchy which is hard to refactor if you get it wrong. This activity is only necessary because these languages can't deal with collections of heterogeneous objects. They can't do any meaningful run-time introspection & can't implement functions that act on objects of any arbitrary type.

  My hope is that the dynamic typing of Python will flatten the object hierarchy & make that up-front phase disappear. I also hope refactoring will be easier & I won't have to do my Amazing Karnak the Fortune Teller bit at the start of every project (i.e. trying to predict, in advance, every possible evolutionary path). You get good at it after a while, but is it necessary?

  So that's where we're at... Don't try this at home, kids :-)

-JF
John Foxx
Friday, April 15, 2005
 
 
> I haven't had much luck with Swig.... For example, it will > not understand that you want this:
>      int SomeFunc(int param, char* ps);
> ...

Swig can handle this sort of stuff using typemaps

http://www.swig.org/Doc1.3/Typemaps.html

There are some predefined typemaps for C-strings in cstring.i.

That said, there's no harm (and a lot of good) in knowing the C API. It's quite straightforward. But if you need to wrap a large API then Swig will save you a lot of time.
_ Send private email
Friday, April 15, 2005
 
 
I think I'm going to end up doing it by hand anyway because I want to do some fancy things. For instance, in C, for the sake of being lightweight, my functions all return an error code which you can use as an index into a table of error descriptions if you want to know what the error means. In the Python wrappers, I think I'm going to cut to the chase & just return the description as a string. I doubt you can get the robot to understand that you want this basic form for all wrapper functions:

PyObject* someFunc(PyObject* Self, PyObject* pArgs)
{
  uint_32  Ret;
  PyObject* p;

  Ret = SomeFunc(); // C version
  p  = Py_BuildValue("s", ErrorDescTbl[Ret]);
  return p;
}

And anyway, as you said, it doesn't hurt to understand what's going on in there.
John Foxx
Saturday, April 16, 2005
 
 
It might be better to accomplish that by throwing an exception.
masharpe
Saturday, April 16, 2005
 
 
John, As marsharpe said, it's more Pythonic to throw an exception. For the record, SWIG typemaps can handle this sort of transformation too. You define a typemap to specify what happens when a C function returns your ERR type. Something like this to autmotically throw a Python string exception if your C function returns an error

%typemap(python, except) ERR {
  $function
  if (result != 0) {
    PyErr_SetString(PyExc_Exception,ErrorDescTbl[result]);
    goto fail;
  }
}

Swig is very powerful but it can take a while to get into some of its more advanced features.
_ Send private email
Saturday, April 16, 2005
 
 
Is it really necessary to throw an exception for normal errors? Java does that, to the extent that read-loops often terminate in exceptions when there's nothing more to read & I hate that. I was going to save the exceptions for exceptional conditions (like bad arguments & stuff like that) & use return codes for the rest. The Python.org literature says to throw exceptions by returning NULL, and that doesn't give you anywhere to put a message. This document describes a more sophisticated method, but the doc is out-of-date & the method doesn't work:  http://starship.python.net/crew/arcege/extwriting/pyext.html 

Is there somewhere else I should be looking? A Python API function name I can Google?
John Foxx
Sunday, April 17, 2005
 
 
No, you can do it anyway you want. Generally the Python standard library throws exceptions for abnormal conditions but not for unexceptional events like reaching the end of a file.

In your example it seemed that you wanted to return a string to indicate an error. I can't imagine doing much other than reporting or logging the error string; you wouldn't be comparing the error string against known error strings and taking different paths through the code. Therefore an exception would work nicely. But it's your choice.

The relevant Python C API docs are here:

http://docs.python.org/api/api.html
http://docs.python.org/api/exceptions.html
http://docs.python.org/api/exceptionHandling.html

Essentially in you Python C code you set the global/per-thread exception state to register the cause of the exception then return NULL to indicate that there was an error. Analogous to setting "errno" and returning -1 in the standard C library.
_ Send private email
Monday, April 18, 2005
 
 
"Analogous to setting "errno" and returning -1 in the standard C library."

Right, except that Guido was smart enough to think about the issue of thread safety and the ANSI folks weren't :-)  Thanks, this section was exactly what I needed.

BTW, I'm over the speed bumps now, I have a working extension module & the app is rolling along. It's amazing how fast you can do things in Python once you've finally got your ducks in a row.
John Foxx
Monday, April 18, 2005
 
 
Are they really ducks in a row or are they just duck-like enough for your purposes? ;-)
Sorry
Monday, April 18, 2005
 
 
Actually they're Grouse, which inherit from Duck, so Grouse.DoQuack() is a little different from Duck.DoQuack() but it's close enough :-)

And here's the post-mortem on my project: I now have a primitive & unpolished but working GUI wrapping my C back-end, which is implemented as a Python extension module. All told, I spent one week banging my head against the wall with Python & wxPython, which is not too bad. I bought the Wingware IDE, & the learning curve for that is zero.

Some lessons learned:

(1) For a small API (~20 functions) you might as well code the Python wrapper by hand. I can post a source template if anyone wants it. The benefit is that I can use Borland C++ Builder X for all 3 versions of my C code: cmd-line console app, static library, and Py extension (i.e. dynamic library). I have 3 different project (.cbx) files, but all 3 use the same source modules. The biggest gotcha I found is that gcc apparently does name-mangling even for .c modules unless you declare your functions extern "C", and Python will complain that it can't find your module init function.  For a .so project, gcc exports everything, and does not support __export or __declspec(__dllexport) as Windows compilers do.

(2) Use the wxPython wiki "Getting started" wiki.wxpython.org and search the wxPython mailing lists at lists.wxwidgets.org  There's a C++ class reference available at wxwidgets.org/manuals/2.4.2/wx26.htm#classref  Dive Into Python by Mark Pilgrim is available on-line (I forget where I downloaded it, but google is your friend)

(3) All-in-all I think it was worth it. Now that the project is rolling, I'm making fast progress & can do a lot with amazingly few lines of code. Much of this is undoubtedly due to the built-in string manipulation. As a strategy, a Python front-end and a C back-end seems just about right.
John Foxx
Tuesday, April 19, 2005
 
 

This topic is archived. No further replies will be accepted.

Other recent topics Other recent topics
 
Powered by FogBugz