25. 模型与视图

一、什么是模型

  对于存储在本机上的数据,我们可以先把数据读取到一个能保存数据的类中,或者类不直接读取数据,但能提供读取数据的接口,然后用能显示数据的控件把数据从模型中读取并显示出来,显示数据的控件并不存储数据,显示的数据只是数据的一个映射。像这种能保存数据或者能提供数据接口的类称为 数据模型(model),把数据模型中的数据显示出来的控件称为 视图控件 (view)。

  要修改或增删视图控件中显示的数据,一种方法是在后台的数据模型中直接修改或增删数据,数据模型中的数据改变了,视图控件中显示的数据也会同时改变,视图控件不直接编辑数据,视图控件显示的数据只是对数据模型中数据的一种映射,是单向的;另一种方法是调用可以编辑数据的控件,在编辑控件中修改数据,修改完成后,通过信号通知数据模型和视图控件,数据模型中的数据和视图控件显示的数据也同时发生改变,像这种用于编辑数据的控件称为 代理控件

模型与视图机制

  我们可以在终端中使用 pip 安装 pyside6 模块。

pip install pyside6

二、数据模型的类型

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

数据模型的继承关系

  数据模型存储数据的 3 种常见结构形式,主要有 列表模型(list model)、表格模型(table model)和 树模型(tree model)。列表模型中的数据没有层级关系,由一列多行数据构成;表格模型由多行多列数据构成;树结构模型的数据是有层级关系的,每层数据下面还有子层数据。

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

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

  数据模型中存放着数据,要获取或写入数据,需要知道数据所在的行和列。行和列单独构成一个类,称为 数据项索引 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                                 # 获取列索引
data(role:int=Qt.DisplayRole) -> Any            # 获取数据相关指定角色的数据
flags() -> Qt.ItemFlag                          # 获取标识
isValid() -> bool                               # 判断索引是否有效

  用 parent() 方法可以获得父数据项的索引;用 sibling(row,column) 方法、siblingAtColumn(column) 方法和 siblingAtRow(row) 方法可以获取同级别的 row 行 column 列的数据项的索引;用 isValid() 方法可以判断索引是否有效;用 row() 方法和 column() 方法可以获取数据索引所指向的行值和列值;用 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种状态间切换

三、抽象模型与抽象视图

3.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
# 移除单行,成功返回True
removeRow(row:int, parent:QModelIndex=QModelIndex()) -> bool
# 移除多行,成功返回True
removeRows(row:int, count:int, parent:QModelIndex=QModelIndex()])

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

  用 setData(QModelIndex,Any,role=Qt.ItemDataRole) 方法可以设置数据项的某角色值,用 setItemData(QModelIndex,roles:Dict[int,Any]) 方法可以用字典方式设置某数据项的多个角色值,用 data(QModelIndex,role=Qt.ItemDataRole)itemData(QModelIndex) 方法获取角色值,其中参数 Qt.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) 方法设置表头某角色的值,当 orientation 取 Qt.Horizontal 时,section 是指列;orientation 取 Qt.Vertical 时,section 是指行。

  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()

3.2、抽象视图

  所有的视图都继承于 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                                                        # 清空选择

  用 setModel(QAbstractItemModel) 方法可以给视图控件设置关联的数据模型;用 setRootIndex(QModelIndex) 方法设置视图控件需要显示的数据索引下的子数据项。用 setSelectionMode(QAbstractItemView.SelectionMode) 方法可以设置选择模式,其中参数 QAbstractItemView.SelectionMode 的取值如下:

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

  QAbstractItemView 类常用的信号如下:

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

四、文本列表模型与列表视图

4.1、文本列表模型

  文本列表模型 QStringListModel 通常用于存储一维文本列表,它由一列多行文本数据构成。用于显示 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]                           # 获取文本列表

4.2、列表视图控件

  列表视图控件 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     # 将指定索引的项放到指定位置处

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

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

  用 setLayoutMode(QListView.LayoutMode) 方法设置数据的显示方式,其中 QListView.LayoutMode 可取值如下:

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

  用 setMovement(QListView.Movement) 方法设置数据项的拖拽方式,其中 QListView.Movement 可取值如下:

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

  用 setViewMode(QListView.ViewMode) 方法设置显示模式,参数 QListView.ViewMode 可以取值如下:

