28. 模型与视图

一、什么是模型与视图

  模型/视图(Model/View)结构是进行数据显示与编辑的一种编程结构,在这种结构里,源数据由模型(Model)读取,然后在视图(View)组件上显示和编辑,在界面上编辑修改的数据又通过模型保存到源数据。源数据可以是内存中的字符串列表或二维表格型数据,也可以是数据库中的数据表。视图就是界面上的视图类组件,如 QListViewQTreeViewQTableView 等。

模型与视图机制

  • 外部数据是外部数据,如数据库的一个数据表或SQL查询结果、内存中的一个字符串列表或磁盘文件结构等。
  • 数据模型与外部数据通信,并为视图组件提供数据接口。它从外部数据提取需要的数据,用于视图组件进行显示和编辑。
  • 视图控件是界面组件,视图控件从数据模型获得数据然后显示在界面上。PySide6 提供一些现成的视图组件类,如 QListViewQTreeViewQTableView 等。
  • 代理控件在视图与模型之间交互操作时提供临时编辑组件的功能。模型向视图提供数据是单向的,一般仅用于显示。当需要在视图上编辑数据时,代理功能会为编辑数据提供一个编辑器,这个编辑器获取模型的数据、接受用户编辑的数据后又提交给模型。

  模型、视图和代理之间使用信号和槽进行通信。当源数据发生变化时,数据模型发射信号通知视图组件。当用户在界面上操作数据时,视图组件发射信号表示这些操作信息。在编辑数据时,代理会发射信号告知数据模型和视图组件编辑器的状态。

  我们可以在终端中使用 pip 安装 PySide6 模块。默认是从国外的主站上下载,因此,我们可能会遇到网络不好的情况导致下载失败。我们可以在 pip 指令后通过 -i 指定国内镜像源下载

pip install pyside6 -i https://mirrors.aliyun.com/pypi/simple

  国内常用的 pip 下载源列表:

二、抽象模型与抽象视图

2.1、抽象模型

  根据用途不同,数据模型分为多种类型,QAbstractItemModel 是所有数据模型的基类。

数据模型的继承关系

  抽象模型 QAbstractItemModel 提供数据模型与视图控件的数据接口,不能直接使用该类,需要用其子类定义数据模型。QAbstractItemModel 的方法会被其子类继承。

index(row:int, column:int, parent:QModelIndex=QModelIndex()) -> QModelIndex     # 获取父索引下的指定行和列的数据索引项
parent(child:QModelIndex) -> QModelIndex                                        # 获取指定索引项的父索引项
sibling(row:int, column:int, idx:QModelIndex) -> QModelIndex                    # 获取指定索引项的指定行和列的数据索引项

flags(index:QModelIndex) -> Qt.ItemFlag                                         # 获取指定索引项的标识

hasChildren(parent:QModelIndex=QModelIndex()) -> bool                           # 判断是否有子数据项
hasIndex(row:int, column:int, parent:QModelIndex=QModelIndex()) -> bool         # 判断是否能创建数据索引项

rowCount(parent:QModelIndex=QModelIndex()) -> int                               # 获取行数
columnCount(parent:QModelIndex=QModelIndex()) -> int                            # 获取行数

insertRow(row:int, parent:QModelIndex=QModelIndex()) -> bool                    # 插入行,成功返回True
insertRows(row:int, count:int, parent:QModelIndex=QModelIndex()) -> bool        # 插入多行,成功返回True
insertColumn(column:int, parent:QModelIndex=QModelIndex()) -> bool              # 插入列,成功返回True
insertColumns(column:int, count:int, parent:QModelIndex=QModelIndex()) -> bool  # 插入多列,成功返回True

setData(index:QModelIndex, value:Any, role:Qt.ItemDataRole=Qt.EditRole) -> bool # 设置数据,成功返回True
data(index:QModelIndex, role:Qt.ItemDataRole=Qt.EditRole) -> Any                # 获取数据
setItemData(index:QModelIndex, role:Dict[int, Any]) -> bool                     # 设置数据,成功返回True
itemData(index:QModelIndex) -> Any                                              # 获取数据
clearItemData(index:QModelIndex) -> bool                                        # 清除项中的数据

setHeaderData(section:int, orientation:Qt.Orientation, value:Any, role:int=Qt.EditRole) -> bool     # 设置表头数据,成功返回True
headerData(section:int, orientation:Qt.Orientation, role:int=Qt.EditRole) -> Any                    # 获取表头数据Any

supportedDragActions() -> Qt.DropActions                                        # 获取支持的拖放动作

sort(column:int, order:Qt.SortOrder=Qt.AscendingOrder) -> None                  # 排序 

submit() -> None                                                                # 提交缓存数据到永久存储中
revert() -> None                                                                # 放弃提交缓存数据到永久存储中

# 移动单行,成功返回True
moveRow(sourceParent:QModelIndex, sourceRow:int, destinationParent:QModelIndex, destinationRow:int) -> bool
# 移动多行,成功返回True
moveRows(sourceParent:QModelIndex, sourceRow:int, count:int, destinationParent:QModelIndex, destinationRow:int) -> bool

removeRow(row:int, parent:QModelIndex=QModelIndex()) -> bool                    # 移除单行,成功返回True
removeRows(row:int, count:int, parent:QModelIndex=QModelIndex())                # 移除多行,成功返回True

# 将目标数据项索引的指定列移动到目标数据项索引的指定列处,成功返回True
moveColumn(sourceParent:QModelIndex, sourceColumn:int, destinationParent:QModelIndex, destinationColumn:int) -> bool  
# 移动多列到目标索引的指定列处,成功返回True
moveColumns(sourceParent:QModelIndex, sourceColumn:int, count:int, destinationParent:QModelIndex, destinationColumn:int) -> bool

removeColumn(column:int, parent:QModelIndex=QModelIndex()) -> bool              # 移除单列,成功返回True
removeColumns(column:int, count:int, parent:QModelIndex=QModelIndex())          # 移除多列,成功返回True

  QAbstractItemModel 类常用信号:

rowsAboutToBeInserted(parent:QModelIndex, first:int, last:int)                  # 插入行之前发射信号
rowsInserted(parent:QModelIndex, first:int, last:int)                           # 插入行之后发射信号

# 移动行之前发射信号
rowsAboutToBeMoved(sourceParent:QModelIndex, sourceStart:int, sourceEnd:int, destinationParent:QModelIndex, destinationRow:int)
# 移动行之后发射信号
rowsMoved(sourceParent:QModelIndex, sourceStart:int, sourceEnd:int, destinationParent:QModelIndex, destinationRow:int)

rowsAboutToBeRemoved(parent:QModelIndex, first:int, last:int)                   # 移除行之前发射信号
rowsRemoved(parent:QModelIndex, first:int, last:int)                            # 移除行之后发射信号

columnsAboutToBeInserted(parent:QModelIndex, first:int, last:int)               # 插入列之前发射信号
columnsInserted(parent: QModelIndex, first:int, last:iint)                      # 插入列之后发射信号

# 移动列之前发射信号
columnsAboutToBeMoved(sourceParent:QModelIndex, sourceStart:int, sourceEnd:int, destinationParent:QModelIndex, destinationColumn:int)
# 移动列之后发射信号
columnsMoved(sourceParent:QModelIndex, sourceStart:int, sourceEnd:int, destinationParent:QModelIndex, destinationColumn:int)

columnsAboutToBeRemoved(parent:QModelIndex, first:int, last:int)                # 移除列之后发射信号
columnsRemoved(parent:QModelIndex, first:int, last:int)                         # 移除列之后发射信号

dataChanged(topLeft:QModelIndex, topRight:QModelIndex, roles:List[int]=list())  # 数据改变之前发射信号
headerDataChanged(orientation:Qt.Orientation, first:int, last:int)              # 数据改变之后发射信号

modelAboutToBeReset()                                                           # 重置之前发射信号
modelReset()                                                                    # 重置之后发射信号

  用 setData(index:QModelIndex, data:Any,role:Qt.ItemDataRole) 方法可以 设置数据项的某角色值,用 setItemData(index:QModelIndex, roles:Dict[int,Any]) 方法可以 用字典方式设置某数据项的多个角色值,用 data(index:QModelIndex, role:Qt.ItemDataRole)itemData(index:QModelIndex) 方法 获取角色值,其中参数 roleQt.ItemDataRole 类型的枚举值,可以取值如下所示:

