PyQt中的模型与视图框架
一、Qt中模型与视图相关的类
二、模型与视图的基本原理
-
MVC把图形界面分为三个部分:模型(Model),视图(View)和控制器(Controller),
- 模型(Model):用于管理数据,注意,数据不一定需要位于模型之中
- 视图(View):就是呈现在用户面前的界面外观,视图负责把模型中的数据展现给用户。
- 控制器(Controller):用于处理用户在用户界面的输入
-
MVC把需要处理的数据及其显示分离开来。
-
Qt实现的MVC模型:
其实Qt中的MVC并不叫MVC,而是叫“MVD”,Qt中没有Controller的说法,而是使用了另外一种抽象: Delegate (委托) ,其行为和传统的MVC是相同的。
- Qt把视图和控制器组合在一起,从而形成模型/视图结构。
- 模型直接与数据进行通信,并为视图和委托提供访问数据的接口。
- 数据与模型:数据不一定要存储在模型中,数据也可以存储在文件、数据库等其他地方,存储的数据不一定拥有一种数据结构,但是模型通常会把这些数据组织成一种数据结构(比如列表List结构、树形tree结构等),这种结构只是逻辑上的结构,数据本身不一定拥有这种结构。然后视图根据模型提供的逻辑结构显示数据,比如列表视图QListView可以显示把数据组织成列表结构的模型(QStandardItemModel模型实现了该种结构)中的数据。当然了,也可以使用列表视图显示组织为树形结构的数据,但这样做需要做一些比较麻烦的处理才能正确显示,所以通常应使用列表视图显示列表模型的数据,树形视图显示树形结构模型的数据。
- 为了对用户的输入进行灵活的处理,Qt引入了Delegate(代理/委托)的概念,委托其实就是把用户输入的数据委托给Qt的某个部件处理,比如委托QSpinBox部件来处理用户输入的整数等。另外,委托还会绘制(或渲染)视图中的个别数据项。
- 模型、视图、委托之间的通信使用Qt的信号和槽机制来完成。
- 下面以实际的图形界面为例,介绍了MVC各部分之间的功能
-
Qt对模型/视图结构的具体实现
-
模型:Qt使用抽象类QAbstractItemModel来描述模型,所有的模型都是通过子类化该抽象类而实现的。Qt实现了一些标准的现成模型,比如:
- QStringListModel:用于存储QString项目的列表
- QStandardItemModel:该模型可以被当做列表模型、表格模型、树形模型来使用
- QFileSystemModel:该模型提供本地文件系统中的文件和目录信息,模型本身没有任何的数据项目
- QSqlQueryModel、QSqlTableModel、QSqlRelationalTableModel与数据库有关。
-
视图:Qt使用抽象类QAbstractItemView来描述视图,所有的视图都是通过子类化该抽象类而实现的。Qt实现了一些标准的现成视图,比如QListView,QTableView,QTreeView等等。
-
委托:Qt使用抽象类QAbstractItemDelegate来描述委托,Qt实现了两个委托类,QStyledItemDelegate和QItemDelegate,这两个委托之中只能使用其中一个,其区别在于QItemDelegate总是使用一种默认的样式绘制数据项,而QStyledItemDelegate使用当前的样式来绘制数据项,通常使用的是QStyledItemDelegate。Qt默认使用QStyledItemDelegate。
-
使用Qt MVC 模型/视图的步骤示意如下:
# 创建一个3行3列的表格结构的模型
model = QStandardItemModel(3,3)
# 创建一个表格视图
my_table_view = QTableView()
# 设置模型的数据
model.setData(model.index(0,0),123);
model.setData(model.index(0,1),222);
model.setData(model.index(0,2),333);
model.setData(model.index(1,0),444);
model.setData(model.index(1,1),555);
model.setData(model.index(1,2),666);
model.setData(model.index(2,0),777);
model.setData(model.index(2,1),888);
model.setData(model.index(2,2),999);
# 设置视图的模型
my_table_view.setModel(model)
# 显示视图
my_table_view.show()
三、定位模型中的数据与模型索引
-
模型的结构
在Qt中,无论数据被存储为何种数据结构,模型总是以层次结构(即树形结构)来表示数据,视图按照此约定来访问模型中的数据,若数据是列表或表格结构的数据,则可以把其看做是只含有顶层节点,不含任何叶子节点的树形结构。也就是说,我们在子类化QAbstractItemModel来定义自己的模型结构时,始终应以树形结构为出发点,来组织自己的模型结构。 -
模型索引(简称索引)
-
Qt使用模型索引来使数据的表示和访问相分离,视图和委托使用索引来访问模型中的数据项,因此,只有模型知道怎样获取数据。
-
模型索引不像普通索引仅使用一个数字就能描述,模型索引需要使用3个属性进行描述:行号、列号、父模型索引。虽然模型索引使用行号、列号来定位数据项,但这并不意味着,数据是存储在数组或表格结构中的,使用行号、列号只是允许模型与视图、代理进行通信的一种约定。之所以还需要父模型索引,是因为Qt的模型都是以层次结构(树形结构)来组织的,Qt具体实现时,顶级数据项的父模型索引,使用“无效模型索引”来表示。
-
模型索引包含一个指向父索引的参数,当使用多个模型时,可避免混淆。
- Qt对模型索引的实现
-
Qt使用QModelIndex来实现模型索引,该类提供的索引是一个临时索引,因为模型可能会随时重新组织其内部结构(即数据项可能随时发生变化,比如删除,增加新数据项等),因此模型索引可能会变得无效,所以模型索引不需要也不应该被存储,若需要对数据项进行长时间的引用,应使用QPersistentModelIndex类创建一个持久模型索引。
-
使用模型索引引用模型中数据项的方法是使用QAbstractItemModel::index()函数。
-
无效索引模式:使用零参数的QModelIndex类的构造函数创建,即QModelIndex()就表示创建了一个无效模型索引。
- Qt常见的模型结构
-
树形结构
// 获取数据项A的模型索引,A的父索引由一个无效模型索引指定
QModeIndex indexA = model.index(0, 0, QModelIndex())
// 获取数据项B的模型索引,B位于父索引indexA的第一行第一列的位置
QModexIndex indexB = model.index(1, 1, indexA) -
表格结构
// 获取数据项B的模型索引,B是顶级数据项,因此其父索引是一个无效索引
QModelIndex indexB = model.index(1, 2, QModelIndex()) -
列表结构
// 获取数据项B的模型索引,B是顶级数据项,因此其父索引是一个无效索引
QModelIndex indexB = model.index(1, 0, QModelIndex())
四、数据的角色与数据的类型
-
同一个类型的数据可以作为不同的角色(或作用)来使用,比如对于字符串"AAA",可以把该字符串以文本的形式显示在视图的相应位置上,也可以把该字符串作为工具提示使用,还可以把该字符串作为what’s this的帮助提示等。由此可见,数据的角色,决定了该数据在视图中的显示方法,角色不同,显示方式也不同。
-
数据项与数据元素:位于同一个位置的数据项,并不仅仅只是一个数据元素,本文把组成数据项的数据称为数据元素,每个数据元素都有其自身的角色,比如一个数据项可能会同时含有图标数据元素(角色为Qt::DecorationRole)、文本数据元素(角色为Qt::DisplayRole)、工具提示数据元素(角色为Qt::ToolTipRole)等等。见下图中的数据项"222"
-
因为数据项可以由多个数据元素组成,因此把数据项使用一个单独的类来管理是比较方便的,比如对于QStandardItemModel模型的数据项,就使用了类QStandardItem来专门管理其数据项。
-
为了避免概念上的混乱,下面对数据、数据项、项(项目)、节点、单元格、数据元素、模型索引(索引)做一简介
-
数据:是一个统称,既可以是数据项也可以是数据元素。
-
数据项:是由多个数据元素组成的,每个数据元素都有自已的角色。
-
单元格、节点、项目:这三个概念通常用于指模型中的某一个数据项所在的位置, 只是对于不同的模型结构会有不同的称呼, 比如树形结构通常称为节点,表格结构通常称为单元格,而项目是一种更通用的称呼, Qt 中通常称其为项目(item)或数据项;有时也会对以上概念加以区别对待,比如单元格XXX的数据项,此时单元格表示位置,数据项就表示实际存储的数据。不管怎样,模型中某个数据项所在位置都是需要使用模型索引来指定的,因此单元格、节点、项目、数据项、模型索引这几个概念通常会根据不同的情形用于表示模型中的某个位置。
-
标准的数据角色由枚举Qt::ItemDataRole来描述,见下表。数据角色由QAbstractItemModel::setData()函数的第 3 个参数指定,其使用方法如下,其效果见第 2 点图示中第 1 行第 2 列的单元格:
- model.setData(model.index(0,1),222,Qt::DisplayRole); //设置显示的文本
- model.setData(model.index(0,1),QIcon(“F:/1i.png”),Qt::DecorationRole); //设置图标
- model.setData(model.index(0,1),“EEE”,Qt::ToolTipRole); //设置工具提示
五、选择视图中的数据项(选择模型)
选择模型: 对视图内项目的选择
Qt 使用 QItemSelectionModel 类来实现。所有的标准视图都有自已默认的选择模型。视图可以使用 QAbstractItemView::selectionModel()函数和QAbstractItemView::setSelectionModel()来获取和设置选择模型。通常不需要对选择模型进行设置。
六、Qt 实现的便利类
Qt 通过模型/视图框架结构实现了一些标准的方便使用的部件,使用这些便利部件比较简单。
这些类是 QListWidget、 QTreeWidget、 QTableWidget。