在条目视图类中使用的选择模型提供了很多相比于 Qt 3 中的选择模型的改进。它提供一个基于模型/视图架构的对选择的更加通用的描述。尽管标准的用来处理选择的类已经足够应对所提供的条目视图类,选择模型仍然允许你来创建特殊的选择模型,以满足你自己的条目模型和视图的要求。
关于一个视图中被选中的条目的信息是保存在一个QItemSelectionModel类的实例中。这个类维护一个单个模型中的条目的模型索引,独立于任何视图。由于一个模型上可以有很多视图,所以可以在视图之间共享选择,以允许程序以一个统一的方式来显示多个视图。
选择是由选择范围组成的。这些东西通过仅仅记录一个条目选择范围中的开始和结束条目的模型索引来有效地维护大量选中条目的信息。非连续的条目的选择是通过使用多于一个选择范围来构造以描述该选择的。
选择被应用到一个选择模型所持有的模型索引的集合上。最近应用的条目的选择就是当前的选择。甚至在这个选择被应用之后,也可以通过使用特定种类的选择命令来修改它的效果。这些在这一节的后面描述。
在一个视图中,问题有一个当前条目和一个选中的条目--两种独立的状态。一个条目可以同时是当前条目和选中条目。视图有责任保证问题有一个当前条目,因为,例如,键盘的导航需要有一个当前条目。
下面的表格突出了当前条目与选中条目之间的区别。
当前条目 | 选中条目 |
只能有一个当前条目。 | 可以有多个选中条目。 |
键盘导航或鼠标点击会改变当前条目。 | 当用户与条目交互时,依据一些预先定义的模式,例如单选、多选,条目的选中状态会被设置或者取消。 |
如果编辑键,F2,被按下,或者条目被双击,则当前条目会被编辑(在允许编辑的情况下)。 | 当前条目可以与一个锚标一起使用,以指定一个应当被选中或取消选中(或者是两者的组合)的范围。 |
当前条目是由焦点框来指示的。 | 选中条目是由选择框来指示的。 |
在处理选择的时候,将QItemSelectionModel看成是一个条目模型中所有条目的选择状态的记录通常是非常有用的。一旦一个选择模型被建立起来,条目的集合就可以被选中、取消选中或者切换选中状态,而不需要知道哪些条目是已经被选中的。所有选中的条目的索引可以在任何时候获取,而且其它组件可以通过信号/信号槽机制来得知在选择模型上发生的变化。
标准的视图类提供默认的可以在大部分应用中使用的选择模型。属于个一视图的选择模型可以通过该视图的selectionModel()函数来获取,并且可以通过setSelectionModel()来在多个视图之间共享,所以一般地不需要构造新的选择模型。
一个选择是通过向一个QItemSelection指定一个模型和一对主要的模型索引来创建的。这个使用两个索引来引用给出的模型中的条目,并且将它们解释成一个选中的条目块中的左上角和右下角的条目。要将选择应用到一个模型中的条目,需要将选择提交给一个选择模型;这可以用很多方法实现,每种都会对选择模型中已经有的选择产生不同的效果。
为了展示选择的一些主要特性,我们构造一个有32个条目的自定义表格模型的实例,并且在之上打开一个视图:
TableModel *model = new TableModel(8, 4, &app);
QTableView *table = new QTableView(0);
table->setModel(model);
QItemSelectionModel *selectionModel = table->selectionModel();
我们获取表格视图的默认选择,以便于以后使用。我们不修改模型中的任何条目,而是选中视图在表格的左上角显示的一些条目。要实现这一点,我们需要获取对就于将被选中的区域中的左上角和右下角的条目的模型索引:
QModelIndex topLeft;
QModelIndex bottomRight;
topLeft = model->index(0, 0, QModelIndex());
bottomRight = model->index(5, 2, QModelIndex());
要选中模型中的这些条目,并且看到表格视图中对应的变化,我们需要构造一个选择对象并且将它应用到选择模型中:
QItemSelection selection(topLeft, bottomRight);
selectionModel->select(selection, 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);
}
上面的代码使用Qt 的方便的foreach 关键字来遍历并且修改由选择模型所返回的索引对应的条目。
选择模型发射信息来指示选择中的变化。这些信息通知其它组件关于条目模型中的选择本身的变化以及当前获得焦点的条目的变化。我们可以将selectionChanged()信号连接到一个信号槽,并且在选择发生改变的时候检查模型中被选中或取消选中的条目。这个信号槽是带着两个QItemSelection对象来调用的:一个包含着一个对应着新近被选中的条目的索引组成的列表;另一个包含着一个对应着新近被取消选中的条目的索引组成的列表。
在下面的代码中,我们提供一个信号槽,它接收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()信号连接到一个信号槽来跟踪当前获得焦点的条目,这个信号槽将被带着两个模型索引来调用。这两个模型索引对应着上次获得焦点的条目,以及当前获得焦点的条目。
在下面的代码中,我们提供一个信号槽,它接收currentChanged()信号,并且使用这个信息来更新一个QMainWindow的状态条:
void MainWindow::changeCurrent(const QModelIndex ¤t,
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()函数被调用的时候告诉选择模型该如何更新它内部的关于选中的条目的记录。最常用的标志包括:Select标志,指示选择模型将指定的条目记录为选中;Toggle标志,引起选择模型反转指定条目的选择状态,选中任何未选中的条目,取消选中任何当前已经选中的条目;Deselect标志,取消选中所有指定的条目。
选择模型中的单个的条目是通过创建一个条目选择,再将它们应用到选择模型中来更新的。在下面的代码中,我们使用命令来反转给定的条目的选择状态,将第二个条目选择情况应用到上面展示的表格模型中。
QItemSelection toggleSelection;
topLeft = model->index(2, 1, QModelIndex());
bottomRight = model->index(7, 3, QModelIndex());
toggleSelection.select(topLeft, bottomRight);
selectionModel->select(toggleSelection, QItemSelectionModel::Toggle);
这个操作的结果显示在表格视图中,它提供了一个方便的将我们做到的事情可视化的方式:
默认情况下,选择命令只在模型索引指定的单个条目上操作。然而,用来描述选择命令的标志可以与附加的标志组合起来,以改变整个行和列。例如,如果你在调用select()的时候只指定一个索引,但是使用一个由Select和Rows组合而成的命令,那么包含指定的条目的整个行都会被选中。下面的代码展示了Rows和Columns标志的用法:
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);
尽管只有4个索引被指定到选择模型中,但是Columns和Rows选择标志的使用意味着有2行和2列被选中。下面的图片展示了这2个选择的结果:
在这个示例模型上执行的命令都累积到模型中的选择条目上了。还可以清除选择,或者使用一个新的选择来替换当前的选择。
要使用一个新的选择来替换当前的选择,就将Current标志与其它选择标志组合。一个使用这个标志的命令指示选择模型来用在调用select()时指定的那些模型索引替换当前的模型索引集合。要在你开始添加新的条目之前清除所有选中的条目,就将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);
这种操作需要对模型中所有的层次进行。对于顶级条目,我们会以这个常用的方法来宝父索引:
QModelIndex parent = QModelIndex();
对于层次型的模型,hasChildren()函数被用来确定是否有任何条目是另一个条目层次的父对象。
HxLauncher: Launch Android applications by voice commands