在QT官方帮助文档中搜索:Model,找到:Model/View Programming 以及Model/View Tutorial 系列教程,当然看起来有些费劲,但却是最权威的资料。
该系列博文,内容全部来自官方手册,加上一些我自己的理解,相信大家看起来会更轻松一些。
0、MV架构
该文章来自【暴躁的野生猿】博客,如有非法转载,请读者帮忙举报下。
MVC指的是Model、View、Control,模型视图控制三者分离的一种数据与用户交互的方式。Qt本质上实现的是MV结构,没有Control,其实MV架构已经足以应付大多数应用场景了。其优势是把数据的存储方式和视图的展示方式,给解耦了,所谓解耦,举2个例子:
(1)数据可能存储在某个类的实体中、txt文件中、数据库中、网络上、操作系统内核里(如文件系统信息)等等,而视图View要展示这些数据时,并不关心数据是存储在哪,数据的存储结构如何,因为View要展示数据时,只会跟Model索要数据。
(2)基于QT提供的Model框架,我们可以在不改变数据的情况下,更改视图的展示方式,比如对于同一组数据,可以立即展示为列表、饼图、柱状图等,甚至可以针对这同一组数据同时显示多种视图,用户从任何一个视图修改数据(如果相应的视图提供了编辑功能的话),在其他视图中都会得到同步响应。
MVD的通信结构,该图来自官方手册。在图中我们看到视图View是无法直接与数据Data打交道的,不论读还是写数据,必须由Model进行中转。更确切的说,由上图可见,View与Data的交互,必须经过Delegate和Model两层中转,才能得以读写。
在QT源码中,数据在Data与View之间的流转过程,是基于信号槽实现的:
- 当Data发生变化时,Model会发出信号通知View,进行重新渲染,如上图的Model->View的箭头所示。
- 当用户用鼠标或者键盘等在视图上动作时,View会发出信号给Delegate,用户要如何编辑数据,如上图View->Delegate箭头所示。
- 当用户使用Delegate编辑数据时,Delegate会发信号告诉Model和View,用户编辑的结果。
1、模型
model “模型”的作用是:供视图读写真正的数据。
不同于上图所示的结构,Data有时候会被写在Model实体的内部,这只发生在一些简单的应用场景中,可参考我的该系列的另一篇博文,有这样的例子。即使我们把Data放到了Model内部,也仍然要遵循上述这种数据流转架构。
学习这些概念的最终目的是为了写程序,这里还是要讲一下QT,所有的模型类的祖宗都是QAbstractItemModel,通过继承他,我们可以写出能够适应各种视图的模型。不过如果你想把这个模型展示为列表、表格、树,这三种情形之一时,没必要继承QAbstractItemModel,因为QT提供了更方便的类QAbstractListModel和QAbstractTableModel类供我们使用,这两个类也是继承的QAbstractItemModel,只是已经替我们实现了必要的虚函数,可参考该系列我其他的博文,看看这两个类是如何简化我们的编程工作量的。
不仅如此,QT还再次继承类QAbstractListModel和QAbstractTableModel类,提供了比这俩更方便的类:
- QStringListModel 字符串列表模型;
- QStandardItemModel 用户更加复杂的树形机构,内容可以是任意数据
- QFileSystemModel 这个可以直接帮我们取出操作系统文件模型,供我们将其展示为列表、表格、树等,例如windows资源管理器那种树形结构。
- QSqlQueryModel, QSqlTableModel, QSqlRelationalTableModel 用于访问数据库
这些类很方便(所谓“方便”,其实就是说,程序员可以少写几个函数)的同时,也会丧失灵活性,毫无疑问,最灵活的类是祖宗QAbstractItemModel,继承它可以实现model的所有功能,也是用起来最麻烦的类。
上图是直接把QFileSystemModel的一个对象,直接分别绑定到列表视图、树形视图、表格视图时的效果,简直方便。
QFileSystemModel *fModel = new QFileSystemModel();
fModel->setRootPath("C:\");
QTreeView *treeView = new QTreeView();
treeView->setModel(fModel);
treeView->show();
QTableView *tableView = new QTableView();
tableView->setModel(fModel);
tableView->show();
2、委托Delegate
delegate “委托”,这个词听起来相当令人困惑,有些教程中也会将其翻译为“代理”,从字面上很难理解他到底是干嘛的,这只能死记硬背了。delegate的作用是:为指定索引的数据,提供一种自定义的展示方式与编辑方式,仅此而已。
QAbstractItemDelegate是所有delegate的祖宗类,QT内置了它的2个子类:QStyledItemDelegate和QItemDelegate,他俩的区别在于QStyledItemDelegate使用当前样式进行绘制,且支持样式表。QT官方推荐,自定义委托时,要使用QStyledItemDelegate,不推荐QItemDelegate。