Tio Pepe
Tio Pepe

Reputation: 3099

QTreeView::scrollTo not working

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

Answers (8)

Stephen Punak
Stephen Punak

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

bleg
bleg

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

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

Carlton
Carlton

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

0x35
0x35

Reputation: 172

Everything was simple. Just change autoExpandDelay property from -1 to 0(for example).

ui->treeView->setAutoExpandDelay(0);

Upvotes: 1

user3249417
user3249417

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

Tio Pepe
Tio Pepe

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

Seth Anderson
Seth Anderson

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

Related Questions