视图中的选择操作
项视图类(Item View Class)中使用的选择模型提供了对项进行选择的一般描述。尽管用于提供选择的标准视图足以处理提供的项视图,但是选择模型允许创建专门的选择模型,以适合项模型和视图的要求。
有关在视图中选择的项目的信息存储在QItemSelectionModel类的实例中。它以独立于任何视图的方式维护单个模型中项的模型索引,由于模型上可以有多个视图,因此可以在视图之间共享选择,从而允许应用程序以一致的方式显示多个视图。
选择由选择范围组成。仅通过记录每个选定项目范围的开始和结束模型索引,就可以有效地维护大量项的选择信息。也可以通过使用一个以上的选择范围来描述选择,来构建多个不连续的选择项。
选择是选择模型所拥有的模型索引的集合。最近选择的项称为当前选择。即使在应用了某些选择命令之后,也可以修改此选择的效果。
当前项与选中项
在视图中,始终有一个当前项和一个选定项(这是两个独立的状态)。一个项可以是当前项,同时也是选中项。例如,当使用键盘导航时,视图负责确保始终存在当前项。
下面信息突出显示了当前项和选定项之间的区别:
- 当前项只能有一项; 选定项可以有多项。
- 当前项通过按键导航或鼠标单击来更改; 当用户与项进行交互时,选定项取决于几种预定义模式(例如,单选,多选等),可以设置或取消设置项的选定状态。
- 如果按下编辑键F2或双击该项,则当前项将被编辑(假设已启用编辑); 选定项可与锚点一起使用,以指定应选择或取消选择的范围(或两者的组合)。
- 当前项由焦点矩形标注指示; 选中的项由选择矩形标注指示。
在处理选择时,考虑使用QItemSelectionModel来记录一个项模型中所有项的选择状态是很有帮助的。一旦建立了选择模型,项的集合就可以选择,撤销选择,或者反向选择,而不需要知道哪些项已经被选择。任何时候都可以提取所有被选择项的索引,同时通过信号和槽机制,其他的部件也可以知道选择模型的变动。
使用选择模型
标准视图类提供了可以在大多数应用程序中使用的默认选择模型。可以使用视图的selectionModel()函数获得一个视图的选择模型,并通过setSelectionModel()在多个视图之间共享该模型,因此通常不需要构造新的选择模型。
指定一个模型,同时为QItemSelection指定一对模型索引,就可以创建一个选择。使用索引指向指定模型中的项,并解释成一个选中方块中左上角和右下角的项。要把选择应用于模型里的项,就必须把选择提交给选择模型。这可以通过多种方法实现,每一种方法在显示选择模型的选择都有不同的效果。
选择项
为了演示选择的一些基本特征,我们构建了一个共有32个项的自定义表格模型的实例,并且用一个表格视图显示它的数据。
model = TableModel(8, 4, self)table = QTableView(self)table.setModel(model)selectionModel = table.selectionModel()
获取视图的默认选择模型以备后用。我们不修改模型里的任何项,而是选择一些显示在视图左上角的项。要达到这个效果,我们要提取选择区域中左上角及右下角相应的模型索引:
topLeft = model.index(0, 0, QModelIndex())bottomRight = model.index(5, 2, QModelIndex())
要选择模型中的这些项,并看到视图上相应的变化,我们需要一个选择对象,并把它应用于选择模型:
selection = QItemSelection(topLeft, bottomRight)selectionModel.select(selection, QItemSelectionModel.Select)
通过使用一选择标识组合定义的命令就可以将选择应用于选择模型。在本例中,不管项的原来的状态是怎样,使用的标识会使选择对象记录的项都包含在选择模型中。选择的结果由视图显示。
可以使用由选择标志定义的各种操作来修改项的选择。
读取选择状态
可以通过selectedIndexes()函数读取储存在选择模型里的模型索引。selectedIndexes()函数返回一个未排序的模型索引列表,只要我们知道这些模型索引属于哪一个模型,就可以历遍这些选择项。
indexes = selectionModel.selectedIndexes() for index in indexes: text = '({},{})'.format(index.row(), index.column()) model.setData(index, text)
视图显示为下图的样子:
选择模型发射信号以表明选择的变动。这些信号通知其它部件关于项模型中整体选择及当前焦点项的改变。我们可以把selectionChanged()信号连接到一个槽,当选择改变时,就可以检查模型中被选择或撤销选择的项。这个槽要用到两个QItemSelection对象:一个包含新选择项对应的 索引列表,另一个包含新撤销选择项的对应索引列表。以下代码我们提供一个接受selectionChanged() 信号的槽,用一个字符串填满选择的项,并清除撤销选择项的内容。
def updateSelection(self, selected, deselected): items = selected.indexes() for index in items: text = '({},{})'.format(index.row(), index.column()) self.model.setData(index, text) items = deselected.indexes() for index in items: self.model.setData(index, '')
将currentChanged()信号连接到一个使用两个模型索引为参数的槽函数,我们就能够保持对当前焦点项的跟踪。这两个索引分别对应前一个焦点项和当前焦点项。下面的代码我们提供一个接受currentChanged()的槽,并使用QMainWindow的状态栏显示更新状态:
def changeCurrent(self, current, previous): #初始化时,previous.row() = -1,不显示信息 if int(previous.row()
更新选择
选择命令由QItemSelectionModel.SelectionFlag定义的一个选择标志组合来提供。当任何一个select()函数被调用时,每一个选择标识就会通知选择模型应怎样更新选择项的内部记录。最常用的标识就是Select,它指示选择模型把指定的项记录为选中的状态。 Toggle标识使选择模型转换指定项的状态,选择原来没有选中的项,撤销对当前选中项的选择。Deselect标识撤销对指定项的选择。
通过创建项选择并将其应用于选择模型可以更新选择模型中的各个项。在以下代码中,我们我们把第二个选择项应用于前面的表格模型,然后使用Toggle命令反转给定项目的选择状态。
toggleSelection = QItemSelection() 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的使用:
columnSelection = QItemSelection() topLeft = model.index(0, 1, QModelIndex()) bottomRight = model.index(0, 2, QModelIndex()) columnSelection.select(topLeft, bottomRight) selectionModel.select(columnSelection, QItemSelectionModel.Select | QItemSelectionModel.Columns) rowSelection = QItemSelection() topLeft = model.index(0, 1, QModelIndex()) bottomRight = model.index(1, 0, QModelIndex()) rowSelection.select(topLeft, bottomRight) selectionModel.select(rowSelection, QItemSelectionModel.Select | QItemSelectionModel.Rows)
虽然只指定了四个索引给选择模型,但Columns 和 Rows 选择标识的使用意味着选择了两列和两行。下图显示了这两个选择的结果:
选择模型的所有项
为了选择模型里的所有项,必须为模型的每一级建立一个选择,以覆盖该级的所有项。通过一个给定的父索引并提取左上角和右下角相应的索引来实现这个操作。
topLeft = model.index(0, 0, parent) bottomRight = model.index(model.rowCount(parent)-1, model.columnCount(parent), parent)
使用这些索引和模型构建一个选择,到时相应的项就会在选择模型中被选中。
selection = QItemSelection(topLeft, bottomRight) selectionModel.select(selection, QItemSelectionModel.Select)
对模型中的所有级都要执行这样的操作执行。对于顶级项,我们将以通常的方式定义父索引:
parent = QModelIndex()
对于分层模型,使用hasChildren() 函数可以确定一个指定的项是否是另一层次项的父项。
演示代码及效果
handlingselections.py演示了鼠标拖动时改变选择的效果,并在状态条上显示信息。完整代码如下:
import sysfrom PyQt5 import QtCore, QtGui, QtWidgetsfrom PyQt5.QtCore import Qt,QVariant, QItemSelection, QItemSelectionModel, QModelIndexfrom PyQt5.QtGui import QStandardItemModel, QStandardItemfrom PyQt5.QtWidgets import QApplication, QMainWindow, QTableView class TableModel(QStandardItemModel): def __init__(self, rows, columns, parent): super(TableModel, self).__init__(rows, columns, parent) hTitle=[] for col in range(columns): hTitle.append('Column{}'.format(col)) self.setHorizontalHeaderLabels(hTitle) vTitle=[] for row in range(rows): vTitle.append('Row{}'.format(row)) self.setVerticalHeaderLabels(vTitle) class DemoHandlingSelections(QMainWindow): def __init__(self, parent=None): super(DemoHandlingSelections, self).__init__(parent) # 设置窗口标题 self.setWindowTitle('实战PyQt5: Model-View框架演示') # 设置窗口大小 self.resize(460, 280) self.initUi() def initUi(self): model = TableModel(8, 4, self) table = QTableView(self) table.setModel(model) selectionModel = table.selectionModel() topLeft = model.index(0, 0, QModelIndex()) bottomRight = model.index(5, 2, QModelIndex()) selection = QItemSelection(topLeft, bottomRight) selectionModel.select(selection, QItemSelectionModel.Select) indexes = selectionModel.selectedIndexes() for index in indexes: text = '({},{})'.format(index.row(), index.column()) model.setData(index, text) selectionModel.selectionChanged.connect(self.updateSelection) selectionModel.currentChanged.connect(self.changeCurrent) self.model = model self.setCentralWidget(table) def updateSelection(self, selected, deselected): items = selected.indexes() for index in items: text = '({},{})'.format(index.row(), index.column()) self.model.setData(index, text) items = deselected.indexes() for index in items: self.model.setData(index, '') def changeCurrent(self, current, previous): #初始化时,previous.row() = -1,不显示信息 if int(previous.row()
运行结果如下图:
本文知识点
- 当前项与选中项的差别;
- 使用选择模型;
- 更新选择项;
- 组合选择;
- 选择所有项。
喜欢本文内容就关注,收藏,点赞,评论和转发。