PDA

View Full Version : QModelIndex value for the root of a tree model



Corny
19th March 2019, 14:55
I’m a bit confused about QModelIndex concerning the fact that you need to have a parent index to get an index value for an item in a tree model. That makes perfect sense if the item your obtaining an index value for, is the child of another item.

However if you need the index value for a top level item, it is unclear to me about how to obtain or create that value since the parent of top level items is the root and it doesn’t appear that you can obtain a QModelIndex for the root.

I’ve attempted to make a QModelIndex at row 0 column 0 but since there is no parent for that position, it doesn’t quite work. I have also used a null QModelIndex using QModelIndex() as the parent, and that too, does not appear to work.

At this point, I’m thinking I may be going about this all wrong, so if anyone can clear up my confusion concerning this issue, I would certainly appreciate it.

Thanks in advance
Corny

d_stranz
19th March 2019, 16:27
Have you looked at the Qt Simple Tree Model (https://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html) example? It pretty much explains everything you would need for a non-editable tree model. There is also an example for an editable tree model (https://doc.qt.io/qt-5/qtwidgets-itemviews-editabletreemodel-example.html).

anda_skoa
19th March 2019, 17:11
I’ve attempted to make a QModelIndex at row 0 column 0 but since there is no parent for that position, it doesn’t quite work. I have also used a null QModelIndex using QModelIndex() as the parent, and that too, does not appear to work.


The null/empty QModelndex represents the root of a tree model


QVariant rootDisplayRoleData = model->data(QModelIndex(), Qt::DisplayRole);


Toplevel nodes therefore have that as their parent


QModelIndex topLevelRow0Column0 = model->index(0, 0, QModelIndex());
QVariant displayRoleDataRow0Column0 = model->data(topLevelRow0Column0, Qt::DisplayRole);


Of course accessing the model's index API directly is rarely necessary, signals carry QModelIndex arguments and delegates also get told which index they are to work on.

Cheers,
_

Corny
19th March 2019, 20:21
Thank you for your prompt reply, and everything you mention is pertinent.

I may not have sufficiently explained the situation though, so to be a bit more precise, I have re-implemented a QAbstactItemModel and the index and setData functions. When I'm attempting to set the data, I need an index value of that item. To get the index value of a top level item, I need to send the row and column of the item and the index value of the item's parent (which technically is the root) to the index function. That obviously results in an invalid index value being returned.

Now the problem is sending the index value of the parent of the top level item. If I send QModelIndex() as the parent, the setData() function sees the value as invalid and simply returns a false. I considered just treating any invalid value as representing the root, but thought that might pretty much defeat the purpose.

Added after 6 minutes:

d_stranz, thanks for the suggestion. Yes, I have pored over those examples extensively and perhaps due to blindness, didn’t see a solution to my particular situation.

d_stranz
19th March 2019, 23:09
To get the index value of a top level item, I need to send the row and column of the item and the index value of the item's parent (which technically is the root) to the index function.

Maybe you are confused because you are thinking that a QAbstractItemModel of a tree is like the usual computer science implementation of a tree: that there is an actual node that represents the root of the tree, and that node holds pointers to the top level items in the tree, and so forth.

In a Qt tree model, there is no root node. It is an imaginary, null QModelIndex, such that the parent of any topmost node in the tree is QModelIndex(). If you call QModelIndex::parent() for any top-level QModelIndex, you get QModelIndex(), for which QModelIndex::isValid() returns false.

If you ask the model for QAbstractItemModel::rowCount() (with no argument; that is, the default argument QModelIndex()), the correct return value is the number of top-level items. columnCount() for the root should return 0.

When calling QAbstractItemModel::index() for a top-level item in the tree, you pass a null QModelIndex(). For all other levels of the tree, you pass the result of parent() for that item.

Remember that QAbstractItemModel really is an abstraction of a tree. There is nothing behind it, and you have to derive from it and wrap the interface around your own data structure. That's one of the reasons why the createIndex() protected method takes a quintptr as its last argument - you can use that to pass a pointer to the item in your internal data structure represented by the index in the Qt tree. You can retrieve that pointer using QModelIndex::internalId() and use it to go back to the appropriate entry in your internal data.


If I send QModelIndex() as the parent, the setData() function sees the value as invalid and simply returns a false.

Then you probably haven't implemented setData() correctly (or you are calling it incorrectly). setData() takes a QModelIndex that points to the actual item you want to change, not the parent of that item. If you do pass a null QModelIndex, the setData() should correctly return false because that would mean you are trying to change the imaginary root of the tree. However, if you pass a valid QModelIndex, and that index's parent is null, that's fine - it just means you are trying to change a top-level item.


Yes, I have pored over those examples extensively and perhaps due to blindness, didn’t see a solution to my particular situation.

Maybe give a bit more detail about the shape of your tree - what underlying data structure are you trying to represent?

Corny
19th March 2019, 23:27
Thank you!

That actually clears up some of the confusion I was experiencing. I'm going to attempt a different approach and see what effect that has. I'm fairly confident at this point that I have a better understanding of how this works.

Again, I want to thank you for clearing up my understanding.

anda_skoa
20th March 2019, 08:07
To get the index value of a top level item, I need to send the row and column of the item and the index value of the item's parent (which technically is the root) to the index function. That obviously results in an invalid index value being returned.

Not obvious at all, this should return a valid index if the model has such a cell:


if (model->rowCount(QModelIndex()) > 0 && model->columnCount(QModelIndex()) > 0) {
QModelIndex topLevelRow0Column0 = model->index(0, 0, QModelIndex());
Q_ASSERT(topLevelRow0Column0.isValid());
}

If the index is not valid despite this cell being part of the model, then there must a problem with the model's index() implementation.

The index() and the parent() method are the two tricky ones when implementing a tree model, so things can go wrong there.

See https://wiki.qt.io/Model_Test for a simple standard test harness for models and also check out https://www.kdab.com/development-resources/qt-tools/gammaray/ (has a model inspector plugin)

Cheers,
_