选择模型 QItemSelectionModel
项视图类(Item View Class)中使用的选择模型提供了对项进行选择的一般描述。尽管用于提供选择的标准视图足以处理提供的项视图,但是选择模型允许创建专门的选择模型,以适合项模型和视图的要求。
有关在视图中选择的项目的信息存储在QItemSelectionModel类的实例中。它以独立于任何视图的方式维护单个模型中项的模型索引,由于模型上可以有多个视图,因此可以在视图之间共享选择,从而允许应用程序以一致的方式显示多个视图。
选择由选择范围组成。仅通过记录每个选定项目范围的开始和结束模型索引,就可以有效地维护大量项的选择信息。也可以通过使用一个以上的选择范围来描述选择,来构建多个不连续的选择项。
选择是选择模型所拥有的模型索引的集合。最近选择的项称为当前选择。即使在应用了某些选择命令之后,也可以修改此选择的效果。
在列表、树和表格视图中,如要对数据项进行操作,需要先选中数据项,被选中的数据项高亮或反色显示。
在 PySide6 中被选中的数据项记录在选择模型 QItemSelectionModel中,如果多个视图控件同时关联到一个数据模型,选择模型可以记录多个视图控件中被选中的数据项,形成数据选择集 QItemSelection。视图控件有自已默认的选择模型,般可以满足用户的需要;另外可以单独创建新的选择模型,以实现特殊目的。
视图控件都有 setSelectionModel(QItemSelectionModel)方法和 selectionModel()方法,用于设置视图控件的选择模型和获取选择模型。
用selectionModel()方法获取某一个视图控件的选择模型后,可以使用setSelectionModel()方法提供给其他视图共享选择模型因此一般没有必要新建选择模型
用QItemSelectionModel 创建选择模型的方法如下
from PySide6.QtCore import QItemSelectionModel
QItemSelectionModel(model: PySide6.QtCore.QAbstractItemModel,parent: PySide6.QtCore.QObject)-> None
QItemSelectionModel(model: Union[PySide6.QtCore.QAbstractItemModel,NoneType]= None)-> None
选择模型 QItemSelectionModel 的常用方法
方法总览
-
用selection()方法可以获取项的选择集 QItemSelection;
-
用select(index; QModelIndex,command:QItemSelectionModel,SelectionFlags)方法可以往选择集中添加内容,或从选择集中移除选择,其中command是QItemSelectionModel,SelectionFlags 的枚举值,可取的值如表示。
QItemSelectionModel.SelectionFlags 的取值 说明 QItemSelectionModel,NoUpdate 选择集没有变化,不会进行选择 QItemSelectionModel.Clear 清空选择集 QItemSelectionModel,Select 选择所有指定的项 QItemSelectionModel.Deselect 取消选择所有指定的项 QItemSelectionModel.Toggle 根据项的状态选择或不选择 QItemSelectionModel.Current 更新当前的选择 QItemSelectionModel.Rows 选择整行 QItemSelectionModel.Columns 选择整列 QItemSelectionModel.SelectCurrent Select | Current QItemSelectionModel.ToggleCurrent Toggle | Current QItemSelectionModel.ClearAndSelect Clear | Select
QtemSelectionModel的方法及参数类型 | 返回值的类型 | 说明 |
---|---|---|
[slot]clear() | None | 清空选择模型并发送selectionChanged()和currentChanged()信号 |
[slot]reset() | None | 清空选择模型,不发送信号 |
[slot]clearCurrentIndex() | None | 清空当前的数据索引模型并发送 currentChanged()信号 |
[slot]clearSelection() | None | 清空选择模型并发送 selectionChanged()信号 |
[slot]setCurrentIndex(index: QModelIndex,command: QItemSelectionModel.SelectionFlags) | None | 设置当前的项,并发送currentChanged()信号 |
[slot]select(index: QModelIndex,command: QItemSelectionModel,SelectionFlags) | None | 选择项,并发送selectionChanged()信号 |
[slot]select(selection:QItemSelection,command:QItemSelectionModel.SelectionFlags) | None | 选择项,并发送selectionChanged()信号 |
rowIntersectsSelection(row: int,parent: QModelIndex) | bool | 如果选择的数据项与parent的子数据项的 指定行有交集,则返回True |
columnIntersectsSelection(column:int,parent:QModelIndex) | bool | 如果选择的数据项与parent的子数据项的 指定列有交集,则返回True |
currentIndex() | QModelIndex | 获取当前数据项的索引 |
hasSelection() | b001 | 获取是否有选择项 |
isColumnSelected(column: int,parent: QModelIndex) | bool | 获取parent下的某列是否全部选中 |
isRowSelected(row:int,parent: QModelIndex) | bool | 获取parent下的某行是否全部选中 |
isSelected(index:QModelIndex) | bool | 获取某数据项是否选中 |
selectedRows(column:int=0) | ListCint] | 获取某行中被选中的数据项的索引列表 |
selectedColumns(row:int=0) | ListCint] | 获取某列中被选中的数据项的索引列表 |
selectedIndexes() | ListCint] | 获取被选中的数据项的索引列表 |
selection() | QItemSelection | 获取项的选择集 |
setModel(QAbstractItemModel) | None | 设置数据模型 |
当前项与选中项
在视图中,始终有一个当前项和一个选定项(这是两个独立的状态)。一个项可以是当前项,同时也是选中项。例如,当使用键盘导航时,视图负责确保始终存在当前项。
下面信息突出显示了当前项和选定项之间的区别:
- 当前项只能有一项; 选定项可以有多项。
- 当前项通过按键导航或鼠标单击来更改; 当用户与项进行交互时,选定项取决于几种预定义模式(例如,单选,多选等),可以设置或取消设置项的选定状态。
- 如果按下编辑键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()< 0):
return
self.statusBar().showMessage('Moved from({},{})to({},{})'.format(previous.row(),
previous.column(),
current.row(),
current.column()))
更新选择
选择命令由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()函数可以确定一个指定的项是否是另一层次项的父项。
选择模型QItemSelectionModel 的信号
选择模型QItemSelectionModel的信号如表所示
QItemSelectionModel的信号及参数类型 | 说明 |
---|---|
curentChanged(current: QModelIndex,previous: QModellndex) | 当前数据项发生改变时发送信号 |
currentColumnChanged(current: QModelIndex,previous: QModelIndex) | 当前数据项的列改变时发送信号 |
currentRowChanged(current:QModelIndex,previous:QModelIndex) | 当前数据项的行改变时发送信号 |
modelChanged(QAbstractItemModel) | 数据模型发生改变时发送信号 |
selectionChanged(selected: QItemSelection,deselected: QItemSelection) | 选择区域发生改变时发送信号 |
选择集QItemSelection方法
选择集QItemSelection是指数据模型中已经被选中的项的集合其方法如表所示。
QItemSelection的方法及参数类型 | 返回值的类型 | 说月 明 |
---|---|---|
select(topLeft: QModelIndex,bottomRight: QModelIndex) | None | 添加从左上角到右下角位置处的 所有项 |
merge(Other: QItemSelection,command: QItemSelectionModel.SelectionFlags) | None | 与其他选择集合并 |
indexes() | List[QModelIndexJ | 获取选择集中的数据索引列表 |
contains(index:QModelIndex) | bool | 获取指定的项是否在选择集中 |
clear() | None | 清空选择集 |
count() | int | 获取选择集中元素的个数 |
选择模型QItemSelectionModel 的实例
handlingselections.py演示了鼠标拖动时改变选择的效果,并在状态条上显示信息。完整代码如下:
# -*- coding: UTF-8 -*-
# File date: Hi_2023/3/27 0:29
# File_name: 01-例子.py
import sys
from PySide6.QtCore import QItemSelectionModel,QModelIndex,QItemSelection
from PySide6.QtGui import QStandardItemModel
from PySide6.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('PySide6: QItemSelection演示')
# 设置窗口大小
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()< 0):
return
self.statusBar().showMessage('Moved from({},{})to({},{})'.format(previous.row(),
previous.column(),
current.row(),
current.column()))
if __name__ == '__main__':
app = QApplication(sys.argv)
window = DemoHandlingSelections()
window.show()
sys.exit(app.exec())