Reputation: 3099
Qt 4.8
I have a QTreeView
based class with an asociated QAbstractItemModel
based class. If I reload the model with new information I want to expand/scroll the tree to a previous selected item.
Both clases, tree view and model are correctly created and connected using QTreeView::setSelectionModel(...)
working everything properly.
After reloading the model, I get a valid index to the previous selected item and I scrollTo it:
myTreeView->scrollTo(index);
but the tree is not expanded. However, if I expand the tree manually, the item is really selected.
Tree view is initialized in contruct with:
header()->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
header()->setStretchLastSection(false);
header()->setResizeMode(0, QHeaderView::ResizeToContents);
Any idea about expanding the tree to the selection?
Upvotes: 5
Views: 8205
Reputation: 11
I've encountered this exact issue multiple times in the past, and it has always boiled down to an issue in the model as mentioned in bleg's answer.
Make sure your abstract model item has something like the following:
if(parent == nullptr) {
return _parent->_children.indexOf(this);
}
else {
return _model->rootItems().indexOf(this);
}
Upvotes: 0
Reputation: 301
Recently I struggled with same problem. It's most likely a bug in your model class implementation.
In my case the row()
method (that is supposed to return the index of the index under its parent) was not implemented correctly.
Qt doesn't complain about that, and even selects the index (if you expand manually you will notice).
So, just go through the model code and hunt for bugs in row()
and parent()
methods etc.
Upvotes: 0
Reputation: 1
You need to wait for the event loop to be idle. In PySide it's best to run
QtCore.QCoreApplication.processEvents()
call to ensure that all pending events, including rendering and expansion events, are processed before scrolling. This is better solution that using QTimer with fixed delay, as it makes the scrolling action occur as soon as the QTreeView is ready.
Upvotes: 0
Reputation: 4297
You may be calling scrollTo
before the tree view has finished reacting to the changes in current index and which indices are expanded/collapsed. A possible solution may be to delay the call to scrollTo
by connecting it to a single-shot timer like this:
QTimer::singleShot(0, [this]{scrollTo(index);});
Using the timer will delay the call until control is passed back to the event queue.
Upvotes: 0
Reputation: 172
Everything was simple. Just change autoExpandDelay property from -1 to 0(for example).
ui->treeView->setAutoExpandDelay(0);
Upvotes: 1
Reputation: 41
I just dealt with similar situation, setting model index via setModelIndex (which internally ends up with scrollTo) worked OK for one of my models, but badly for the other one.
When I forcefully expanded all level 1 items, the scrollTo worked just as described above (calling expandAll
suffices).
The reason was a bug in my model class in:
QModelIndex MyBadModel::parent(const QModelIndex& index) const
and as I fixed that, things got normal there too. The bug was such that internalId
of parent model index was not the same as when this same model index (for parent) is calculated "from other direction", therefore in this model index (returned by parent
method) could not be found in the list of visual indices.
Upvotes: 4
Reputation: 3099
Even QTreeView::scrollTo
documentation says:
Scroll the contents of the tree view until the given model item index is
visible. The hint parameter specifies more precisely where the item should
be located after the operation. If any of the parents of the model item
are collapsed, they will be expanded to ensure that the model item is visible.
That is not really true (I think)
If solved the problem expanding all previous tree levels manually:
// This slot is invoqued from model using last selected item
void MyTreeWidget::ItemSelectedManually(const QModelIndex & ar_index)
{
std::vector<std::pair<int, int> > indexes;
// first of all, I save all item "offsets" relative to its parent
QModelIndex indexAbobe = ar_index.parent();
while (indexAbobe.isValid())
{
indexes.push_back(std::make_pair(indexAbobe.row(), indexAbobe.column()));
indexAbobe = indexAbobe.parent();
}
// now, select actual selection model
auto model = _viewer.selectionModel()->model();
// get root item
QModelIndex index = model->index(0, 0, QModelIndex());
if (index.isValid())
{
// now, expand all items below
for (auto it = indexes.rbegin(); it != indexes.rend() && index.isValid(); ++it)
{
auto row = (*it).first;
auto colum = (*it).second;
_viewer.setExpanded(index, true);
// and get a new item relative to parent
index = model->index(row, colum, index);
}
}
// finally, scroll to real item, after expanding everything above.
_viewer.scrollTo(ar_index);
}
Upvotes: 5
Reputation: 150
QTreeView::scrollTo
should expand the hierarchy appropriately.
It's likely that your QModelIndex
object is being invalidated when the model is updated (and perhaps still selecting the correct row because the row information is still valid though the parentage is not, don't ask me how those internals work). From the QModelIndex
documentation:
Note: Model indexes should be used immediately and then discarded. You should not rely on indexes to remain valid after calling model functions that change the structure of the model or delete items. If you need to keep a model index over time use a QPersistentModelIndex.
You can certainly look into the QPersistentModelIndex
object, but like it says in its documentation:
It is good practice to check that persistent model indexes are valid before using them.
Otherwise, you can always query for that item again after the model refresh.
Upvotes: 0