在 QTableView 中保持选中行

Keep row selected in QTableView

我正在开发一个程序来查看和编辑文件中的记录。它具有显示所有记录的 QTableView、搜索记录的 QLineEdit 以及显示所选记录详细信息的一些标签:

有一个 QAbstractTableModel class 保存数据,还有一个 QSortFilterProxyModel class 帮助过滤 QTableView.[=18 中的行=]

搜索和过滤工作正常。在搜索框中键入文本会立即过滤记录列表。但是有两件事我无法开始工作:

  1. 如果列表不为空,我希望其中一项始终selected/current
  2. 在搜索框中键入内容时,selected/current 项必须出现在视图中

例如,当我键入 "tesla" 时,列表将为空,因为没有项目匹配。但是一旦我退格到 "te","Forester" 就会匹配,我希望它被选中。第二个例子:当(启动程序后)我键入 "f" 时,列表缩小到 8 个项目并选择了 "Pacifica"。当我擦除 "f" 时,Pacifica 仍处于选中状态但不再位于列表的可见部分。

我已经在 Pastie 上发布了完整的源代码,这里有一些(希望如此)相关的片段。

void MainWindow::on_lineEditSearch_textChanged(const QString & text)
{
    itemProxy->setFilterFixedString(text);

    updateStatusBar();
}

void MainWindow::currentRowChangedSlot(QModelIndex const & current, QModelIndex const & /*previous*/)
{
    Car * car = 0;

    if (current.isValid())
    {
        QModelIndex sibling = current.sibling(current.row(), COLUMN_THIS);
        QVariant variant = itemProxy->data(sibling);
        car = static_cast<Car *> (variant.value<void *> ());
    }

    updateCarMake(car);
    updateCarModel(car);
}

MainWindow::MainWindow(QWidget * parent, CarItemModel * itemModel, CarSortFilterProxyModel * itemProxy) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    this->itemModel = itemModel;
    this->itemProxy = itemProxy;

    ui->setupUi(this);
    setupStatusBar();

    ui->tableView->setModel(itemProxy);

    ui->tableView->setColumnHidden(COLUMN_THIS, true);

    QItemSelectionModel * selectionModel = ui->tableView->selectionModel();

    connect(selectionModel, SIGNAL(currentRowChanged(QModelIndex const &, QModelIndex const &)),
            this, SLOT(currentRowChangedSlot(QModelIndex const &, QModelIndex const &)));

    connect(selectionModel, SIGNAL(selectionChanged(QItemSelection const &, QItemSelection const &)),
            this, SLOT(selectionChangedSlot(QItemSelection const &, QItemSelection const &)));

    ui->tableView->selectRow(0);

    ui->lineEditSearch->setFocus();

    updateStatusBar();
}

<widget class="QTableView" name="tableView">
 <property name="verticalScrollBarPolicy">
  <enum>Qt::ScrollBarAlwaysOn</enum>
 </property>
 <property name="selectionMode">
  <enum>QAbstractItemView::SingleSelection</enum>
 </property>
 <property name="selectionBehavior">
  <enum>QAbstractItemView::SelectRows</enum>
 </property>
</widget>

所以,我的问题是:如何确保某个项目始终处于选中状态并且位于列表的可见部分(当然,除非用户正在滚动)?

处理select离子变化信号并将其连接到您的自定义插槽:

 QSortFilterProxyModel *model = new QSortFilterProxyModel(this);
    ui->tableView->setModel(model);
    connect(ui->tableView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(selectionChanged(QItemSelection,QItemSelection)));

每次,您 table 更改当前 selection 应用应该 select 模型中的第一项:

void MainWindow::selectionChanged(QItemSelection selected, QItemSelection deselected) {
    // Use this 
    const int rows = ui->tableView->selectionModel()->selectedRows().size();
    // or 
    const int rowCount = selected.size();

    // Lets select the first item if there are some items availables
    if (rowCount < 1) {
        const int availableItems  = ui->tableView->model()->rowCount();
        if (availableItems > 0) {
            const QModelIndex index = ui->tableView->model()->index(0,0);
            ui->tableView->selectionModel()->select(index, QItemSelectionModel::ClearAndSelect);
        }
    }

}

通过这种方式,您应该至少拥有一项selected。

这是我对

所做的
  • 始终选择一个项目,并且
  • 搜索时显示所选项目

该解决方案甚至有点奇特,因为当从不可见项目变为一些可见项目时,将选择最近选择的项目,而不是仅选择第一个项目。

首先,我在MainWindowclass中添加了一个QModelIndex lastModelIndex;私有成员,并设置在SelectionChanged槽中。请注意,存储的是模型索引而不是代理索引。

void MainWindow::selectionChangedSlot(QItemSelection const & selected, QItemSelection const & /*deselected*/)
{
    if (selected.count() > 0)
    {
        QModelIndex index = selected.indexes().first();
        QModelIndex modelIndex = itemProxy->mapToSource(index);

        lastModelIndex = modelIndex;
    }
}

接下来,我添加了两个方法:ensureSelected() ...

void MainWindow::ensureSelected(QItemSelectionModel * selectionModel, int const proxyCount)
{
    if (selectionModel->hasSelection())
    {
        // an item is currently selected - don't have to do anything
    }
    else if (proxyCount == 1)
    {
        // no item is currently selected, but there is exactly one item in the list - select it
        QModelIndex proxyIndex = itemProxy->index(0, 0);

        selectionModel->setCurrentIndex(proxyIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
    }
    else if (proxyCount > 1)
    {
        // no item is currently selected, but there are several items in the list

        QModelIndex proxyIndex; // !isValid

        if (lastModelIndex.isValid())
        {
            // there's a most recently selected item - compute its index in the list
            proxyIndex = itemProxy->mapFromSource(lastModelIndex);
        }

        if (proxyIndex.isValid())
        {
            // the most recently selected item is in the list - select it
            proxyIndex =  proxyIndex.sibling(proxyIndex.row(), COLUMN_THIS);
        }
        else
        {
            // there's no most recently selected item or it is no longer in the list - select the first item
            proxyIndex = itemProxy->index(0, 0);
        }

        selectionModel->setCurrentIndex(proxyIndex, QItemSelectionModel::Select | QItemSelectionModel::Rows);
    }
    else
    {
        // There are no items in the list - cannot select anything.
    }
}

... 和 ensureVisible():

void MainWindow::ensureVisible(QItemSelectionModel * selectionModel)
{
    if (selectionModel->hasSelection())
    {
        const QModelIndex index = ui->tableView->currentIndex();

        ui->tableView->scrollTo(index);
        ui->tableView->selectRow(index.row());
        ui->tableView->scrollTo(index);
    }
}

虽然看起来很奇怪,但我必须调用 scrollTo() 两次,否则 tableView 不会滚动。

这些新方法是从 on_lineEditSearch_textChanged() 调用的,如下所示:

void MainWindow::on_lineEditSearch_textChanged(const QString & text)
{
    itemProxy->setFilterFixedString(text);

    QItemSelectionModel * selectionModel = ui->tableView->selectionModel();

    int modelCount = itemModel->rowCount(); // number of items in the model
    int proxyCount = itemProxy->rowCount(); // number of items in tableview

    ensureSelected(selectionModel, proxyCount);

    ensureVisible(selectionModel);

    updateStatusBar();
}

请在 Pastie 上找到更新的完整源代码。