Monday, June 28, 2010

Porting Gonert: Qt's Design

All of the tutorials probably followed the bad example of Qt for concrete inheritance.  Some features are only available through inheritance.  Others have only recently allowed for composition.

Luckily I was able to get through all of Gonvert without concrete inheritance.  A nice trick to avoid inheriting from QObject to create signals is to hijack QAction.  I actually used QActions for what they were intended in Gonvert but in some future apps I might abuse them.  I find the class hierarchies odd as compared to GTK.  Just one example is GTK encourages inserting widgets into scroll windows but Qt has widgets inherit from them.  I do admit this probably lets them couple the concepts closely enough that it makes it easier to give a native feel on platforms.

The sweet taste of bloat in the morning.  Qt let's you show any QWidget as a window.  This requires less boiletplate for debugging my own custom widgets (which I left for my next porting exercise) but it makes each widget feel bloated that they can completely handle being a window in of themselves.

Qt was designed when C++ had a less extensive set of libraries.  Why does that have to bleed over into the Python bindings though?  When I first dived into GTK I was nervous about my interactions with the underlying types and ownership.  I eventually learned I could just ignore all of it and it felt great.  GTK had a feel that is very native to Python.  Tutorials for PyQt on the other hand have to go into long discussions of the trade-offs between native data types and Qt's data types.  They also had to go over object ownership rules.  This left a bad taste in my mouth.

I also see people recommending Qt equivalents of python objects. I'd rather not couple myself that tightly to the UI framework.  You can keep your database access, your XML, and (I hope) your threading.  That last one I have yet to explore how worth while it is to do.

A minor annoyance, ctrl+c in the shell does not seem to kill Qt.  If things went wrong setting everything up I have to go and kill my application.  That can be annoying for keeping debug cycles short.  I also had it crash on exit a lot.  I have no clue what I changed that removed that.

The old-style signal syntax is nasty in Qt.  The new stuff is pretty good.  I dislike that I don't get a token to use to unregister a slot but I pass in the same function.  This is annoying when your slot is a lambda.  It is also annoying if for some crazy reason you wanted to register a slot multiple times.  Registering multiple times and my use of lambdas brings up another annoyance.  GTK is written in C and so all callbacks had to offer a void* data pointer at the end so anything can be done with them.  In PyGTK this was cool because they exposed that to the user by allowing arbitrary objects to be passed in there (the object was already being passed in when using a bound method).  They even implemented it through *args so you didn't have it bundle up multiple objects yourself, it would do it for you.  In DialCentral I use a single slot for the dialpad and then use the data parameter to differentiate the key.  Qt is designed around slots being on objects I think and is used to member function pointers.  This sadly means they didn't have to have the void* of C days and you have to go through extra steps to pass arbitrary data to a slot.

I do really enjoy QActions.  At least I said something positive, right?  I'll probably have more gripes and more things to praise it for as I continue porting my applications over.