4
votes

I'm still having a great deal of difficulty understanding QAbstractItemModel's representation of items. There are two methods that return QModelIndex items that just aren't making any sense to me.

QModelIndex QAbstractItemModel::index(int row, int column, const QModelIndex& index)

is the first. What is the view passing to this function? I index the specific tree item to have an index created for? If so, what's the point of the function? Why not just return index? What do row and column represent? Is index actually a parent node, with the function returning the specific index based on the number of rows beneath that parent? Is column just a no-op here?

When the passed row parameter is used, if ever, does row 0 refer to the index/parent node itself, or the first item below it?

Second,

QModelIndex QAbstractItemModel::parent(const QModelIndex& index) const

It seems like this method would return the direct parent of the passed index. I'm working with a data structure which is inherently tree-like, but stored in a flat array, with array elements containing information on tree depth, so a direct parent would always have a parent with depth 1 less than it's own depth. But what gets fed to createIndex in this case? What do the internal QModelIndex row, column, and internalPointer refer to? Given the array-based structure I'm using, what should the parent of array[0] be?

I've read over the Qt examples and documentation on these topics already, and can't seem to make any progress understanding how these classes work.

1

1 Answers

4
votes

QAbstractItemModel is called "abstract" for a reason. It does not define or enforce any particular way of storing the model items, it's completely up to you, the developer subclassing QAbstractItemModel and implementing the required interfaces in your subclass. What QAbstractItemModel does enforce is the interfaces which are used by views to communicate with the model. What it also kinda enforces is a "mental model" of how the data can be presented by the standard views.

You can think of QAbstractItemModel as a child born in the family of a tree and a table. Imagine a tree: you have a bunch of items, each item can have its own child items, they can have their child items as well and so on. Now imagine a table: you have a bunch of items which are arranged into a 2D-array which can be indexed by row and column. Now imagine that you have a tree all items in which can have multiple columns and thus look like rows of the table. So if you, say, expand some tree item known to have child items, you'd see a table - several child items arranged into several rows + each of them having several columns. I hope this mental model can help to make sense of otherwise confusing interfaces of QAbstractItemModel and of seemingly strange QModelIndex class.

QModelIndex, as the official documentation states, is used to locate items within the model. In the first approximation, it should be enough for the index to have a row, a column and parent to uniquely identify the item within such a tree-of-rows model. In reality QModelIndex allows you to do even more: you can put some pointer or some internal id into the index if that makes it easier for your own code to identify the model item within your own data structures used in your QAbstractItemModel subclass.

Thus, the methods you asked about do roughly the following:

  1. QAbstractItemModel::index takes row, column and parent of the supposed model item and returns QModelIndex corresponding to it. Later on the view might use that returned QModelIndex object to call, for example, the data method of your model in order to get some actual data to display. You might ask why won't the view just query the data by row, column and parent? Well, in theory it could but doing so via QModelIndex allows you, the developer of a particular model subclass, to employ such tricks as internal pointer or internal id.
  2. QAbstractItemModel::parent should return the parent item's QModelIndex for the item represented by the passed in QModelIndex. Or just invalid QModelIndex if that item doesn't have a parent. You might wonder now how on Earth can you create a QModelIndex which would know its parent if QAbstractItemModel::createIndex takes row, column and a pointer or id but not the parent QModelIndex? The answer is simple: QModelIndex returned by QAbstractItemModel::createIndex contains a link to the model object that has created it; so when the view asks QModelIndex of its parent, that call is propagated to the model that created that QModelIndex i.e. QAbstractItemModel::parent gets called. And it is your model now which should use either special indexing or internal pointer or internal id or maybe some crazy magic to identify the parent of the item pointed to by the passed in QModelIndex.