QListView.ViewMode.ListMode    # 采用QListView.TopToBottom排列、小尺寸和QListView.Static不能移动方式
QListView.ViewMode.IconMode,  # 采用QListView.LeftToRight排列、大尺寸和QListView.Free自由移动方式

  用 setResizeMode(QListView.ResizeMode) 方法设置尺寸调整模式,参数可取值如下:

QListView.ResizeMode.Fixed
QListView.ResizeMode.Adjust

  列表视图控件 QListView 类的常用信号如下:

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

from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtWidgets import QListView
from PySide6.QtCore import QStringListModel

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

    def setup_ui(self):
        # 1.设置窗口对象大小
        self.resize(700, 500)

        # 2.创建数据模型
        model = QStringListModel()

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

        # 4.创建列表视图对象
        listView = QListView(self)
        listView.resize(300, 100)

        # 5.为视图设置模型
        listView.setModel(model)

        # 6.设置选择模式
        listView.setSelectionMode(QListView.SelectionMode.ExtendedSelection)

        # 7.设置选择项的方式
        listView.setSelectionBehavior(QListView.SelectionBehavior.SelectItems)

        # 8.设置显示模式
        listView.setViewMode(QListView.ViewMode.IconMode)

        # 9.设置是否自动换行
        listView.setWordWrap(True)

        # 10.定义信号与槽的连接
        listView.clicked.connect(self.clicked)
        listView.doubleClicked.connect(self.doubleClicked)
        listView.entered.connect(self.entered)
        listView.pressed.connect(self.pressed)

    def clicked(self, index):
        print(f"【{index.data()}】项被单击了")

    def doubleClicked(self, index):
        print(f"【{index.data()}】项被双击了")

    def entered(self, index):
        print(f"鼠标移入【{index.data()}】项")

    def pressed(self, index):
        print(f"【{index.data()}】项被按下了")

if __name__ == "__main__":
    # 1.创建一个QApplication类的实例
    app = QApplication(sys.argv)
    # 2.创建一个窗口
    window = MyWidget()
    # 3.展示窗口
    window.show()
    # 4.进入程序的主循环并通过exit()函数确保主循环安全结束
    sys.exit(app.exec())

五、文件系统模型与树视图

5.1、文件系统模型

  利用文件系统模型 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                                      # 获取文件类型

  用 setRootPath(path:str) 方法设置模型的根目录,并返回指向该目录的模型数据索引。改变根目录时,发送 rootPathChanged(newPath) 信号。用 rootPath() 方法获取根目录。

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

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

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

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.AllDirs,否则无法识别路径的结构。

  QFileSystemModel 类的常用信号如下:

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

5.2、树视图

  树视图控件 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)         # 展开节点时发射信号
import sys

from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtWidgets import QTreeView, QFileSystemModel
from PySide6.QtCore import Qt

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

    def setup_ui(self):
        # 1.设置窗口对象大小
        self.resize(700, 500)

        # 2.创建文件系统模型对象
        model = QFileSystemModel()

        # 3.设置根路径为当前系统根目录
        model.setRootPath("/")

        # 4.创建树视图
        treeView = QTreeView(self)
        treeView.resize(700, 500)

        # 5.设置要显示的数据模型
        treeView.setModel(model)

        # 6.设置是否隐藏头部
        treeView.setHeaderHidden(False)

        # 7.设置单击头部时是否可以排序
        treeView.setSortingEnabled(True)

        # 8.设置是否显示垂直滚动条
        treeView.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)

        # 9.设置是否显示水平滚动条
        treeView.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)

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

        # 11.设置是否自动换行
        treeView.setWordWrap(False)

        # 12.设置是否支持双击展开树节点
        treeView.setExpandsOnDoubleClick(True)

        # 13.设置选中模式
        treeView.setSelectionMode(QTreeView.SelectionMode.ExtendedSelection)

        # 14.设置选中方式
        treeView.setSelectionBehavior(QTreeView.SelectionBehavior.SelectItems)

        # 15.设置自动展开的时间,单位为毫秒,如果值小于0,表示禁用自动展开
        treeView.setAutoExpandDelay(-1)

        # 16.收缩所有节点
        treeView.collapseAll()

        # 17.定义信号与槽的连接
        treeView.clicked.connect(self.clicked)
        treeView.doubleClicked.connect(self.doubleClicked)
        treeView.entered.connect(self.entered)
        treeView.pressed.connect(self.pressed)
        treeView.collapsed.connect(self.collapsed)
        treeView.expanded.connect(self.expanded)

    def clicked(self, index):
        print(f"【{index.data()}】节点被单击了")

    def doubleClicked(self, index):
        print(f"【{index.data()}】节点被双击了")

    def entered(self, index):
        print(f"鼠标移入【{index.data()}】节点")

    def pressed(self, index):
        print(f"【{index.data()}】节点被按下了")

    def collapsed(self, index):
        print(f"你收缩了【{index.data()}】节点")

    def expanded(self, index):
        print(f"你展开了【{index.data()}】节点")