Qt.ItemDataRole 取值 对应的数据类型 说明
Qt.ItemDataRole.DisplayRole 0 str 视图控件显示文本
Qt.ItemDataRole.DecorationRole 1 QIcon、QPixmap 图标
Qt.ItemDataRole.EditRole 2 str 视图控件中编辑是显示文本
Qt.ItemDataRole.ToolTipRole 3 str 提示信号
Qt.ItemDataRole.StatusTipRole 4 str 状态提示信息
Qt.ItemDataRole.WhatsThisRole 5 str 按下 Shift+F1 键时显示的数据
Qt.ItemDataRole.FontRole 6 QFont 默认代理控件的字体
Qt.ItemDataRole.TextAlignmentRole 7 Qt.AlignmentFlag 默认代理控件的对齐方式
Qt.ItemDataRole.BackgroundRole 8 QBrush、QColor、Qt.GlobalColor 默认代理控件的背景色
Qt.ItemDataRole.ForegroundRole 9 QBrush、QColor、Qt.GlobalColor 默认代理控件的前景色
Qt.ItemDataRole.CheckStateRole 10 Qt.CheckState 勾选状态
Qt.ItemDataRole.AccessibleTextRole 11 str 用于可访问插件扩展的文本
Qt.ItemDataRole.AccessibleDescriptionRole 12 str 用于可访问功能的描述
Qt.ItemDataRole.SizeHintRole 13 QSize 尺寸提示
Qt.ItemDataRole.InitialSortOrderRole 14 Qt.SortOrder 初始排序
Qt.ItemDataRole.UserRole 0x0100 Any 自定义角色,可使用多个自定义角色,第 1 个为 Qt.UserRole,第 2 个为 Qt.UserRole+1,依次类推

  用 setHeaderData(section:int,orientation:Qt.Orientation,value:Any,role:int=Qt.EditRole) 方法设置表头某角色的值,当 orientationQt.Orientation.Horizontal 时,section 是指列;orientationQt.Orientation.Vertical 时,section 是指行。

2.2、数据索引

  数据模型中存放着数据,要获取或写入数据,需要知道数据所在的行和列。行和列单独构成一个类,称为 数据项索引 QModelIndex,通过数据项索引可以定位到对应的数据。

  由于数据模型可能是一个列表、表格、树或更复杂的结构,所以数据模型的数据索引也会比较复杂。通常用 QModelIndex() 表示指向数据模型根部的索引,这个索引不指向任何数据,表示最高层索引。用数据模型的 index(row,column,parent) 表示索引 parent(类型是 QModelIndex)下的第 row 行第 column 列的数据项索引。

  数据索引常用的方法如下:

model() -> QAbstractItemModel                                                   # 获取数据模型

parent() -> QModelIndex                                                         # 获取父索引
sibling(row:int, column:int) -> QModelIndex                                     # 获取兄弟索引
siblingAtRow(row:int) -> QModelIndex                                            # 获取指定行的兄弟索引
siblingAtColumn(column:int) -> QModelIndex                                      # 获取指定列的兄弟索引
row() -> int                                                                    # 获取行索引
column() -> int                                                                 # 获取列索引
isValid() -> bool                                                               # 判断索引是否有效

data(role:int=Qt.DisplayRole) -> Any                                            # 获取数据相关指定角色的数据
flags() -> Qt.ItemFlag                                                          # 获取标识

  用 flags() 方法 获取数据项的状态,返回值是 Qt.ItemFlag 的枚举值,可取值如下:

Qt.ItemFlag.NoItemFlags                                                         # 没有任何属性
Qt.ItemFlag.ItemIsSelectable                                                    # 可选择
Qt.ItemFlag.ItemIsEditable                                                      # 可编辑
Qt.ItemFlag.ItemIsDragEnabled                                                   # 可拖拽
Qt.ItemFlag.ItemIsDropEnabled                                                   # 可拖放
Qt.ItemFlag.ItemIsUserCheckable                                                 # 可勾选
Qt.ItemFlag.ItemIsEnabled                                                       # 可激活
Qt.ItemFlag.ItemIsAutoTristate                                                  # 由子项的状态决定
Qt.ItemFlag.ItemNeverHasChildren                                                # 禁止有子项
Qt.ItemFlag.ItemIsUserTristate                                                  # 用户可以在3种状态间切换

2.3、抽象视图

  所有的视图都继承于 QAbstractItemView 类。

基于项和模型控件的继承关系

  QAbstractItemView 类常用的方法如下:

# 实例方法
setModel(model:QAbstractItemModel) -> None                                      # 设置数据模型
setSelectionModel(selectionModel:QItemSelectionModel) -> None                   # 设置选择模型
selectionModel() -> QItemSelectionModel                                         # 获取选择模型
setSelection(rect:QRect, command:QItemSelectionModel.SelectionFlag) -> None     # 选择指定范围内的数据项
setRootIndex(index:QModelIndex) -> None                                         # 设置根索引

setAlternatingRowColors(enable:bool) -> None                                    # 设置交替色
indexAt(point:QPoint) -> QModelIndex                                            # 获取指定位置处数据项的模型数据索引

selectAll() -> None                                                             # 选择所有数据项
selectedIndexes() -> QModelIndexList                                            # 获取所有选择项的模型数据索引

setTextElideMode(mode:Qt.TextElideMode) -> None                                 # 设置省略号的位置

isIndexHidden(index:QModelIndex) -> bool                                        # 获取索引对应的单元是否隐藏

# 使数据项可见
scrollTo(index:QModelIndex, hint:QAbstractItemView.ScrollHint=QAbstractItemView.ScrollHint.EnsureVisible) -> None

# 槽方法
clearSelection() -> None                                                        # 清空选择

  QAbstractItemView 类常用的信号如下:

activated(index:QModelIndex)                                                    # 数据项活跃时发射信号
clicked(index:QModelIndex)                                                      # 单击数据项时发射信号
doubleClicked(index:QModelIndex)                                                # 双击数据项时发射信号
entered(index:QModelIndex)                                                      # 光标进入数据项时发射信号
iconSizeChanged(size:QSize)                                                     # 图标尺寸发生变化时发射信号
pressed(index:QModelIndex)                                                      # 按下鼠标时发射信号
viewportEntered()                                                               # 光标进入视口时发射信号

  视图组件在显示数据时,只需调用视图类的 setModel(model:QAbstractItemModel) 函数为视图组件 设置一个数据模型,就可以实现视图组件与数据模型之间的关联,在视图组件上的修改自动保存到关联的数据模型里,一个数据模型可以同时在多个视图组件里显示数据。

  用 setSelectionMode(mode:QAbstractItemView.SelectionMode) 方法可以 设置选择模式,其中参数 modeQAbstractItemView.SelectionMode 类型的枚举值,可以取值如下:

QAbstractItemView.SelectionMode.NoSelection                                     # 进制选择
QAbstractItemView.SelectionMode.SingleSelection                                 # 单选
QAbstractItemView.SelectionMode.MultiSelection                                  # 多选
QAbstractItemView.SelectionMode.ExtendedSelection                               # 按Ctrl键点选,按Shift键连选
QAbstractItemView.SelectionMode.ContiguousSelection                             # 按Ctrl键或Shift键时连选

三、数据模型

  数据模型存储数据的 3 种常见结构形式,主要有 列表模型(list model)、表格模型(table model)和 树模型(tree model)。

  • 列表模型中的数据没有层级关系,由一列多行数据构成。
  • 表格模型由多行多列数据构成。
  • 树模型的数据是有层级关系的,每层数据下面还有子层数据。

数据模型存储数据的三种结构模式

  不管数据的存储形式如何,每个数据都称为 数据项(data item)。数据项存储不同角色、不同用途的数据,每个数据项都有一个索引(model index),通过数据索引可以获取数据项上存储的数据。

3.1、文本列表模型

  文本列表模型 QStringListModel 通常用于存储一维文本列表,它由一列多行文本数据构成。它可以作为 QListView 的数据模型,在界面上显示和编辑字符串列表。

  用 QStringListModel 类创建文本列表模型实例的方法如下:

QStringListModel(parent:QWidget=None)
QStringListModel(strings:Sequence[str], parent:QWidget=None)

  其中 parent 是继承自 QObject 的实例对象;strings字符串型列表或元组,用于确定文本列表模型中显示角色和编辑角色的数据。

  文本列表模型 QStringListModel 类的特有方法如下:

setStringList(strings:Sequence[str]) -> None                                    # 设置列表模型显示和编辑角色的文本数据
stringList() -> List[str]                                                       # 获取文本列表

3.2、文件系统模型

  文件系统模型 QFileSystemModel 为本机的文件系统提供一个数据模型,可用于访问本机的文件系统。QFileSystemModel 和视图组件 QTreeView 结合使用,可以用目录树的形式显示本机上的文件系统,如同 Windows 系统的资源管理器一样。

  使用 QFileSystemModel 提供的接口函数,可以创建目录、删除目录、重命名目录,可以获得文件名称、目录名称、文件大小等参数,还可以获得文件的详细信息。

  用 QFileSystemModel 类定义文件系统模型的方法如下所示:

