实战PyQt5- 064-MV框架中的Model类

区块链

  模型(Model)简介

  在Model-View框架中,模型(Model)为视图(View)和委托(Delegate)使用数据提供了标准接口。大多数情况下模型中并不真正存储数据(如果只有少量的数据,可以直接存在在模型里),它们只负责从诸如磁盘文件、数据库、网络通讯等获得源数据,并提供给视图,用户在视图中对数据进行修改,然后视图再通过模型更新源数据。在Qt中,模型类的标准接口通过QAbstractItemModel类来定义。在Qt中内置了多种标准模型。

  不管数据在底层以何种数据结构存储,所有QAbstractItemModel的子类都将以包含项表格的层次结构来呈现这些数据。视图类使用这个约定来存取模型中数据的项,但是它们向用户显示信息的方法则不会受到限制。当数据发生改变时,模型通过信号槽机制来通知与其相关联的视图。模型内部数据的组织方式并不一定和视图中数据的显示相同。

各种模型的结构结构示意图(图片来源:doc.qt.io)

  List Model虽然是线性的列表,有一个 Root Item(根节点),线性的一个个数据可以看作是一个只有一列的表格,但是它是有层次的,因为有一个根节点。Table Model就比较容易理解,只是也存在一个根节点。Tree Model主要面向层次数据,而每一层次都可以都很多列,因此也是一个带有层次的表格。

  模型索引(Model Index)

  为了确保数据显示与数据访问二者分离,Qt引入了模型索引的概念。通过模型获取的数据项可以通过模型索引显示。视图和委托都可以使用索引来访问数据,然后显示出来。因此,只有模型需要了解如何获取数据,并且模型管理的数据类型可以定义得相当广泛。模型索引包含一个指向创建它们的模型的对象,使得同时操作多个模型成为可能。

  模型索引提供了所需要的信息的临时索引,用于通过模型取回或者修改数据。由于模型随时可能重新组织其内部的结构,因此模型索引很可能变成不可用的,此时,就不应该保存这些数据。如果你需要长期有效的数据片段,必须创建持久索引。持久索引保证其引用的数据及时更新。临时索引(也就是通常使用的索引)由QModelIndex类提供,持久索引则是 QPersistentModelIndex 类提供。

  为了定位模型中的数据,需要三个属性:行号、列号以及父索引。

  行数和列数

  在模型最基本的形式中,模型可以使用简单的表格进行存取,表格中的项根据行号和列号确定。但这并不意味底层数据以数组结构储存,使用行号和列号只是部件之间相互通信的一个约定。我们可以提取任何指定行号和列号的模型项的信息,同时得到一个代表这个项的索引。

  index = model.index(row, column, ...)