if __name__ == "__main__":
    # 1.创建一个QApplication类的实例
    app = QApplication(sys.argv)
    # 2.创建一个窗口
    window = MyWidget()
    # 3.展示窗口
    window.show()
    # 4.进入程序的主循环并通过exit()函数确保主循环安全结束
    sys.exit(app.exec())

六、标准数据模型与表格视图

6.1、标准数据模型

  标准数据模型 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                                                                           # 获取排序角色

6.2、数据项

  用 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  # 对列进行排序

6.3、表格视图

  表格视图控件 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(bool) 方法设置是否显示表格线条,用 setGridStyle(Qt.PenStyle) 方法可以设置表格线条的样式,其中参数 Qt.PenStyle 可取值如下:

Qt.PenStyle.NoPen            # 没有表格线条
Qt.PenStyle.SolidLine
Qt.PenStyle.DashLine
Qt.PenStyle.DotLine
Qt.PenStyle.DashDotLine
Qt.PenStyle.DashDotDotLine
Qt.PenStyle.CustomDashLine   # 用setDashPattern()方法自定义
import sys

from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtWidgets import QTableView
from PySide6.QtGui import QStandardItemModel, QStandardItem
from PySide6.QtCore import Qt

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

    def setup_ui(self):
        # 1.设置窗口对象大小
        self.resize(700, 500)

        # 2.创建标准模型对象
        model = QStandardItemModel()

        # 3.设置表格的行数
        model.setRowCount(3)

        # 4.设置表格的列数
        model.setColumnCount(4)

        # 5.设置表格的水平表头
        model.setHorizontalHeaderLabels(["部门", "姓名", "性别", "年龄"])

        # 6.设置表格的垂直表头
        model.setVerticalHeaderLabels(["1", "2", "3"])

        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))
                # 7.设置单元格的内容
                model.setItem(i, j, item)
                j += 1

        # 8.创建表格视图
        tableView = QTableView(self)
        tableView.resize(700, 500)

        # 9.设置要显示的数据模型
        tableView.setModel(model)

        # 10.设置表格列的宽度跟随内容改变
        tableView.resizeColumnsToContents()

        # 11.设置表格行的高度跟随内容改变
        tableView.resizeRowsToContents()

        # 12.设置单元格是否可以编辑
        tableView.setEditTriggers(QTableView.EditTrigger.DoubleClicked)

        # 13.设置表格颜色交错显示
        tableView.setAlternatingRowColors(True)

        # 14.设置选中模式
        tableView.setSelectionMode(QTableView.SelectionMode.ExtendedSelection)

        # 15.设置表格的选择行为
        tableView.setSelectionBehavior(QTableView.SelectionBehavior.SelectItems)

        # 16.设置是否自动换行
        tableView.setWordWrap(True)

        # 17.设置是否显示网格
        tableView.setShowGrid(True)

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

        # 19.设置单击头部时是否可以排序
        tableView.setSortingEnabled(True)

        # 20.设置是否显示垂直滚动条
        tableView.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)

        # 21.设置是否显示水平滚动条
        tableView.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)

        # 22.定义信号与槽的连接
        tableView.clicked.connect(self.clicked)
        tableView.doubleClicked.connect(self.doubleClicked)
        tableView.pressed.connect(self.pressed)

    def clicked(self, index):
        print(f"【{index.data()}】单元格被单击了")

    def doubleClicked(self, index):
        print(f"【{index.data()}】单元格被双击了")

    def pressed(self, index):
        print(f"【{index.data()}】单元格被按下了")

