【Qt笔记】视图选择

选择是视图中经常使用的一个操做。在列表、树或者表格中,经过鼠标点击能够选中某一项,被选中项会变成高亮或者反色。在 Qt 中,选择也是使用了一种模型。在 model/view 架构中,这种选择模型提供了一种更通用的对选择操做的描述。对于通常应用而言,Qt 内置的选择模型已经足够,可是,Qt 仍是容许你建立本身的选择模型,来实现一些特殊的操做。架构

 

Qt 使用QItemSelectionModel类获取视图中项目的选择状况。这个模型保持有项目的索引,而且独立于任何视图。这意味着,咱们可让不一样的视图共享同一个选择模型,历来达到一种同步操做的目的。选择由选择区域组成。模型只将选区的开始和结束的索引位置记录下来,以保证对于很大的选区也有很好的性能。非连续选区则由多个连续选择组成。函数

选择会直接应用于选择模型所维护的那些被选中的索引上面。最新的选择就是当前选择。这意味着,即使界面上没有显示有任何项目被选择,若是经过某些命令对选区进行操做,一样会有做用。性能

在视图中,始终存在一个当前项和被选择项(即使从界面上看不到有任何选择)。与一般所想的不一样,当前项和选择项是相互独立的两个状态。一个项目能够便是当前项又是选择项。下表是当前项和选择项的区别:code

当前项 选择项
只能有一个当前项。 能够有多个选择项。
使用键盘或者鼠标点击能够改变当前项。 选择项使用两种状态:选择和未选择,这取决于项目以前的状态和其它一些设置,例如,单选或多选。只有在用户进行交互的时候,这种状态才会发生改变。
当前项可使用 F2 或者鼠标双击进行编辑(前提是程序容许)。 当前项能够结合另一个锚点指定被选择或者去除选择的一块选区(或两者的结合)。
当前项一般会有一个焦点框进行标识。 选择项使用选区颜色进行标识。

在处理选择的时候,咱们能够将QItemSelectionModel当成数据模型中全部数据项的选择状态的一个记录。一旦选择模型建立好,这些数据项就能够在不知道哪些项被选择的状况下进行选择、取消选择或者改变选择状态的操做。全部被选择项的索引都在可随时更改,其它组件也能够经过信号槽机制修改这些选择的信息。对象

标准视图类(QListViewQTreeView以及QTableView)已经提供了默认的选择模型,足以知足大多数应用程序的需求。某一个视图的选择模型能够经过selectionModel()函数获取,而后使用setSelectionModel()提供给其它视图共享,所以,通常没有必要新建选择模型。索引

若是须要建立一个选区,咱们须要指定一个模型以及一对索引,使用这些数据建立一个QItemSelection对象。这两个索引应该指向给定的模型中的数据,而且做为一个块状选区的左上角和右下角的索引。为了将选区应用到模型上,须要将选区提交到选择模型。这种操做有多种实现,对于现有选择模型有着不一样的影响。文档

下面咱们来看一些代码片断。首选构建一个总数 32 个数据项的表格模型,而后将其设置为一个表格视图的数据:字符串

QTableWidget tableWidget(8, 4);

QItemSelectionModel *selectionModel = tableWidget.selectionModel();

在代码的最后,咱们得到QTableView的选择模型,以备之后使用。如今,咱们没有修改模型中的数据,而是选择表格左上角的一些单元格。下面咱们来看看代码如何实现:get

QModelIndex topLeft = tableWidget.model()->index(0, 0, QModelIndex());
QModelIndex bottomRight = tableWidget.model()->index(5, 2, QModelIndex());

接下来,咱们将得到的两个索引定义为选区。为达这一目的,咱们首先构造一个QItemSelection对象,而后将其赋值给咱们获取的选择模型:同步

QItemSelection selection(topLeft, bottomRight);
selectionModel->select(selection, QItemSelectionModel::Select);

正如前面咱们说的,首先利用左上角和右下角的坐标构建一个QItemSelection对象,而后将这个对象设置为选择模型的选择区。select()函数的第一个参数就是须要选择的选区,第二个参数是选区的标志位。Qt 提供了不少不一样的操做,能够参考下QItemSelectionModel::SelectionFlags的文档。在本例中,咱们使用了QItemSelectionModel::Select,这意味着选区中所包含的全部单元格都会被选择。

下面就是咱们的运行结果:

