Search code examples
c++qtqtreeview

How to select previous row in a QTreeView?


I have a QTreeView with single column and multiple row (lets say 5 rows). What I want to achieve is that if I select a row, I want to re-select previous row on some condition.

Here is the code I have:

MyWidget.h

#ifndef MYWIDGET_H
#define MYWIDGET_H

#include <QWidget>
#include <QTreeView>
#include <QStandardItemModel>
#include <QStandardItem>
#include <QModelIndex>
#include <QHBoxLayout>

class MyWidget : public QWidget
{
    Q_OBJECT

public:
    MyWidget(QWidget *parent = 0);
    ~MyWidget();

private slots:
    void _OnTreeViewCurrentRowChanged(const QModelIndex &rcqmiCurrIndex,
        const QModelIndex &rcqmiPrevIndex);

private:
    QHBoxLayout *_pLayout;

    QTreeView *_pTreeView;
    QStandardItemModel *_pStandardItemModel;
    QStandardItem *_pStandardItem;
};

#endif

MyWidget.cpp

#include "MyWidget.h"

static bool bCondition = false;

MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
    _pLayout = new QHBoxLayout(this);

    _pTreeView = new QTreeView(this);
    _pStandardItemModel = new QStandardItemModel(_pTreeView);
    _pStandardItem = new QStandardItem("Column A");

    _pLayout->addWidget(_pTreeView);

    _pStandardItemModel->setColumnCount(1);
    _pStandardItemModel->setHorizontalHeaderItem(0, _pStandardItem);

    _pTreeView->setModel(_pStandardItemModel);
    _pTreeView->setSelectionBehavior(QAbstractItemView::SelectRows);
    _pTreeView->setSelectionMode(QAbstractItemView::SingleSelection);
    _pTreeView->setEditTriggers(QAbstractItemView::NoEditTriggers);

    // add rows
    _pTreeView->selectionModel()->blockSignals(true);

    for (int i = 0; i < 5; ++i)
    {
        QStandardItem *pStandardItem = new QStandardItem(
            QString("Row: %1").arg(i + 1));

        _pStandardItemModel->appendRow(pStandardItem);
    }

    _pTreeView->selectionModel()->blockSignals(false);

    // update view
    _pTreeView->viewport()->update();

    connect(_pTreeView->selectionModel(),
        SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)),
        this, SLOT(_OnTreeViewCurrentRowChanged(const QModelIndex &,
        const QModelIndex &)));
}

MyWidget::~MyWidget()
{

}

void MyWidget::_OnTreeViewCurrentRowChanged(
    const QModelIndex &rcqmiCurrIndex, const QModelIndex &rcqmiPrevIndex)
{
    if (bCondition) // some condition
    {
        _pTreeView->selectionModel()->blockSignals(true);

        // select previous index
        _pTreeView->selectionModel()->setCurrentIndex(
            rcqmiPrevIndex, QItemSelectionModel::SelectCurrent);

        _pTreeView->selectionModel()->blockSignals(false);

        // update view
        _pTreeView->viewport()->update();
    }

    bCondition = !bCondition;
}

In _OnTreeViewCurrentRowChanged(...), I can see that "selection" of tree view's selection model is updated with QItemSelectionModel::setCurrentIndex(rcqmiPrevIndex, QItemSelectionModel::SelectCurrent); but the tree view's row selection is NOT updated, still the rcqmiCurrIndex is selected.

What am I missing here?

Any help is much appreciated. Thanks in advance.


Solution

  • Use the QItemSelectionModel::select() method.

    _pTreeView->selectionModel()->select(rcqmiPrevIndex, QItemSelectionModel::ClearAndSelect);
    

    EDIT

    There is probably another problem: The _OnTreeViewCurrentRowChanged(...) is probably a slot invoked when the current row is changed, right? That means you are changing the selection twice in single event. This is not a good idea. Use the QTimer to perform the selection in the next event loop cycle:

    // lambda
    auto func = [this, rcqmiPrevIndex](){
        _pTreeView->selectionModel()->select(rcqmiPrevIndex, QItemSelectionModel::ClearAndSelect);
    }
    
    // A QTimer with a timeout interval of 0 will time out as soon as all the events in the window system's event queue have been processed.
    QTimer::singleShot(0, func);