QFileSystemModel(parent:QObject=None)

  QFileSystemModel 类的常用方法如下:

setRootPath(path:str) -> QModelIndex                                            # 设置模型的根目录,并返回指向该目录的模型数据索引

setFilter(filters:QDir.Filter) -> None                                          # 设置路径过滤器

setNameFilters(filters:Sequence[str]) -> None                                   # 设置文件名过滤器
nameFilters() -> List[str]                                                      # 获取名称过滤器
setNameFilterDisables(enable:bool) -> None                                      # 设置名称过滤器是否激活
nameFilterDisables() -> bool                                                    # 获取名称过滤器是否激活

setOption(option:QFileSystemModel.Option, on:bool=true) -> None                 # 设置文件系统模型的参数

setReadOnly(enable:bool) -> None                                                # 设置是否只读
isReadOnly() -> bool                                                            # 获取是否只读

fileIcon(index:QModelIndex) -> QIcon                                            # 获取文件图标
fileInfo(index:QModelIndex) -> QFileInfo                                        # 获取文件信息
fileName(index:QModelIndex) -> str                                              # 获取文件名
filePath(index:QModelIndex) -> str                                              # 获取文件路径

index(path:str, column:int=0) -> QModelIndex                                    # 获取文件路径对应的索引
isDir(index:QModelIndex) -> bool                                                # 获取是否为目录
lastModified(index:QModelIndex) -> QDateTime                                    # 获取文件最后修改时间
mkdir(parent:QModelIndex, name:str) -> QModelIndex                              # 创建目录
myComputer(role:Qt.UserRole=Qt.DisplayRole) -> Any                              # 获取我的电脑的路径
remove(index:QModelIndex) -> bool                                               # 删除文件或目录
rmdir(index:QModelIndex) -> bool                                                # 删除目录
rootDirectory() -> QDir                                                         # 返回根目录
rootPath() -> str                                                               # 获取根目录路径
size(index:QModelIndex) -> int                                                  # 获取文件大小
type(index:QModelIndex) -> str                                                  # 获取文件类型

  QFileSystemModel 类的常用信号如下:

directoryLoaded(path:str)                                                       # 当加载路径时发射信号
fileRenamed(path:str, oldName:str, newName:str)                                 # 当文件重命名时发射信号
rootPathChanged(newPath:str)                                                    # 根路径发生改变时发射信号

  要通过 QFileSystemModel 获得本机的文件系统,需要用 setRootPath(path:str) 函数为 QFileSystemModel 设置一个根目录。该函数返回一个 指向该目录的模型数据索引。改变根目录时,发送 rootPathChanged(newPath) 信号。用 rootPath() 方法可以 获取根目录

  用 setOption(model:QFileSystemModel.Option,on:bool=True) 方法 设置文件系统模型的参数,其中参数 modelQFileSystemModel.Option 类型的枚举值,可以取值如下:

QFileSystemModel.Option.DontWatchForChanges                                     # 不使用监控器,默认是关闭的
QFileSystemModel.Option.DontResolveSymlinks                                     # 不解析链接,默认是关闭的
QFileSystemModel.Option.DontUseCustomDirectoryIcons                             # 不使用客户图标,默认是关闭的

  用 setNameFilters(filters:Sequence[str]) 方法 设置名称过滤器;用 setFilter(filters:QDir.Filter) 方法 设置路径过滤器,其中 filtersQDir.Filter 类型的枚举值,可以取值如下:

QDir.Filter.Dirs
QDir.Filter.AllDirs
QDir.Filter.Files
QDir.Filter.Drives
QDir.Filter.NoSymLinks
QDir.Filter.NoDotAndDotDot
QDir.Filter.NoDot
QDir.Filter.NoDotDot
QDir.Filter.AllEntries
QDir.Filter.Readable
QDir.Filter.Writable
QDir.Filter.Executable
QDir.Filter.Modified
QDir.Filter.Hidden
QDir.Filter.System
QDir.Filter.CaseSensitive

设置路径过滤器时一定要包括 QDir.Filter.AllDirs,否则无法识别路径的结构。

3.3、标准数据模型

  标准数据模型 QStandardItemModel 可以存储多行多列的数据表格,数据表格中的每个数据称为数据项 QStandardItem,每个数据项下面还可以存储多行多列的子数据表格,并形成层级关系,这样会形成比较复杂的结构关系。数据项可以存储文本、图标、勾选状态等信息。

  用 QStandardItemModel 创建标准数据模型的方法如下所示:

QStandardItemModel(parent:QObject=None)
QStandardItemModel(rows:int, columns:int, parent:QObject=None)

  QStandardItemModel 类的常用方法如下:

setRowCount(rows:int) -> None                                                   # 设置行的数量
setColumnCount(columns:int) -> None                                             # 设置列的数量

appendRow(item:QStandItem) -> None                                              # 添加行
appendRow(items:Sequence[QStandItem]) -> None                                   # 添加行
appendColumn(items:Sequence[QStandItem]) -> None                                # 添加列

insertRow(row:int, item:QStandItem) -> None                                     # 插入行
insertRow(row:int, items:Sequence[QStandItem]) -> None                          # 插入行
insertColumn(column:int, items:Sequence[QStandItem]) -> None                    # 插入列

takeRow(row:int) -> List[QStandItem]                                            # 删除行
takeColumn(column:int) -> List[QStandItem]                                      # 删除列

setItem(row:int, item:QStandItem) -> None                                       # 根据行设置单元格的值
setItem(row:int, column:int, item:QStandItem) -> None                           # 根据行和列设置单元格的值
item(row:int, column:int=0) -> QStandItem                                       # 根据行和列获取单元格的值
takeItem(row:int, column:int=0) -> QStandItem                                   # 移除单元格

setHorizontalHeaderItem(column:int, item:QStandItem) -> None                    # 设置水平表头的项
horizontalHeaderItem(column:int) -> QStandItem                                  # 获取水平表头的项
takeHorizontalHeaderItem(column:int) -> QStandItem                              # 移除水平表头的项
setVerticalHeaderItem(row:int, item:QStandItem) -> None                         # 设置垂直表头的项
verticalHeaderItem(row:int) -> QStandItem                                       # 获取垂直表头的项
takeVerticalHeaderItem(row:int) -> QStandItem                                   # 移除垂直表头的项

indexFromItem(item:QStandItem) -> QModelIndex                                   # 根据项获取索引
itemFromIndex(index:QModelIndex) -> QStandItem                                  # 根据索引获取项
invisibleRootItem() -> QStandItem                                               # 获取根项
clear() -> None                                                                 # 清空表格
findItems(text:str, flags:Qt.MatchFlag=Qt.MatchExactly, column:int=0) -> List[QStandItem]   # 获取满足匹配条件的数据项列表
setSortRole(role:int) -> None                                                   # 设置排序角色
sortRole() -> int                                                               # 获取排序角色

  用 QStandardItem 创建数据项的方法如下所示,用 QStandardItem(rows,columns) 方法可以创建一个含有多行多列子数据项的数据项。

QStandardItem()
QStandardItem(text:str)
QStandardItem(icon:Union[QIcon, QPixmap], text:str)
QStandardItem(rows:int, columns:int-1)

  QStandardItem 类的常用方法如下:

index() -> QModelIndex                                                          # 获取数据项的索引
model() -> QAbstractItemModel                                                   # 获取模型

setRowCount(rows:int) -> None                                                   # 设置行数
rowCount() -> int                                                               # 获取行数
setColumnCount(columns:int) -> None                                             # 设置列数
columnCount() -> int                                                            # 获取列数

setChild(row:int, item:QStandardItem) -> None                                   # 根据行设置子数据项
setChild(row:int, column:int, item:QStandardItem) -> None                       # 根据行和列设置子数据项
hasChildren() -> bool                                                           # 是否有子数据项
child(row:int, column:int=0) -> QStandardItem                                   # 获取子数据项
takeChild(row:int, column:int=0) -> QStandardItem                               # 移除并返回子项

parent() -> QStandardItem                                                       # 获取父数据项

row() -> int                                                                    # 获取行号
column() -> int                                                                 # 获取列号

appendRow(item:QStandardItem) -> None                                           # 添加行
appendRow(items:Sequence[QStandardItem]) -> None                                # 添加行
appendRows(items:Sequence[QStandardItem]) -> None                               # 添加多行

