PDA

View Full Version : Coding a model for nested data



dhalbert
16th August 2010, 20:45
Hi - I am building a simple model-view graphical editor in (Py)Qt, which was implemented once already in TkInter. I am a bit distressed by how complex it has been to implement the model in Qt.

Here is a simplified view of the program and its data: There are multiple Images, and multiple Points marked on each Image. The user selects one of the Images, and then views and edits its Points.

There is a QListView of Image names, annotated to show if an Image has been edited but not yet saved. A separate QListView lists the Points in the current image, displaying their names and locations. The current Image and its Points are edited in a QGraphicsView. A QTreeView might also be used in the future to show the list of Images and Points simultaneously.

The Points are actually held in a PointList class so that edits can be undone.

So the overall nested data structure is:


Image: filename, name, etc.
PointList: edit-in-progress, old-point-values
Point: name, x, y
Point
Point
…
Image
PointList
Point
…
…
I have implemented this as a subclass of QAbstractItemModel. It is a facade on my real nested data structure above. The top-level rows of the model are the Images, and each Image row has a set of child rows, one row per Point. I will implement a proxy model as well to present only the currently selected Image and its Points to the appropriate views.

The QAbstractItemModel coding was fairly painful, and is much more complex and fragile than the original data structure. There is a lot of checking whether a QModelIndex has a parent or not and a lot of case statement switching on column numbers and roles. I feel like I am emulating abstract data types in FORTRAN.

Actually, I believe that the pain of coding the model is because I had to flatten my three abstractions (Image, PointList, Point) into a single abstraction, my QAbstractItemModel.

Should I rethink how to do this? As an alternative, I could map my original abstractions onto two or three different nested models. The downside is that I have to connect them together explicitly with signals to make the model/view auto-updating work properly. But nested models is not a paradigm I have seen used much in Qt.

If you have faced a similar problem, I would be interested to hear how you solved it and how satisfied you were with the resulting implementation.

Thanks,
Dan

tbscope
17th August 2010, 06:38
I'm not sure I understand it all correctly.

You don't have to do anything about your model structure. That can be anything you want or how you like to create it.
The only thing that models need to give to views are correct indexes and correct data. That would be the index(...) and data(...) functions.

So, basically, you can reuse your existing structure and build the model around it.

However, it doesn't matter how you look at it, at a certain point, the model does need to know about its items and how to add, delete, move, ... items in the model (if that's necessary). And this means you need to implement the functions of the model to do that. And the more complex the model, the more work it will take.

If I may make a suggestion: use a database and a standard QSql...Model class. It will make your work a lot easier.

dhalbert
17th August 2010, 13:33
Thanks. I do understood I don't have to change my own underlying data structures. What I'm being grouchy about is that the implementation of data(), index(), setData(), parent(), etc. was fairly tortured. I'm mapping (row, column, parent) QModelIndexes to and from my own hierarchical data structure, and I don't find that mapping very natural. For instance, whether or not there's a parent determines whether I'm in a top-level Image or one of the Point rows. It's a rather weird kind of run-time type determination. Then the internalPointer() helps me find where I really am.

My Qt model has flattened my natural hierarchy. An alternative way, which would make the mapping more natural, would be to use two Qt model classes: one for the Images, and one of the sets of Points. But then I need to set up signal/slot connections between the models so that a "dataChanged()" signal gets propagated from one model to another and onward to the views that are connected to that model. I am wondering if someone has used multiple connected models in this way and found it more natural.

Your point about the QSql model is interesting, because an RDBMS is naturally flatter, and the mapping may be easier. In my case, the objects are actually stored in an RDBMS, but I use the Storm ORM for that, so it's at a different level in the program.

The QModelIndex paradigm is very general, and maps onto many complex data structures, but at the cost of a lot of conceptual translation. I could think of variants such as (row, column, depth), or (row, column, object-type, object-pointer), etc. that might be more natural. The Qt 4 designers may have looked at some of these, and maybe they don't work out in the long run.