概述
Qt包含了一组item view类,它们使用模型/视图架构来管理数据之间的关系以及呈现给用户的方式。该体系结构引入的功能分离为开发人员提供了更大的灵活性来定制项目的表示,并提供了一个标准的模型接口,以允许广泛的数据源与现有项目视图一起使用。在本文中,我们简要介绍了模型/视图范式,概述了所涉及的概念,并描述了项目视图系统的体系结构。对架构中的每个组件都进行了解释,并给出了如何使用所提供的类的示例。
模型/视图架构
模型-视图-控制器(Model-View-Controller, MVC)是一种源自Smalltalk的设计模式,经常用于构建用户界面。在《设计模式》一书中,Gamma等人写道:
- MVC由三种对象组成。模型是应用程序对象,视图是它的屏幕显示,控制器定义了用户界面对用户输入的反应方式。在使用MVC之前,用户界面设计倾向于将这些对象放在一起。MVC将它们解耦以提高灵活性和重用性。
如果视图和控制器对象被组合,结果就是模型/视图架构。这仍然将数据的存储方式和呈现给用户的方式分开,但基于相同的原则提供了一个更简单的框架。这种分离使得可以在多个不同的视图中显示相同的数据,并实现新的视图类型,而无需更改底层数据结构。为了灵活地处理用户输入,我们引入了委托的概念。在这个框架中使用委托的好处是,它允许自定义数据项的渲染和编辑方式。
模型/视图架构
该模型与数据源进行通信,为架构中的其他组件提供接口。通信的性质取决于数据源的类型,以及模型的实现方式。
视图从模型中获取模型索引;这些是对数据项的引用。通过向模型提供模型索引,视图可以从数据源检索数据项。
在标准视图中,委托渲染数据项。当一个项目被编辑时,委托使用模型索引直接与模型通信。
一般来说,模型/视图类可以分为上面描述的三组:模型、视图和委托。每个组件都由提供公共接口的抽象类定义,在某些情况下,还提供功能的默认实现。抽象类旨在被子类化,以便提供其他组件所期望的全套功能;这也允许编写专用组件。
模型、视图和委托使用信号(signal)和槽(slot)相互通信:
- 来自模型的信号通知视图关于数据源所持有的数据的更改。
- 来自视图的信号提供了关于用户与正在显示的项目交互的信息。
- 来自委托的信号在编辑过程中用于告诉模型和视图关于编辑器的状态。
模型
所有item模型都基于QAbstractItemModel类。这个类定义了一个接口,视图和委托使用该接口访问数据。数据本身并不一定要存储在模型中;它可以保存在由单独的类、文件、数据库或其他应用程序组件提供的数据结构或存储库中。
模型类一节将介绍模型的基本概念。
QAbstractItemModel提供了一个数据接口,它足够灵活,可以处理以表、列表和树的形式表示数据的视图。然而,在为列表和类似表格的数据结构实现新模型时,QAbstractListModel和QAbstractTableModel类是更好的起点,因为它们提供了通用函数的适当默认实现。这些类都可以子类化,以提供支持特定类型列表和表的模型。
在创建新模型一节中讨论了模型的子类化过程。
Qt提供了一些现成的模型来处理数据项:
- QStringListModel用于存储一个简单的QString元素列表。
- QStandardItemModel管理更复杂的项目树结构,每个项目可以包含任意数据。
- QFileSystemModel提供了关于本地文件系统中的文件和目录的信息。
- QSqlQueryModel、qsqlltablemodel和QSqlRelationalTableModel按模型/视图约定访问数据库。
如果这些标准模型不能满足您的需求,您可以子类化QAbstractItemModel, QAbstractListModel, or QAbstractTableModel 来创建您自己的自定义模型。
视图
为不同类型的视图提供了完整的实现:QListView显示项目列表,QTableView显示表中来自模型的数据,QTreeView显示分层列表中的数据模型项。这些类都基于QAbstractItemView抽象基类。虽然这些类是现成的实现,但它们也可以子类化以提供自定义视图。
可用的视图将在视图类一节中介绍。
委托
QAbstractItemDelegate是模型/视图框架中代理的抽象基类。默认的委托实现由QStyledItemDelegate提供,它被Qt的标准视图用作默认委托。然而,QStyledItemDelegate和QItemDelegate是绘图的独立替代方案,并为视图中的项目提供编辑器。它们之间的区别在于,QStyledItemDelegate使用当前样式来绘制它的项。因此,在实现自定义委托或使用Qt样式表时,我们建议使用QStyledItemDelegate作为基类。
委托在委托类一节中描述。
排序
在模型/视图架构中有两种排序方法;选择哪种方法取决于你的基础模型。
如果你的模型是可排序的,即如果它重新实现了QAbstractItemModel::sort()函数,QTableView和QTreeView都提供了一个API,允许你以编程方式对模型数据进行排序。此外,您可以启用交互式排序(即允许用户通过单击视图的标题对数据进行排序),通过分别将QHeaderView::sortIndicatorChanged()信号连接到QTableView::sortByColumn()插槽或QTreeView::sortByColumn()插槽。
另一种方法是,如果你的模型没有所需的接口,或者你想使用列表视图来显示数据,则在视图中显示数据之前,使用代理模型来转换模型的结构。这在代理模型一节中有详细介绍。
方便类
为了让依赖于Qt基于item的item view和table类的应用程序受益,许多便利类都派生自标准视图类。它们不打算被子类化。
这些类的例子包括QListWidget、QTreeWidget和QTableWidget。
这些类不如视图类灵活,不能与任意模型一起使用。我们建议你使用model/view方法来处理item视图中的数据,除非你非常需要一组基于item的类。
如果你想利用模型/视图方法提供的特性,同时仍然使用基于item项的接口,可以考虑使用视图类,例如QListView、QTableView和QTreeView与QStandardItemModel(推荐)。
使用模型和视图
接下来的几节解释如何在Qt中使用model/view模式。每一节都包含一个示例,然后还有一节展示如何创建新组件。
Qt包含两种模型
Qt提供的两个标准模型是QStandardItemModel和QFileSystemModel。QStandardItemModel是一个多用途模型,可用于表示列表、表和树视图所需的各种不同的数据结构。这个模型还保存了数据项(item)。QFileSystemModel是一个维护目录内容信息的模型。因此,它本身不保存任何数据项,只是表示本地文件系统上的文件和目录。
QFileSystemModel提供了一个现成的模型来进行实验,可以很容易地配置以使用现有数据。使用这个模型,我们可以展示如何为现成的视图设置模型,并探索如何使用模型索引操作数据。
在现有模型中使用视图
QListView和QTreeView类是最适合与QFileSystemModel一起使用的视图。下面给出的示例在树视图中显示目录的内容,与列表视图中的相同信息相邻。这两个视图共享用户的选择,因此选中的项目在两个视图中都被突出显示。
我们设置了一个QFileSystemModel,以便可以使用,并创建了一些视图来显示目录的内容。这展示了使用模型的最简单方法。模型的构造和使用是在一个main()函数中完成的:
int main(int argc, char *argv[])
{QApplication app(argc, argv);QSplitter *splitter = new QSplitter;QFileSystemModel *model = new QFileSystemModel;model->setRootPath(QDir::currentPath());
该模型被设置为使用来自某个文件系统的数据。对setRootPath()的调用告诉模型要向视图公开文件系统上的哪个驱动器。
我们创建了两个视图,以便我们可以以两种不同的方式检查模型中保存的项:
QTreeView *tree = new QTreeView(splitter);tree->setModel(model);tree->setRootIndex(model->index(QDir::currentPath()));QListView *list = new QListView(splitter);list->setModel(model);list->setRootIndex(model->index(QDir::currentPath()));
视图的构造方式与其他部件相同。要想在视图中显示模型中的项目,只需调用它的setModel()函数,将目录模型作为参数即可。我们在每个视图上调用setRootIndex()函数,从文件系统模型中为当前目录传入一个合适的模型索引,从而过滤模型提供的数据。
这里使用的index()函数是QFileSystemModel唯一的。我们给它提供一个目录,它会返回一个模型索引。模型索引在模型类中讨论。
函数的其余部分只是显示splitter部件中的视图,并运行应用程序的事件循环:
splitter->setWindowTitle("Two views onto the same file system model");splitter->show();return app.exec();
}
在上面的例子中,我们忽略了如何处理元素的选择。在Item视图中处理选择的部分会更详细地介绍这个主题。Handling Selections in Item Views.
Model/View Programming | Qt Widgets 5.15.17