insertRow(row:int, item:QStandardItem) -> None                                  # 插入行
insertRows(row:int, count:int) -> None                                          # 插入多行
insertRows(row:int, items:Sequence[QStandardItem]) -> None                      # 插入多行

removeRow(row:int) -> None                                                      # 移除行
removeRows(row:int, count:int) -> None                                          # 移除多行
takeRow(row:int) -> List[QStandardItem]                                         # 移除并返回行

appendColumn(items:Sequence[QStandardItem]) -> None                             # 添加列

insertColumn(column:int, items:Sequence[QStandardItem]) -> None                 # 插入列
insertColumns(column:int, count:int) -> None                                    # 插入多列

removeColumn(column:int) -> None                                                # 移除列
removeColumns(column:int, count:int) -> None                                    # 移除多列
takeColumn(column:int) -> List[QStandardItem]                                   # 移除并返回列

setAutoTristate(tristate:bool) -> None                                          # 设置自动有第三种状态
isAutoTristate() -> bool                                                        # 获取是否自动有第三种状态   

# 设置前景色
setForeground(brush:Union[QBrish, Qt.BrushStyle, Qt.GlobalColor, QColor, QGradint, QImage, QPixmap]) -> None
foreground() -> QBrush                                                          # 获取前景画刷

# 设置背景色
setBackground(brush:Union[QBrish, Qt.BrushStyle, Qt.GlobalColor, QColor, QGradint, QImage, QPixmap]) -> None
background() -> QBrush                                                          # 获取背景画刷

setCheckable(checkable:bool) -> None                                            # 设置是否可选
setCheckState(checkState:Qt.CheckState) -> None                                 # 设置选中状态

setDragEnabled(dragEnabled:bool) -> None                                        # 设置是否可以拖拽
isDragEnabled() -> bool                                                         # 获取是否可以拖拽

setDropEnabled(dropEnabled:bool) -> None                                        # 设置是否可以拖放
isDropEnabled() -> bool                                                         # 获取是否可以拖放

setEditable(editable:bool) -> None                                              # 设置是否可以编辑
isEditable() -> bool                                                            # 获取是否可以编辑

setEnabled(enabled:bool) -> None                                                # 设置是否可用
isEnabled() -> bool                                                             # 获取是否可用

setFlags(flags:Qt.ItemFlag) -> None                                             # 设置标识

setSelectable(selectable:bool) -> None                                          # 获取是否可选
isSelectable() -> bool                                                          # 获取是否可选

isUserTristate() -> bool                                                        # 获取是否用户有第三种状态

setIcon(icon:QIcon) -> None                                                     # 设置图标
setFont(font:QFont) -> None                                                     # 设置字体

setText(text:str) -> None                                                       # 设置文本
text() -> str                                                                   # 获取文本

setToolTip(toolTip:str) -> None                                                 # 设置提示信息
setWhatsThis(whatsThis:str) -> None                                             # 设置按Shift+F1键的提示信息

sortChildren(column:int, order:Qt.SortOrder=Qt.AscendingOrder) -> None          # 对列进行排序

3.4、选择模型

  在列表、树和表格视图中,如要对数据项进行操作,需要先选中数据项,被选中的数据项高亮或反色显示。在 PySide6 中被选中的数据项记录在选择模型 QItemSelectionModel 中,如果多个视图控件同时关联到一个数据模型,选择模型可以记录多个视图控件中被选中的数据项,形成数据选择集 QItemSelection。视图控件有自己默认的选择模型,一般可以满足用户的需要。另外可以单独创建新的选择模型,以实现特殊目的。

  视图控件都有 setSelectionModel(model:QItemSelectionModel) 方法和 selectionModel() 方法,用于 设置视图控件的选择模型和获取选择模型。用 selectionModel() 方法 获取某一个视图控件的选择模型 后,可以使用 setSelectionModel() 方法提供给其他视图共享选择模型,因此一般没有必要新建选择模型。

  用 QItemSelectionModel 创建选择模型的方法如下:

QItemSelectionModel(model:QAbstractItemModel=None)
QItemSelectionModel(model:QAbstractItemModel, parent:QObject)

  QItemSelectionModel 类常用方法如下:

# 实例方法
clear() -> None                                                                 # 清空选择模型并发送selectionChanged()和currentChanged()信号
reset() -> None                                                                 # 清空选择模型,不发送信号
clearCurrentIndex() -> None                                                     # 清空当前选中项索引模型,并发送currentChanged()信号

setCurrentIndex(index:QModelIndex, command:QItemSelectionCommand.SelectionFlags) -> None    # 设置当前项,并发送currentChanged()信号 
select(index:QModelIndex, command:QItemSelectionCommand.SelectionFlags) -> None             # 选择项,并发送selectionChanged()信号 
select(selection:QItemSelection, command:QItemSelectionCommand.SelectionFlags) -> None      # 选择项,并发送selectionChanged()信号 

rowIntersectsSelection(row:int, parent:QModelIndex=QModelIndex()) -> bool       # 如果选择的数据项与parent的子数据项的指定行有交集,则返回True
columnIntersectsSelection(column:int, parent:QModelIndex=QModelIndex()) -> bool # 如果选择的数据项与parent的子数据项的指定列有交集,则返回True

currentIndex() -> QModelIndex                                                   # 返回当前选中项的索引模型
hasSelection() -> bool                                                          # 如果有选择项,则返回True
isRowSelected(row:int, parent:QModelIndex=QModelIndex()) -> bool                # 获取parent下的某行是否全部选中
isColumnSelected(column:int, parent:QModelIndex=QModelIndex()) -> bool          # 获取parent下的某列是否全部选中
isSelected(index:QModelIndex) -> bool                                           # 获取index数据项是否选中
selectedRows(column:int=0) -> List[int]                                         # 获取某行中被选中数据项的索引列表
selectedColumns(row:int=0) -> List[int]                                         # 获取某列中被选中数据项的索引列表
selectedIndexes() -> List[int]                                                  # 获取被选中数据项的索引列表
selection() -> QItemSelection                                                   # 获取项的选择集

setModel(model:QAbstractItemModel) -> None                                      # 设置数据模型

# 槽函数
clearSelection() -> None                                                        # 清空选择模型,并发送currentChanged()信号

  QItemSelectionModel 类常用信号如下:

currentChanged(current:QModelIndex, previous:QModelIndex)                       # 当前数据项发生改变时发射信号
currentRowChanged(current:QModelIndex, previous:QModelIndex)                    # 当前数据项的行改变时发射信号
currentColumnChanged(current:QModelIndex, previous:QModelIndex)                 # 当前数据项的列改变时发射信号
modelChanged(model:QAbstractItemModel)                                          # 数据模型发生改变时发射信号
selectionChanged(selected:QItemSelection, deselected:QItemSelection)            # 选择区域发生改变时发射信号

  其中用 selection() 方法可以 获取项的选择集 QItemSelection。用 select(index:QModelIndex, command:QItemSelectionModel.SelectionFlag) 方法可以 往选择集中添加内容,或 从选择集中移除选择,其中参数 indexQItemSelectionModel.SelectionFlag 类型的枚举值,可取的值如下所示:

QItemSelectionModel.SelectionFlag.NoUpdate                                      # 选择集没有变化
QItemSelectionModel.SelectionFlag.Clear                                         # 清空选择集
QItemSelectionModel.SelectionFlag.Select                                        # 选择所有指定项
QItemSelectionModel.SelectionFlag.Deselect                                      # 取消选择所有指定项
QItemSelectionModel.SelectionFlag.Toggle                                        # 根据项的状态选择或不选择
QItemSelectionModel.SelectionFlag.Current                                       # 更新当前的选择
QItemSelectionModel.SelectionFlag.Rows                                          # 选择整行
QItemSelectionModel.SelectionFlag.Columns                                       # 选择整列
QItemSelectionModel.SelectionFlag.SelectCurrent                                 # Select | Current
QItemSelectionModel.SelectionFlag.ToggleCurrent                                 # Toggle | Current
QItemSelectionModel.SelectionFlag.ClearAndSelect                                # Claer | Select

  选择集 QItemSelection指数据模型中已经被选中的项的集合,其方法如下所示:

select(topLeft:QModelIndex, bottomRight:QModelIndex) -> None                    # 添加左上角到右下角位置处的所有项
merge(other:QItemSelection, command:QItemSelectionModel.SelectionFlag) -> None  # 与其它选择集合并 
indexes() -> List[QModelIndex]                                                  # 返回选择集中的所有索引
contains(index:QModelIndex) -> bool                                             # 获取指定的项是否在选择集中
clear() -> None                                                                 # 清空选择集   
count() -> int                                                                  # 返回选择集中的项数量

四、视图控件