if __name__ == "__main__":
    # 1.创建一个QApplication类的实例
    app = QApplication(sys.argv)
    # 2.创建一个窗口
    window = MyWidget()
    # 3.展示窗口
    window.show()
    # 4.进入程序的主循环并通过exit()函数确保主循环安全结束
    sys.exit(app.exec())

七、选择模型

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

  视图控件都有 setSelectionModel(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()信号

  其中用 selection() 方法可以获取项的选择集 QItemSelection;用 select(index:QModelIndex,command:QItemSelectionModel.SelectionFlag) 方法可以往选择集中添加内容,或从选择集中移除选择,其中 command 是 QItemSelectionModel.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

  QItemSelectionModel 类常用信号如下:

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

  选择集 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                                                                      # 返回选择集中的项数量

八、代理控件

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

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

  在 QStyledItemDelegate 或 QItemDelegate 的子类中定义代理控件的类型、位置,以及如何读取和返回数据。视图控件都有从 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) -> QWidget

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

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

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

  createEditor() 函数中的参数 parent 指代理控件所在的窗口,通常取视图控件所在的窗体;其他 3 个函数的 editor 指 createEditor() 返回的代理控件,用于传递数据;QModelIndex 是数据项的索引,系统会给实参传递索引; 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    # 行中只有一个项,两端对齐
import sys

from PySide6.QtWidgets import QApplication, QWidget
from PySide6.QtWidgets import QTableView
from PySide6.QtWidgets import QStyledItemDelegate
from PySide6.QtWidgets import QComboBox, QSpinBox
from PySide6.QtGui import QStandardItemModel, QStandardItem, Qt

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) -> None:
        # 将编辑器中的数据更新回模型
        value = editor.currentText()
        model.setData(index, value, Qt.ItemDataRole.EditRole)

    def updateEditorGeometry(self, editor, option, index) -> None:
        # 更新编辑器的几何布局
        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)

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

    def setup_ui(self):
        # 1.设置窗口对象大小
        self.resize(700, 500)

        # 2.创建标准模型对象
        model = QStandardItemModel()

        # 3.设置表格的行数
        model.setRowCount(3)

        # 4.设置表格的列数
        model.setColumnCount(4)

        # 5.设置表格的水平表头
        model.setHorizontalHeaderLabels(["部门", "姓名", "性别", "年龄"])

        # 6.设置表格的垂直表头
        model.setVerticalHeaderLabels(["1", "2", "3"])

        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))
                # 7.设置单元格的内容
                model.setItem(i, j, item)
                j += 1

        # 8.创建表格视图
        tableView = QTableView(self)
        tableView.resize(700, 500)

        # 9.设置要显示的数据模型
        tableView.setModel(model)

        # 10.设置单元格是否可以编辑
        tableView.setEditTriggers(QTableView.EditTrigger.DoubleClicked)

        # 11.设置表格颜色交错显示
        tableView.setAlternatingRowColors(True)

        # 12.设置选中模式
        tableView.setSelectionMode(QTableView.SelectionMode.ExtendedSelection)

        # 13.设置表格的选择行为
        tableView.setSelectionBehavior(QTableView.SelectionBehavior.SelectItems)

        # 14.设置是否自动换行
        tableView.setWordWrap(True)

        # 15.设置是否显示网格
        tableView.setShowGrid(True)

        # 16.设置单击头部时是否可以排序
        tableView.setSortingEnabled(True)

        # 17.设置是否显示垂直滚动条
        tableView.setVerticalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)

        # 18.设置是否显示水平滚动条
        tableView.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAsNeeded)

        # 19.只对第三列(性别)应用代理
        delegate_gril = ComboBoxDelegate(tableView)
        tableView.setItemDelegateForColumn(2, delegate_gril)

        # 20.只对第四列(年龄)应用代理
        delegate_age = SpinBoxDelegate(tableView)
        tableView.setItemDelegateForColumn(3, delegate_age)

if __name__ == "__main__":
    # 1.创建一个QApplication类的实例
    app = QApplication(sys.argv)
    # 2.创建一个窗口
    window = MyWidget()
    # 3.展示窗口
    window.show()
    # 4.进入程序的主循环并通过exit()函数确保主循环安全结束
    sys.exit(app.exec())
posted @   星光映梦  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
历史上的今天:
2024-01-13 43. FSMC驱动LCD屏
点击右上角即可分享
微信分享提示