如今咱们知道如何设置选区。下面来看看如何获取选区。获取选区须要使用selectedIndexes()函数。该函数返回一个无序列表。咱们能够经过遍历这个列表得到哪些被选择:

QModelIndexList indexes = selectionModel->selectedIndexes();
QModelIndex index;

foreach(index, indexes) {
    QString text = QString("(%1,%2)").arg(index.row()).arg(index.column());
    model->setData(index, text);
}

在选择发生更改时,选择模型会发出信号。咱们能够链接selectionChanged()信号,在选区改变时检查哪一个项目发生了变化。这个信号有两个参数:第一个是新选择的项目,第二个是刚刚被取消选择的项目。在下面的示例中,咱们经过selectionChanged()信号,将全部新选择的项目填充字符串,将全部被取消选择的部分清空:

void MainWindow::updateSelection(const QItemSelection &selected,
                                 const QItemSelection &deselected)
{
    QModelIndex index;
    QModelIndexList items = selected.indexes();

    foreach (index, items) {
        QString text = QString("(%1,%2)").arg(index.row()).arg(index.column());
        model->setData(index, text);
    }

    items = deselected.indexes();

    foreach (index, items) {
        model->setData(index, "");
    }
 }

经过currentChanged(),咱们能够追踪当前有焦点的项。同selectionChanged()信号相似,这个信号也有两个参数:第一个是新的当前项,第二个是上一个当前项。下面的代码则是该信号的使用:

void MainWindow::changeCurrent(const QModelIndex &current,
                               const QModelIndex &previous)
{
    statusBar()->showMessage(
        tr("Moved from (%1,%2) to (%3,%4)")
            .arg(previous.row()).arg(previous.column())
            .arg(current.row()).arg(current.column()));
}

这些信号能够用来监控选区的改变。若是你还要直接更新选区,咱们还有另外的方法。

一样是利用前面所说的QItemSelectionModel::SelectionFlag,咱们能够对选区进行组合操做。还记得咱们在前面的select()函数中使用过的第二个参数吗?当咱们替换这个参数,就能够得到不一样的组合方式。最经常使用的就是QItemSelectionModel::Select,它的做用是将全部指定的选区都选择上。QItemSelectionModel::Toggle则是一种取反的操做:若是指定的部分原来已经被选择,则取消选择,不然则选择上。QItemSelectionModel::Deselect则是取消指定的已选择的部分。在下面的例子中,咱们使用QItemSelectionModel::Toggle对前面的示例做进一步的操做:

QItemSelection toggleSelection;

topLeft = tableWidget.model()->index(2, 1, QModelIndex());
bottomRight = tableWidget.model()->index(7, 3, QModelIndex());
toggleSelection.select(topLeft, bottomRight);

selectionModel->select(toggleSelection, QItemSelectionModel::Toggle);

运行结果将以下所示:

默认状况下,选择操做会只会影响到指定的模型索引。可是,咱们也能够改变这一设置。例如,只选择整行或者整列:

QItemSelection columnSelection;

topLeft = model->index(0, 1, QModelIndex());
bottomRight = model->index(0, 2, QModelIndex());

columnSelection.select(topLeft, bottomRight);

selectionModel->select(columnSelection,
                       QItemSelectionModel::Select | QItemSelectionModel::Columns);

QItemSelection rowSelection;

topLeft = model->index(0, 0, QModelIndex());
bottomRight = model->index(1, 0, QModelIndex());

rowSelection.select(topLeft, bottomRight);

selectionModel->select(rowSelection,
                       QItemSelectionModel::Select | QItemSelectionModel::Rows);

上面的代码,咱们依然使用两个索引设置了一个区域,可是,在选择的使用咱们使用了QItemSelectionModel::RowsQItemSelectionModel::Columns这两个参数,所以只会选择这两个区域中指定的行或者列:

使用QItemSelectionModel::Current参数能够将当前选区替换为新的选区;使用QItemSelectionModel::Clear则会将原来已有的选区所有取消。为了进行全选,咱们能够设置选区为左上角和右下角两个索引:

QModelIndex topLeft = model->index(0, 0, parent);
QModelIndex bottomRight = model->index(model->rowCount(parent)-1,
model->columnCount(parent)-1, parent);

QItemSelection selection(topLeft, bottomRight);
selectionModel->select(selection, QItemSelectionModel::Select);
相关文章
相关标签/搜索