4.1、列表视图控件

  列表视图控件 QListView 用于显示文本列表模型 QStringListModel 中的文本数据。用 QListView 创建列表视图控件的方法如下:

QListView(parent:QWidget=None)

  其中 parent 是继承自 QWidget 的窗口或容器控件。

  列表视图控件 QListView 用于显示数据模型中某数据项下的所有子数据项的显示角色的文本。列表视图控件没有表头,可以把数据显示成一列,也可以显示成一行。列表视图控件不仅可以显示文本列表模型中的数据,也可显示其他模型中的数据。

  列表视图控件 QListView 类的常用方法如下:

clearPropertyFlags() -> None                                                    # 清空所有属性标志

contentsSize() -> QSize                                                         # 获取内容尺寸
resizeContents(width:int, height:int) -> None                                   # 调整内容尺寸

setModelColumn(column:int) -> None                                              # 设置模型列
modelColumn() -> int                                                            # 获取模型列

setWordWrap(on:bool) -> None                                                    # 设置单词是否可以写到两行上
setWrapping(enable:bool) -> None                                                # 设置文本是否可以写到两行上

setFlow(flow:QListView.Flow) -> None                                            # 设置显示方向
setGridSize(size:QSize) -> None                                                 # 设置网格尺寸
setItemAlignment(alignment:Qt.Alignment) -> None                                # 设置项对齐方式
setLayoutMode(mode:QListView.LayoutMode) -> None                                # 设置数据的显示模式
setBatchSize(batchSize:int) -> None                                             # 设置批量显示的数量,默认100
setMovement(movement:QListView.Movement) -> None                                # 设置数据项的移动方式
setResizeMode(mode:QListView.ResizeMode) -> None                                # 设置数据项的大小调整方式
setRowHidden(row:int, hide:bool) -> None                                        # 设置指定行是否可见
setSpacing(space:int) -> None                                                   # 设置数据项之间的间距
setUniformItemSizes(enable:bool) -> None                                        # 设置所有数据项的大小是否一致
setViewMode(mode:QListView.ViewMode) -> None                                    # 设置显示模式

setPositionForIndex(position:QPoint, index:QModelIndex) -> None                 # 将指定索引的项放到指定位置处

  列表视图控件 QListView 类的特有的信号如下:

indexesMoved(indexes:List[QModelIndex])                                         # 数据索引发生移动时发射信号

  用 setFlow(flow:QListView.Flow) 方法 设置数据项的排列方向,其中参数 flowQListView.Flow 类型的枚举值,可以取值如下:

