Thursday, July 29, 2010

Porting ejpi: Tree Models in Qt

For me, one thing that differentiates the quality of a calculator on a mouse or touch driven device is having a calculation history and having it be interactive.  In Gtk I implemented this with a TreeView and a ListStore.  I had never implemented a custom model in Gtk, the included models were simple enough and flexible enough that I didn't have the need.

When I ported Gonvert to Qt, all I could find information for was custom models with QAbstractItemModel and QTreeWidget/QTreeWidgetItem.  QTreeWidgetItem's seem to only be good for Text.  This worked for parts of Gonvert but not for others so I went through the complicated affair of implementing a QAbstractItemModel.  I never felt like I fully understood the relationships of various items which usually worries me.  I also found it slow due to all of the protection I "should" have in my code which causes a lot of calling back and forth between C++ and Python.

I'm unsure how I missed it but when I started on ejpi I came across QStandardItemModel/QStandardItem.  This seems to at least allow a little more flexibility.  I can put icons and checkboxes in as well as text into the model.  I can also attach random python objects to each item.  The downside to a stock model is that you have to externally maintain any invariants but I was already managing those with my Gtk version.

I would be curious about the various performance merits to each approach but I'll have to pass on that for now.  The two main things I found frustrating was finding out about these and how each API was helpful in its own way, sometimes better, sometimes worse than its fellow approaches.

Most of my API issues were due to being unfamiliar with them and having to fight with the different approaches each model system took to presenting itself to the developer.  I still have one unfortunate piece of code in ejpi's calucation history which implements peek through a pop and a push.

I've not messed with setting the font on any of these (I should have for Gonvert and also for ejpi) so I'm unsure how the font experience is in Qt.  Mainly I'm interested in what formatting Pango html-esque tags allowed, changing relative size and basic formatting while keeping everything else to theme defaults.  A quick look through the API seems to suggest this is simple but that is yet to be determined.
I still miss GTK's generic way of handling rendering with its use of CellRenderers.  You don't have to be stuck with the built-in choices for representing items in your model.  It also keeps presentation information away from the model.

I did enjoy how easy QStandardItem's made user edits (sadly only seems to work on the desktop) and activating of individual items.  I didn't get around to exploring drag and drop for history re-arrangement (helpful for RPN calculators) but I don't think I implemented that for the GTK version either.

My GTK version and my Qt version of my ejpi history are available for comparison.

I guess the summary would be that overall I'm a fan of QStandardItemModel and wish I had found it sooner.

2 comments:

  1. Hi,

    QAbstractItemModel (and friends) aren't really that hard to understand once you play around some.

    Simply put, they're just an API you use to provide an interface to data, you have to write the actual storage bits yourself, whether that's a QList or whatever else. This (of course) means there is no restriction on what you actually store, and how you store it.

    A simple example tutorial I've written might be of use: http://blog.rburchell.com/2010/02/pyside-tutorial-model-view-programming_22.html

    This doesn't use QAIM directly (it's just a list, and obviously, lists are simpler), but it should help you understand the general concepts.

    I'm happy to answer questions if you've got any, you can find me as w00t (or w00t_) on #qt on irc.freenode.net - there's plenty of other helpful folks there, too.

    ReplyDelete
  2. I'd also point out that later I noticed the way to override drawing for these things is through delegates. I think I still prefer GTK's cell renderer approach though but that is without having looked much at how to do a delegate

    ReplyDelete