Getting a QModelIndex from an item in a treeview/treemodel
-
I'm working on an application the uses a treeview (based on the Simple Tree Model (https://doc.qt.io/qt-5/qtwidgets-itemviews-simpletreemodel-example.html) to display categories and subcategories, such as:
[ root item ] | [ category ] --- [ subcategory ] | | [ category ] [ subcategory] -----[ subcategory ] | (etc)
It's working fine so far, but now I want to select the correct category/subcategory whenever an item in the app is displayed. This would presumably involve using QItemSelectionModel::select() and scrollTo(), but for that I need a QModelIndex. After some time, I came up with some code that works on the categories on the left of the above diagram, but fails on the subcategories. I would guess that this is due to the createIndex() functions using only rows and columns, which would seem to be meaningless in a tree view.
How do I get a correct QModelIndex from an item in a tree view? -
A model index consists of a row, column and, importantly, parent model index. The tree is built in the parent-child relationships of the model index of items in column zero.
root + Node A | + Node B | + Node C | + Node D + Node E + Node F ...
For first level nodes (A, E,F) the model index column will be zero and parent index will be the root index.
For second level nodes (B, C) the model index column will be zero and the parent index will be the model index of the first level node (A in this case).
For third level node D the model index column will be zero and the parent index will be the model index of C.If the names of nodes (Qt::DisplayRole), or some other role, are unique you can use QAbstractItemModel::match() with Qt::MatchRecursive to search for them. Something like:
QModelIndexList matches model->match( model->index(0, 0, QModelIndex()), // Node A in my example Qt::DisplayRole, QString("Node D"), 1, // stop at first match, or -1 to find all Qt::MatchFlags(Qt::MatchExactly|Qt::MatchRecursive)) ); if (matches.count() == 1) { QModelIndex ofInterest = matches.at(0); }
You can also navigate this tree recursively or iteratively by explicitly coding that yourself.
-
Thanks. I'll look into that when I get time.
What I have so far is to start with a path to the required item similar to a Unix path, so
/Category1/Category2/Category3...I re-implemented match() for that, and it correctly finds the item. Getting the QModelIndex, however, is currently the hard part: I used QAbstractItemModel::createIndex(row, column, parent), where "parent" is the parent of the item found. Using that index in QItemSelectionModel::select(), however, selects the wrong item, which seems to be an item in the parent's row with the same row number as the item I want.
Hmmm. That gives me an idea. -
Ok, it's working now.
I'd been under the impression that, to create an index to the item, I needed
categoryTreeItem *parent = item->parentItem(); QModelIndex index = model->createIndex(item->row(), column, parent);
On checking, however, that gives an index to the parent, but with the item's row number. No wonder it gets it wrong. When I use instead:
QModelIndex index = model->createIndex(item->row(), column, item);
it's fine.
Thanks for your help. I didn't realise what the problem was until I actually wrote it down.
-
What I gave you is how to find the QModelIndex of an item matching basic criteria from outside the model implementation, i.e. using the public QAbstractItemModel API.
QAbstractItemModel::createIndex() is protected and can only be used inside the model class implementation. How you go about finding a particular node in the internal representation of the model's data, and then map that to a model index for the public API is very dependent on your data structures. I am glad you found the solution matching the internals of that example.