QListView.Flow.LeftToRight                                                      # 值是0
QListView.Flow.TopToBottom                                                      # 值是1

  用 setLayoutMode(mode:QListView.LayoutMode) 方法设置数据的显示方式,其中参数 ``mode QListView.LayoutMode` 类型的枚举值,可以取值如下:

QListView.LayoutMode.SinglePass                                                 # 值是0,全部显示
QListView.LayoutMode.Batched                                                    # 值是1,分批显示

  用 setMovement(movement:QListView.Movement) 方法 设置数据项的拖拽方式,其中参数 movementQListView.Movement 类型的枚举值,可以取值如下:

QListView.Movement.Static                                                       # 不能移动
QListView.Movement.Free                                                         # 可以自由移动
QListView.Movement.Snap                                                         # 捕捉到数据项的位置

  用 setViewMode(mode:QListView.ViewMode) 方法 设置显示模式,参数 modeQListView.ViewMode 类型的枚举值,可以取值如下:

# 采用QListView.TopToBottom排列、小尺寸和QListView.Static不能移动方式
QListView.ViewMode.ListMode

# 采用QListView.LeftToRight排列、大尺寸和QListView.Free自由移动方式
QListView.ViewMode.IconMode

  用 setResizeMode(mode:QListView.ResizeMode) 方法 设置尺寸调整模式,参数 modeQListView.ResizeMode 类型的枚举值,可以取值如下:

QListView.ResizeMode.Fixed
QListView.ResizeMode.Adjust

  新建一个 ui.py 文件,用来存放 UI 相关的代码。

from PySide6.QtWidgets import QWidget
from PySide6.QtWidgets import QLabel, QListView
from PySide6.QtWidgets import QVBoxLayout
from PySide6.QtCore import QStringListModel

class MyUi:
    def setupUi(self, window:QWidget):
        window.resize(800, 600)                                                 # 1.设置窗口对象大小

        layout = QVBoxLayout(window)                                            # 2.创建一个垂直布局
      
        self.listView = QListView(window)                                       # 3.创建列表视图对象,并添加到布局中
        layout.addWidget(self.listView)

        self.listView.setSelectionMode(QListView.SelectionMode.ExtendedSelection)   # 4.设置选择模式
        self.listView.setViewMode(QListView.ViewMode.IconMode)                  # 5.设置显示模式
        self.listView.setWordWrap(True)                                         # 6.设置数据项是否换行

        model = QStringListModel()                                              # 7.创建数据模型
        self.listView.setModel(model)                                           # 8.为视图设置模型

        names = ["木之本樱", "御坂美琴", "夏娜", "赤瞳", "黑瞳"]
        model.setStringList(names)                                              # 9.向模型中添加数据

        self.label = QLabel()                                                   # 10.创建标签,并添加到布局中
        layout.addWidget(self.label)

  新建一个 widget.py 文件,用来存放业务逻辑相关的代码。

import sys

from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtCore import QModelIndex

from ui import MyUi

class MyWidget(QWidget):
    def __init__(self):
        super().__init__()                                                      # 1.调用父类Qwidget类的__init__()方法
      
        self.__ui = MyUi()
        self.__ui.setupUi(self)                                                 # 2.初始化页面

        self.__ui.listView.clicked.connect(self.listView_clicked)               # 3.列表项单击时激活信号
        self.__ui.listView.doubleClicked.connect(self.listView_double_clicked)  # 4.列表项双击时激活信号
        self.__ui.listView.entered.connect(self.listView_entered)               # 5.鼠标移入列表项时激活信号
        self.__ui.listView.pressed.connect(self.listView_pressed)               # 6.列表项被按下时激活信号
        self.__ui.listView.indexesMoved.connect(self.listView_indexes_moved)    # 7.列表项被移动时激活信号

    def listView_clicked(self, index:QModelIndex):
        self.__ui.label.setText(f"【{index.data()}】项被单击了")

    def listView_double_clicked(self, index:QModelIndex):
        self.__ui.label.setText(f"【{index.data()}】项被双击了")

    def listView_entered(self, index:QModelIndex):
        self.__ui.label.setText(f"鼠标移入【{index.data()}】项")

    def listView_pressed(self, index:QModelIndex):
        self.__ui.label.setText(f"【{index.data()}】项被按下了")

    def listView_indexes_moved(self, indexes:list[QModelIndex]):
        for index in indexes:
            self.__ui.label.setText(f"【{index.data()}】项被移动了")
  
if __name__ == "__main__":
    app = QApplication(sys.argv)                                                # 1.创建一个QApplication类的实例
    window = MyWidget()                                                         # 2.创建一个窗口
    window.show()                                                               # 3.显示窗口
    sys.exit(app.exec())                                                        # 4.进入程序的主循环并通过exit()函数确保主循环安全结束

4.2、表格视图控件

  表格视图控件 QTableView 可以用多行多列的单元格来显示标准数据模型,也可显示其他类型的数据模型。

  用 QTableView 创建的表格视图控件的方法如下所示:

QTableView(parent:QWidget=None)

  QTableView 类常用方法如下所示:

# 实例方法
rowAt(y:int) -> int                                                             # 获取y坐标位置处的行号
columnAt(x:int) -> int                                                          # 获取x坐标位置处的列号

rowViewportPosition(row:int) -> int                                             # 获取行号对应的视图位置
columnViewportPosition(column:int) -> int                                       # 获取列号对应的视图位置

setRowHidden(row:int, hide:bool) -> None                                        # 设置是否隐藏行
setColumnHidden(column:int, hide:bool) -> None                                  # 设置是否隐藏列

isRowHidden(row:int) -> bool                                                    # 获取指定行是否隐藏
isColumnHidden(column:int) -> bool                                              # 获取指定列是否隐藏

showGrid() -> bool                                                              # 获取表格线条是否显示
setGridStyle(style:Qt.PenStyle) -> None                                         # 设置表格线的样式

setRowHeight(row:int, height:int) -> None                                       # 设置行的高度
rowHeight(row:int) -> int                                                       # 获取行的高度
setColumnWidth(column:int, width:int) -> None                                   # 设置列的宽度
columnWidth(column:int) -> int                                                  # 获取列的宽度

setCornerButtonEnabled(enable:bool) -> None                                     # 设置是否激活右下角按钮
isCornerButtonEnabled() -> bool                                                 # 获取右下角按钮是否激活

setHorizontalHeader(header:QHeaderView) -> None                                 # 设置水平表头
horizontalHeader() -> QHeaderView                                               # 获取水平表头
setVerticalHeader(header:QHeaderView) -> None                                   # 设置垂直表头
verticalHeader() -> QHeaderView                                                 # 获取垂直表头

setSpan(row:int, column:int, rowSpan:int, columnSpan:int) -> None               # 设置单元格的行跨度和列跨度
rowSpan(row:int, column:int) -> int                                             # 获取单元格的行跨度
columnSpan(row:int, column:int) -> int                                          # 获取单元格的列跨度
clearSpans() -> None                                                            # 清除单元格的行跨度和列跨度

setWordWrap(on:bool) -> None                                                    # 设置是否自动换行

setSortingEnabled(enable:bool) -> None                                          # 设置是否启用排序
isSortingEnabled() -> bool                                                      # 获取是否启用排序

# 槽函数          
resizeRowToContents(row:int) -> None                                            # 自动调整指定行的高度
resizeRowsToContents() -> None                                                  # 自动调整所有行的高度   
resizeColumnToContents(column:int) -> None                                      # 自动调整指定列的宽度
resizeColumnsToContents() -> None                                               # 自动调整所有列的宽度

selectRow(row:int) -> None                                                      # 选择行
selectColumn(column:int) -> None                                                # 选择列

hideRow(row:int) -> None                                                        # 隐藏行
hideColumn(column:int) -> None                                                  # 隐藏列

showRow(row:int) -> None                                                        # 显示行
showColumn(column:int) -> None                                                  # 显示列

setShowGrid(show:bool) -> None                                                  # 设置是否显示网格  

sortByColumn(column:int, order:Qt.SortOrder)-> None                             # 按列进行排序

  用 setShowGrid(on:bool) 方法 设置是否显示表格线条,用 setGridStyle(style:Qt.PenStyle) 方法可以 设置表格线条的样式,其中参数 styleQt.PenStyle 类型的枚举值,可以取值如下:

Qt.PenStyle.NoPen                                                               # 没有表格线条
Qt.PenStyle.SolidLine                                                           # 实线
Qt.PenStyle.DashLine                                                            # 虚线
Qt.PenStyle.DotLine                                                             # 点线
Qt.PenStyle.DashDotLine                                                         # 点划线
Qt.PenStyle.DashDotDotLine                                                      # 双点划线
Qt.PenStyle.CustomDashLine                                                      # 用setDashPattern()方法自定义

  修改 ui.py 文件的内容。

from PySide6.QtWidgets import QWidget
from PySide6.QtWidgets import QLabel, QTableView
from PySide6.QtWidgets import QVBoxLayout
from PySide6.QtGui import QStandardItem, QStandardItemModel

class MyUi:
    def setupUi(self, window:QWidget):
        window.resize(800, 600)                                                 # 1.设置窗口对象大小

        layout = QVBoxLayout(window)                                            # 2.创建一个垂直布局
      
        self.tableView = QTableView(window)                                     # 3.创建表格视图,并添加到布局中
        layout.addWidget(self.tableView)

        self.tableView.setSortingEnabled(True)                                  # 4.设置单击头部时是否可以排序
        self.tableView.setAlternatingRowColors(True)                            # 5.设置表格颜色交错显示
        self.tableView.setSelectionBehavior(QTableView.SelectionBehavior.SelectRows)   # 6.设置表格的选择行为

        model = QStandardItemModel()                                            # 7.创建标准模型对象
        self.tableView.setModel(model)                                          # 8.设置要显示的数据模型
      
        model.setRowCount(15)                                                   # 9.设置表格的行数
        model.setColumnCount(10)                                                # 10.设置表格的列数

        model.setHorizontalHeaderLabels(["部门", "姓名", "性别", "年龄"])        # 11.设置表格的水平表头
        model.setVerticalHeaderLabels(["1", "2", "3"])                          # 12.设置表格的垂直表头

        department_list = ["魔法部", "超能力部"]
        person_list = [
            {"department": department_list[0], "name": "木之本樱", "gender": "女", "age": 10},
            {"department": department_list[0], "name": "夏娜", "gender": "女", "age": 15},
            {"department": department_list[1], "name": "御坂美琴", "gender": "女", "age": 14},
        ]

        for i in range(0, len(person_list)):
            j = 0
            for key, value in person_list[i].items():
                item = QStandardItem(str(value))                                # 13.创建数据项
                model.setItem(i, j, item)                                       # 14.设置单元格的内容
                j += 1

        self.tableView.setSpan(0, 0, 2, 1)                                      # 15.合并单元格

        self.label = QLabel()                                                   # 16.创建标签,并添加到布局中
        layout.addWidget(self.label)

  修改 widget.py 文件的内容。

import sys

from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtCore import QModelIndex

from ui import MyUi

class MyWidget(QWidget):
    def __init__(self):
        super().__init__()                                                      # 1.调用父类Qwidget类的__init__()方法
      
        self.__ui = MyUi()
        self.__ui.setupUi(self)                                                 # 2.初始化页面

        self.__ui.tableView.clicked.connect(self.tableView_clicked)                 # 3.单元格单击时激活信号
        self.__ui.tableView.doubleClicked.connect(self.tableView_double_clicked)    # 4.单元格双击时激活信号
        self.__ui.tableView.entered.connect(self.tableView_entered)                 # 5.鼠标移入单元格时激活信号
        self.__ui.tableView.pressed.connect(self.tableView_pressed)                 # 6.单元格被按下时激活信号

    def tableView_clicked(self, index:QModelIndex):
        self.__ui.label.setText(f"【{index.data()}】项被单击了")

    def tableView_double_clicked(self, index:QModelIndex):
        self.__ui.label.setText(f"【{index.data()}】项被双击了")

    def tableView_entered(self, index:QModelIndex):
        self.__ui.label.setText(f"鼠标移入【{index.data()}】项")

    def tableView_pressed(self, index:QModelIndex):
        self.__ui.label.setText(f"【{index.data()}】项被按下了")
  
if __name__ == "__main__":
    app = QApplication(sys.argv)                                                # 1.创建一个QApplication类的实例
    window = MyWidget()                                                         # 2.创建一个窗口
    window.show()                                                               # 3.显示窗口
    sys.exit(app.exec())                                                        # 4.进入程序的主循环并通过exit()函数确保主循环安全结束

4.3、树视图控件

  树视图控件 QTreeView 以树列表的形式显示文件系统模型关联的本机文件系统,显示出本机的目录、文件名、文件大小等信息,也可以以层级结构形式显示其他类型的数据模型。

QTreeView 类创建树视图控件的方法如下:

QTreeView(parent:QObject=None)

  QTreeView 类常用方法如下:

# 实例方法
setRootIsDecorated(show:bool) -> None                                           # 设置根部是否有折叠或展开标识
rootIsDecorated() -> bool                                                       # 获取根部是否有折叠或展开标识

indexAbove(index:QModelIndex) -> QModelIndex                                    # 返回指定索引的之前的索引
indexBelow(index:QModelIndex) -> QModelIndex                                    # 返回指定索引的之后的索引

setAnimated(enable:bool) -> None                                                # 设置是否使用动画
isAnimated() -> bool                                                            # 获取是否使用动画

setColumnHidden(column:int, hide:bool) -> None                                  # 设置指定列是否隐藏
isColumnHidden(column:int) -> bool                                              # 获取指定列是否隐藏
setColumnWidth(column:int, width:int) -> None                                   # 设置指定列的宽度
columnWidth(column:int) -> int                                                  # 获取指定列的宽度

setRowHidden(row:int, parent:QModelIndex, hide:bool) -> None                    # 设置相对于parent的第row是否隐藏
isRowHidden(row:int, parent:QModelIndex) -> bool                                # 获取相对于parent的第row是否隐藏
rowHeight(index:QModelIndex) -> int                                             # 获取指定行的高度

setItemsExpandable(enable:bool) -> None                                         # 设置是否允许展开和折叠
itemsExpandable() -> bool                                                       # 获取是否允许展开和折叠

setExpanded(index:QModelIndex, expand:bool) -> None                             # 设置是否展开指定的节点
setExpandsOnDoubleClick(enable:bool) -> None                                    # 设置双击是否展开节点

setFirstColumnSpanned(row:int, parent:QModelIndex, span:bool) -> None           # 设置某行的第一列的内容是否占据所有列
isFirstColumnSpanned(row:int, parent:QModelIndex) -> bool                       # 获取某行的第一列的内容是否占据所有列

setHeader(header:QHeaderView) -> None                                           # 设置表头
header() -> QHeaderView                                                         # 获取表头
setHeaderHidden(hide:bool) -> None                                              # 设置表头是否隐藏

setIndentation(i:int) -> None                                                   # 设置缩进
indentation() -> int                                                            # 获取缩进
resetIndentation() -> None                                                      # 重置缩进

setAutoExpandDelay(delay:int) -> None                                           # 拖放操作中设置项打开的延迟时间(毫秒)
autoExpandDelay() -> int                                                        # 获取项打开的延迟时间,如果为负,则不能打开

setAllColumnsShowFocus(enable:bool) -> None                                     # 设置所有列是否显示焦点
allColumnsShowFocus() -> bool                                                   # 获取所有列是否显示焦点
 
setUniformRowHeights(uniform:bool) -> None                                      # 设置所有行的高度是否一致
uniformRowHeights() -> bool                                                     # 获取所有行的高度是否一致
 
setWordWrap(on:bool) -> None                                                    # 设置是否自动换行
  
setTreePosition(logicalIndex:int) -> None                                       # 设置树位置
treePosition() -> int                                                           # 获取树位置

setSortingEnabled(enable:bool) -> None                                          # 设置是否允许排序
isSortingEnabled() -> bool                                                      # 获取是否允许排序

# 槽方法      
collapse(index:QModelIndex) -> None                                             # 折叠指定的节点
collapseAll() -> None                                                           # 折叠所有节点

expand(index:QModelIndex) -> None                                               # 展开指定的节点
expandAll() -> None                                                             # 展开所有节点

expandRecursively(index:QModelIndex, depth:int=-1) -> None                      # 逐级展开,展开深度是depth-1表示展开所有节点,0表示只展开本层
expandToDepth(depth:int) -> None                                                # 展开到指定深度

hideColumn(column:int) -> None                                                  # 隐藏指定列
showColumn(column:int) -> None                                                  # 显示指定列

sortByColumn(column:int, order:Qt.SortOrder) -> None                            # 按列进行排序
resizeColumnToContents(column:int) -> None                                      # 根据内容调整列的尺寸

  QTreeView 类常用信号如下:

collapsed(index:QModelIndex)                                                    # 折叠节点时发射信号
expanded(index:QModelIndex)                                                     # 展开节点时发射信号

  修改 ui.py 文件的内容。

from PySide6.QtWidgets import QWidget
from PySide6.QtWidgets import QLabel, QTreeView, QFileSystemModel
from PySide6.QtWidgets import QVBoxLayout

class MyUi:
    def setupUi(self, window:QWidget):
        window.resize(800, 600)                                                 # 1.设置窗口对象大小

        layout = QVBoxLayout(window)                                            # 2.创建一个垂直布局
      
        self.treeView = QTreeView(window)                                       # 3.创建一个树形控件,并添加到布局中
        layout.addWidget(self.treeView)

        self.treeView.setAlternatingRowColors(True)                             # 4.设置每间隔一行颜色是否一致

        self.treeView.setSelectionMode(QTreeView.SelectionMode.ExtendedSelection)   # 5.设置选中模式
        self.treeView.setSelectionBehavior(QTreeView.SelectionBehavior.SelectItems) # 6.设置选中方式

        model = QFileSystemModel()                                              # 7.创建文件系统模型对象
        self.treeView.setModel(model)                                           # 8.设置要显示的数据模型

        model.setRootPath("/")                                                  # 9.设置根路径为当前系统根目录
        self.treeView.expandToDepth(1)                                          # 10.展开到指定深度

        self.label = QLabel()                                                   # 11.创建标签对象,并添加到布局中
        layout.addWidget(self.label)

  修改 widget.py 文件的内容。

import sys

from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtCore import QModelIndex

from ui import MyUi

class MyWidget(QWidget):
    def __init__(self):
        super().__init__()                                                      # 1.调用父类Qwidget类的__init__()方法
      
        self.__ui = MyUi()
        self.__ui.setupUi(self)                                                 # 2.初始化页面

        self.__ui.treeView.clicked.connect(self.treeView_clicked)               # 3.单元格单击时激活信号
        self.__ui.treeView.doubleClicked.connect(self.treeView_double_clicked)  # 4.单元格双击时激活信号
        self.__ui.treeView.entered.connect(self.treeView_entered)               # 5.鼠标移入单元格时激活信号
        self.__ui.treeView.pressed.connect(self.treeView_pressed)               # 6.单元格被按下时激活信号
        self.__ui.treeView.collapsed.connect(self.treeView_collapsed)           # 7.树形视图被折叠时激活信号
        self.__ui.treeView.expanded.connect(self.treeView_expanded)             # 8.树形视图被展开时激活信号

    def treeView_clicked(self, index:QModelIndex):
        self.__ui.label.setText(f"【{index.data()}】项被单击了")

    def treeView_double_clicked(self, index:QModelIndex):
        self.__ui.label.setText(f"【{index.data()}】项被双击了")

    def treeView_entered(self, index:QModelIndex):
        self.__ui.label.setText(f"鼠标移入【{index.data()}】项")

    def treeView_pressed(self, index:QModelIndex):
        self.__ui.label.setText(f"【{index.data()}】项被按下了")

    def treeView_collapsed(self, index:QModelIndex):
        self.__ui.label.setText(f"【{index.data()}】项被收起了")

    def treeView_expanded(self, index:QModelIndex):
        self.__ui.label.setText(f"【{index.data()}】项被展开了")
  
if __name__ == "__main__":
    app = QApplication(sys.argv)                                                # 1.创建一个QApplication类的实例
    window = MyWidget()                                                         # 2.创建一个窗口
    window.show()                                                               # 3.显示窗口
    sys.exit(app.exec())                                                        # 4.进入程序的主循环并通过exit()函数确保主循环安全结束

五、代理控件

  在视图控件中双击某个数据项,可以修改数据项当前显示的值,即可以输入新的值。输入新值时,并不是直接在视图控件上输入(视图控件只具有显示数据的功能),而是在视图控件的单元格位置出现一个新的可以输入数据的控件,例如 QLineEditQLineEdit 读取数据项的值作为初始值,供用户修改,修改完成后通过数据项的索引把数据保存到数据模型中,并通知视图控件显示新的数据,像这种为视图控件提供编辑功能的控件称为 代理控件委托控件

  系统为每种数据类型定义了默认的代理控件,用户也可以自定义代理控件。定义代理控件需要用 QStyledItemDelegate 类或 QItemDelegate 类创建子类,这两个类都继承自 QAbstractItemDelegate 类。这两个类的主要区别是前者可以使用当前的样式表来设置代理控件的样式,因此建议使用前者来定义代理控件。

  在 QStyledItemDelegateQItemDelegate 的子类中定义代理控件的类型、位置,以及如何读取和返回数据。视图控件都有从 QAbstractItemView 继承而来的方法。

setItemDelegate(delegate:QAbstractItemDelegate)                                 # 为指定的数据项设置代理控件
setItemDelegateForColumn(column:int, delegate:QAbstractItemDelegate)            # 为指定的列数据项设置代理控件
setItemDelegateForRow(row:int, delegate:QAbstractItemDelegate)                  # 为指定的行数据项设置代理控件

  创建代理控件可以用项编辑器工厂 QItemEditorFactory 定义默认的代理控件,也可以自定义代理控件的类型。

  自定义代理控件需要重写 QStyledItemDelegate 类或 QItemDelegate 类的下面 4 个函数:

# 用于创建代理控件的实例对象并返回该实例对象
createEditor(parent:QWidget,option:QStyleOptionViewItem,index:QModelIndex | QPersistentModelIndex) -> QWidget

# 用于读取视图控件的数据项的值到代理控件中
setEditorData(editor:QWidget,index:QModelIndex | QPersistentModelIndex) -> None

# 用于将编辑后的代理控件的值返回到数据模型中
setModelData(editor:QWidget,model:QAbstractItemModel,index:QModelIndex | QPersistentModelIndex) -> None

# 用于设置代理控件显示的位置
updateEditorGeometry(editor:QWidget,option:QStyleOptionViewItem,index:QModelIndex | QPersistentModelIndex) -> None

  createEditor(parent:QWidget,option:QStyleOptionViewItem,index:QModelIndex) 函数用于 创建需要的编辑器控件,并作为函数的返回值。参数 parent代理控件的父容器对象。参数 optionQStyleOptionViewItem 类型变量,可以对创建的编辑器控件的效果做一些高级设置。参数 indexQModelIndex 变量,是 关联数据项的模型索引,通过 index.model() 可以 获取关联的数据模型

  QStyleOptionViewItem 传递的一些属性用于确定代理控件的位置和外观,其属性如下所示。

QStyleOptionViewItemcol 的属性 属性值的类型 说明
backgroundBrush QBursh 项的背景画刷
checkState Qt.CheckState 项的勾选状态
decorationAlignment Qt.Alignment 项的图标对齐位置
decorationPosition QStyleOptionViewItem.Postion 项的图标位置
decorationSize QSize 项的图标尺寸
displayAlignment Qt.Alignment 项的文字对齐方式
features QStyleOptionViewItem.ViewItemFeatures 项所具有的特征
font QFont 项的字体
icon QIcon 项的图标
index QModelIndex 项的模型索引
showDecorationSelected bool 项是否显示图标
text QString 项显示的文本
textElideMode Qt.TextElideMode 省略号的模式
viewItemPosition QStyleOptonViewItem.ViewItemPosition 项在行中的位置
direction Qt.LayoutDirection 布局方向
palette QPalette 调色板
rect QRect 项的矩形区域
styleObject QObject 项的窗口类型
version int 版本

  其中枚举值 QStyleOptionViewItem.Position 可取值如下:

QStyleOptionViewItem.Position.Left
QStyleOptionViewItem.Position.Right
QStyleOptionViewItem.Position.Top
QStyleOptionViewItem.Position.Bottom

  枚举值 QStyleOptionViewItem.ViewItemFeatures 可取值如下:

QStyleOptionViewItem.ViewItemFeatures.None
QStyleOptionViewItem.ViewItemFeatures.WrapText
QStyleOptionViewItem.ViewItemFeatures.Alternate
QStyleOptionViewItem.ViewItemFeatures.HasCheckIndicator
QStyleOptionViewItem.ViewItemFeatures.HasDisplay
QStyleOptionViewItem.ViewItemFeatures.HasDecoration

  枚举值 QStyleOptionViewItem.ViewItemPosition 可取值如下:

QStyleOptionViewItem.ViewItemPosition.Beginning
QStyleOptionViewItem.ViewItemPosition.Middle
QStyleOptionViewItem.ViewItemPosition.End
QStyleOptionViewItem.ViewItemPosition.OnlyOne                                   # 行中只有一个项,两端对齐

  setEditorData(editor:QWidget,index:QModelIndex) 函数用于 从数据模型获取数据,设置为编辑器的显示值双击一个单元格进入编辑状态时,就会自动调用此函数。参数 editor 指向 代理编辑器控件。参数 index关联的数据单元的模型索引index.model() 指向关联的数据模型,然后通过模型的 data(index, Qt.EditRole) 函数 获取单元格的显示文字

  setModelData(editor:QWidget,model:QAbstractItemModel,index:QModelIndex) 函数用于 将代理编辑器上的值更新到数据模型当用户在界面上完成编辑时会自动调用此函数。参数 editor所创建的编辑器组件model关联的数据模型index关联的单元格的模型索引。程序里可以先获取代理组件编辑器 editor 的数值 value,然后将这个值更新到数据模型。

  updateEditorGeometry(editor:QWidget,option:QStyleOptionViewItem,index:QModelIndex) 函数用于 为代理控件 editor 的显示效果进行设置,参数 optionQStyleOptionViewItem 类型变量,其 rect 属性 定义了单元格适合显示代理组件的大小,程序中可以直接设置为此值。index关联数据项的模型索引

  新建一个 delegate.py 文件,用来存放代理控件相关的代码。

from PySide6.QtWidgets import QWidget
from PySide6.QtWidgets import QComboBox, QSpinBox
from PySide6.QtWidgets import QStyledItemDelegate, QStyleOptionViewItem
from PySide6.QtGui import Qt
from PySide6.QtCore import QModelIndex, QPersistentModelIndex

class ComboBoxDelegate(QStyledItemDelegate):

    def createEditor(self, parent, option, index):
        # 创建下拉组合控件并返回
        comboBox = QComboBox(parent)
        comboBox.addItems(["男", "女"])
        return comboBox

    def setEditorData(self, editor, index):
        # 将模型中的数据设置到编辑器中
        data = index.data()
        editor.setCurrentText(data)

    def setModelData(self, editor, model, index):
        # 将编辑器中的数据更新回模型
        value = editor.currentText()
        model.setData(index, value, Qt.ItemDataRole.EditRole)

    def updateEditorGeometry(self, editor:QWidget, option:QStyleOptionViewItem, index:QModelIndex | QPersistentModelIndex):
        # 更新编辑器的几何布局
        editor.setGeometry(option.rect)

class SpinBoxDelegate(QStyledItemDelegate):

    def createEditor(self, parent, option, index):
        # 创建数字选择控件并返回
        spinBox = QSpinBox(parent)
        spinBox.setRange(0, 300)
        return spinBox

    def setEditorData(self, editor, index):
        # 将模型中的数据设置到编辑器中
        value = index.data()
        editor.setValue(int(value))

    def setModelData(self, editor, model, index) -> None:
        # 将编辑器中的数据更新回模型
        value = editor.value()
        model.setData(index, value, Qt.ItemDataRole.EditRole)

    def updateEditorGeometry(self, editor, option, index) -> None:
        # 更新编辑器的几何布局
        editor.setGeometry(option.rect)

  修改 ui.py 文件的内容。

from PySide6.QtWidgets import QWidget
from PySide6.QtWidgets import QTableView
from PySide6.QtWidgets import QVBoxLayout
from PySide6.QtGui import QStandardItem, QStandardItemModel

from delegate import ComboBoxDelegate, SpinBoxDelegate

class MyUi:
    def setupUi(self, window:QWidget):
        window.resize(800, 600)                                                 # 1.设置窗口对象大小

        layout = QVBoxLayout(window)                                            # 2.创建一个垂直布局

        self.tableView = QTableView(window)                                     # 3.创建一个表格控件,并添加到布局中
        layout.addWidget(self.tableView)

        self.tableView.setSortingEnabled(True)                                  # 4.设置单击头部时是否可以排序
        self.tableView.setAlternatingRowColors(True)                            # 5.设置表格颜色交错显示
        self.tableView.setSelectionBehavior(QTableView.SelectionBehavior.SelectRows)   # 6.设置表格的选择行为

        model = QStandardItemModel()                                            # 7.创建标准模型对象
        self.tableView.setModel(model)                                          # 8.设置要显示的数据模型

        model.setRowCount(15)                                                   # 9.设置表格的行数
        model.setColumnCount(10)                                                # 10.设置表格的列数
      
        model.setHorizontalHeaderLabels(["部门", "姓名", "性别", "年龄"])          # 11.设置表格的水平表头
        model.setVerticalHeaderLabels(["1", "2", "3"])                          # 12.设置表格的垂直表头

        department_list = ["魔法部", "超能力部"]
        person_list = [
            {"department": department_list[0], "name": "木之本樱", "gender": "女", "age": 10},
            {"department": department_list[0], "name": "夏娜", "gender": "女", "age": 15},
            {"department": department_list[1], "name": "御坂美琴", "gender": "女", "age": 14},
        ]

        for i in range(0, len(person_list)):
            j = 0
            for key, value in person_list[i].items():
                item = QStandardItem(str(value))                                # 13.创建表格项
                model.setItem(i, j, item)                                       # 14.设置单元格的内容
                j += 1

        delegate_gril = ComboBoxDelegate(self.tableView)                        # 15.创建下拉组合框代理控件实例对象
        self.tableView.setItemDelegateForColumn(2, delegate_gril)               # 19.只对第三列(性别)使用下拉组合框代理控件

        delegate_age = SpinBoxDelegate(self.tableView)                          # 20.创建整数数字选择代理控件实例对象
        self.tableView.setItemDelegateForColumn(3, delegate_age)                # 21.只对第四列(年龄)使用整数数字选择代理控件

  修改 widget.py 文件的内容。

import sys

from PySide6.QtWidgets import QApplication, QWidget

from ui import MyUi

class MyWidget(QWidget):
    def __init__(self):
        super().__init__()                                                      # 1.调用父类Qwidget类的__init__()方法
      
        self.__ui = MyUi()
        self.__ui.setupUi(self)                                                 # 2.初始化页面
  
if __name__ == "__main__":
    app = QApplication(sys.argv)                                                # 1.创建一个QApplication类的实例
    window = MyWidget()                                                         # 2.创建一个窗口
    window.show()                                                               # 3.显示窗口
    sys.exit(app.exec())                                                        # 4.进入程序的主循环并通过exit()函数确保主循环安全结束
posted @ 2025-01-16 18:31  星光映梦  阅读(131)  评论(0)    收藏  举报