表格模型示意图(图片来源doc.qt.io)

  上图显示了一个基本的表格模型,表格的每一项用一对行列数来定位。通过行列数,可以获取代表一个数据项的模型索引。

   indexA = model.index(0, 0,QModelIndex()); indexB = model.index(1, 1,QModelIndex()); indexC = model.index(2, 1,QModelIndex());模型的顶层项总是通过指定QModelIndex()函数来作为他们的父项。

  父项

  当在一个表格或列表视图中使用数据时,模型提供的表格型数据接口是较为理想的。行列号系统正好映射到视图显示项的方法。然而,诸如树型视图的结构要求模型对其内部项要有更灵活的接口。因此,每一个项也可能是另外一个表的父项,同样地,树型视图的顶级项也可以包含另一个列表。

  当需要模型项一个索引时,我们必须提供一些关于这个项的父项的一些信息。在模型外,引用一个项的唯一方法就是通过模型索引,所以一个父项的模型索引也必须要提供。

   index = model.index(row, column, parent)树形模型示意图(图片来源doc.qt.io)

  上图表显示了一个树状模型的表示法,树状模型的项由父项,行和列号定位。项”A”和项”C”代表模型中的顶层成员。

   indexA = model.index(0, 0, QModelIndex()); indexC = model.index(2, 1, QModelIndex());项“A”有一个子成员,项“B”的模型索引可以用以下的代码获得:

   indexB = model.index(1, 0, indexA);条目(Item)的数据角色

  模型中的条目(Item)可以为不同的部件扮演不同的角色,允许为不同的情形提供各种不同的数据,例如Qt.DisplayRole就是用于存取一个可以在视图中显示的字符串。通常情况下,项包含各种不同角色的数据,标准角色由枚举Qt.ItemDataRole定义。通过传递对应项的模型索引给模型,并指定一个角色以取得我们想要的数据的类型,我们就可以从模型中取得项的数据:

   value = model.data(index, role)Model-View框架中的角色(Role)(图片来源:doc.qt.io)

  角色向模型指示要引用的数据类型。视图可以以不同的方式显示角色,因此为每个角色提供适当的信息很重要。Qt. ItemDataRole中定义的标准角色涵盖了项数据的最常见用法。通过为每个角色提供适当的项数据,模型可以向视图和委托提供有关如何将项呈现给用户的提示。不同类型的视图可以根据需要自由解释或忽略此信息。也可以为特定于应用程序的目的定义其他角色。

  小结

  模型索引以独立于任何底层数据结构的方式为视图和委托提供关于模型中项的位置信息;项通过其行号和列号以及其父项的模型索引来引用它;模型索引是由模型根据其他组件(例如视图和委托)的请求来构造;如果使用index()方法请求索引时为父项指定了有效的模型索引,则返回的索引将引用模型中该父项之下的项。获得的索引指向该项的子项;如果使用index()方法请求索引时为父项指定了无效的模型索引,则返回的索引将引用模型中的顶级项;角色区分与项关联的不同类型的数据。使用模型索引

  为了演示如何使用模型索引从模型中检索数据,我们创建了一个没有视图的QFileSystemModel,并在控件中显示文件名和目录名称。虽然这个例子并没有展示使用模型的常用方法,但是它演示了在处理模型索引时模型使用的约定。

  model = QFileSystemModel()parentIndex =model.index(QDir.currentPath())numRows =model.rowCount(parentIndex)在这个例子中,我们创建了一个默认的QFileSystemModel,使用模型的的index()方法提供的一个特定的实现来获得一个父索引,同时使用rowCount()函数计算出这个模型的行数。

  为简单起见,我们只模型第一列的数据。我们按顺序逐行检查,获得每行第一个项的模型索引,然后读取存储在模型项里的数据。

  for row in range (numRows)index = model.index(row, 0, parentIndex)为了获得一个模型索引,我们指定行号,列号(第一列为0),以及我们想要的所有数据项的父项模型索引。储存于每个项中的文本可以用模型的data() 方法获得。我们指定一个模型索引以及DisplayRole来取得一个字符串形式的项数据。

  text = model.data(index, Qt.DisplayRole).toString()上面的示例演示了如何从模型检索数据的基本原理:

  模型的大小可以用rowCount() 和 columnCount()得到。这两个函数通常要指定一个父模型索引;模型索引用于存取模型里的项。需要使用行号,列号以及父模型索引来指定项;要存取模型的顶级项,需用QModelIndex()函数指定一个空的模型索引作为父模型索引;项包含不同角色的数据。要获得特定角色的数据,必须同时向模型提供模型索引和角色;通过实现 QAbstractItemModel提供的标准接口可以创建新的模型。

  标准模型演示

  QStringListModel是一个可编辑的模型,可以为组件提供一系列字符串作为数据,是封装了QStringList的model。QStringListModel通常作为只有一列的视图组件的model,如QListView和QComboBox。

  QFileSystemModel是QT标准的文件系统的模型。

  QSortFilterProxyModel类提供在其他的模型和视图之间排序和过滤数据的。QSortFilterProxyModel不能单独使用,是一个代理,真正的数据需要另外的模型提供。QSortFilterProxyModel的作用是对数据进行排序和过滤。

  文件modeldemo.py演示了这三种标准模型的用法,完整代码如下:

  importsys,osfromPyQt5importQtCore,QtGui,QtWidgetsfromPyQt5.QtCoreimportQDir,QStringListModel,QSortFilterProxyModel,QModelIndexfromPyQt5.QtWidgetsimport(QApplication,QWidget,QLabel,QFileSystemModel,QListView,QTreeView,QVBoxLayout,QAbstractItemView)classDemoModel(QWidget):def__init__(self,parent=None):super(DemoModel,self).__init__(parent)#设置窗口标题self.setWindowTitle('实战PyQt5:Model-View框架演示')#设置窗口大小self.resize(640,480)self.initUi()definitUi(self):vLayout=QVBoxLayout(self)vLayout.addWidget(QLabel('QStringListModel演示'))vLayout.addWidget(self.createStringListModelDemo())vLayout.addWidget(QLabel('QFileSystemModel演示'))vLayout.addWidget(self.createFileSystemModelDemo())vLayout.addWidget(QLabel('QSortFilterProxyModel演示'))vLayout.addWidget(self.createSortFilterProxyModelDemo())self.setLayout(vLayout)defcreateStringListModelDemo(self):slm=QStringListModel()lv=QListView(self)sportNames=('篮球','足球','网球','羽毛球','橄榄球')slm.setStringList(sportNames)lv.setModel(slm)lv.setEditTriggers(QAbstractItemView.AnyKeyPressed

  QAbstractItemView.DoubleClicked)returnlvdefcreateFileSystemModelDemo(self):#创建文件系统模型fsm=QFileSystemModel()fsm.setRootPath(QDir.currentPath())#树状视图tv=QTreeView(self)tv.setModel(fsm)tv.setRootIndex(fsm.index(QDir.currentPath()))returntvdefcreateSortFilterProxyModelDemo(self):#创建文件系统模型fsm=QFileSystemModel()fsm.setRootPath(QDir.currentPath())#代理模型sfpm=QSortFilterProxyModel()sfpm.setFilterKeyColumn(0)sfpm.setSourceModel(fsm)#树状视图tv=QTreeView(self)#为视图指定模型tv.setModel(sfpm)#指定根索引tv.setRootIndex(sfpm.index(0,0,QModelIndex()))returntvif__name__=='__main__':app=QApplication(sys.argv)window=DemoModel()window.show()sys.exit(app.exec())运行结果如下图:

测试Model-View框架中的Model

  本文知识点

  什么是模型索引;怎么从模型中获得模型索引;项数据角色的用法;Qt内建标准模型的用法。


喜欢本文内容就, 收藏,点赞,评论和转发。

标签: 区块链