/

/

Qt 学习之路 2(45):模型

Qt 学习之路 2(45):模型

 豆子  2013年2月26日  Qt 学习之路 2  23条评论

在前面两章的基础之上,我们将开始介绍 model 的通用概念。

在 model/view 架构中,model 提供一种标准接口,供视图和委托访问数据。在 Qt 中,这个接口由QAbstractItemModel类进行定义。不管底层数据是如何存储的,只要是QAbstractItemModel的子类,都提供一种表格形式的层次结构。视图利用统一的转换来访问模型中的数据。但是,需要提供的是,尽管模型内部是这样组织数据的,但是并不要求也得这样子向用户展示数据。

下面是各种 model 的组织示意图。我们利用此图来理解什么叫“一种表格形式的层次结构”。

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

为了能够使得数据的显示同存储分离,我们引入模型索引(model index)的概念。通过索引,我们可以访问模型的特定元素的特定部分。视图和委托使用索引来请求所需要的数据。由此可以看出,只有模型自己需要知道如何获得数据,模型所管理的数据类型可以使用通用的方式进行定义。索引保存有创建的它的那个模型的指针,这使得同时操作多个模型成为可能。

 
 
1
QAbstractItemModel *model = index.model();

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

为了定位模型中的数据,我们需要三个属性:行号、列号以及父索引。下面我们对其一一进行解释。

我们前面介绍过模型的基本形式:数据以二维表的形式进行存储。此时,一个数据可以由行号和列号进行定位。注意,我们仅仅是使用“二维表”这个名词,并不意味着模型内部真的是以二维数组的形式进行存储;所谓“行号”“列号”,也仅仅是为方便描述这种对应关系,并不真的是有行列之分。通过指定行号和列号,我们可以定位一个元素项,取出其信息。此时,我们获得的是一个索引对象(回忆一下,通过索引我们可以获取具体信息):

 
 
1
QModelIndex index = model->index(row, column, ...);

模型提供了一个简单的接口,用于列表以及表格这种非层次视图的数据获取。不过,正如上面的代码暗示的那样,实际接口并不是那么简单。我们可以通过文档查看这个函数的原型:

 
 
1
2
3
QModelIndex QAbstractItemModel::index(int row,
                                      int column,
                                      const QModelIndex &parent=QModelIndex()) const

这里,我们仅仅使用了前两个参数。通过下图来理解一下:

在一个简单的表格中,每一个项都可以由行号和列号确定。因此,我们只需提供两个参数即可获取到表格中的某一个数据项:

 
 
1
2
3
QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexB = model->index(1, 1, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());

函数的最后一个参数始终是 QModelIndex(),接下来我们就要讨论这个参数的含义。

在类似表格的视图中,比如列表和表格,行号和列号足以定位一个数据项。但是,对于树型结构,仅有两个参数就不足够了。这是因为树型结构是一个层次结构,而层次结构中每一个节点都有可能是另外一个表格。所以,每一个项需要指明其父节点。前面说过,在模型外部只能用过索引访问内部数据,因此,index()函数还需要一个 parent 参数:

 
 
1
QModelIndex index = model->index(row, column, parent);

类似的,我们来看看下面的示意图:

图中,A 和 C 都是模型中的顶级项:

 
 
1
2
QModelIndex indexA = model->index(0, 0, QModelIndex());
QModelIndex indexC = model->index(2, 1, QModelIndex());

A 还有自己的子项。那么,我们就应该使用下面的代码获取 B 的索引:

 
 
1
QModelIndex indexB = model->index(1, 0, indexA);

由此我们看到,如果只有行号和列号两个参数,B 的行号是 1,列号是 0,这同与 A 同级的行号是 1,列号是 0 的项相同,所以我们通过 parent 属性区别开来。

以上我们讨论了有关索引的定位。现在我们来看看模型的另外一个部分:数据角色。模型可以针对不同的组件(或者组件的不同部分,比如按钮的提示以及显示的文本等)提供不同的数据。例如,Qt::DisplayRole用于视图的文本显示。通常来说,数据项包含一系列不同的数据角色,这些角色定义在Qt::ItemDataRole枚举中。

我们可以通过指定索引以及角色来获得模型所提供的数据:

 
 
1
QVariant value = model->data(index, role);

通过为每一个角色提供恰当的数据,模型可以告诉视图和委托如何向用户显示内容。不同类型的视图可以选择忽略自己不需要的数据。当然,我们也可以添加我们所需要的额外数据。

总结一下:

  • 模型使用索引来提供给视图和委托有关数据项的位置的信息,这样做的好处是,模型之外的对象无需知道底层的数据存储方式;
  • 数据项通过行号、列号以及父项三个坐标进行定位;
  • 模型索引由模型在其它组件(视图和委托)请求时才会被创建;
  • 如果使用index()函数请求获得一个父项的可用索引,该索引会指向模型中这个父项下面的数据项。这个索引指向该项的一个子项;如果使用index()函数请求获得一个父项的不可用索引,该索引指向模型的最顶级项;
  • 角色用于区分数据项的不同类型的数据。

下面回到前面我们曾经见过的模型QFileSystemModel,看看如何从模型获取数据。

 
 
1
2
3
QFileSystemModel *model = new QFileSystemModel;
QModelIndex parentIndex = model->index(QDir::currentPath());
int numRows = model->rowCount(parentIndex);

在这个例子中,我们创建了QFileSystemModel的实例,使用QFileSystemModel重载的index()获取索引,然后使用rowCount()函数计算当前目录下有多少数据项(也就是行数)。前面一章中迷迷糊糊的代码,现在已经相当清楚了。

为简单起见,下面我们只关心模型第一列。我们遍历所有数据,取得第一列索引:

 
 
1
2
for (int row = 0; row < numRows; ++row) {
    QModelIndex index = model->index(row, 0, parentIndex);

我们使用index()函数,第一个参数是每一行行号,第二个参数是 0,也就是第一列,第三个参数是 parentIndex,也就是当前目录作为父项。我们可以使用模型的data()函数获取每一项的数据。注意,该函数返回值是QVariant,实际是一个字符串,因此我们直接转换成QString

 
 
1
2
3
    QString text = model->data(index, Qt::DisplayRole).toString();
    // 使用 text 数据
}

上面的代码片段显示了从模型获取数据的一些有用的函数:

  • 模型的数目信息可以通过rowCount()columnCount()获得。这些函数需要制定父项;
  • 索引用于访问模型中的数据。我们需要利用行号、列号以及父项三个参数来获得该索引;
  • 当我们使用QModelIndex()创建一个空索引使用时,我们获得的就是模型中最顶级项;
  • 数据项包含了不同角色的数据。为获取特定角色的数据,必须指定这个角色。
 

Qt 学习之路 2(45):模型的更多相关文章

  1. Qt 学习之路 :自定义只读模型

    model/view 模型将数据与视图分割开来,也就是说,我们可以为不同的视图,QListView.QTableView和QTreeView提供一个数据模型,这样我们可以从不同角度来展示数据的方方面面 ...

  2. Qt 学习之路 2(10):对象模型

    Home / Qt 学习之路 2 / Qt 学习之路 2(10):对象模型 Qt 学习之路 2(10):对象模型  豆子  2012年9月2日  Qt 学习之路 2  45条评论 标准 C++ 对象模 ...

  3. Qt 学习之路 2(51):布尔表达式树模型

    Qt 学习之路 2(51):布尔表达式树模型 豆子 2013年5月15日 Qt 学习之路 2 17条评论 本章将会是自定义模型的最后一部分.原本打算结束这部分内容,不过实在不忍心放弃这个示例.来自于 ...

  4. Qt 学习之路 2(56):使用模型操作数据库

    Qt 学习之路 2(56):使用模型操作数据库 (okgogo: skip) 豆子 2013年6月20日 Qt 学习之路 2 13条评论 前一章我们使用 SQL 语句完成了对数据库的常规操作,包括简单 ...

  5. Qt 学习之路 2(50):自定义可编辑模型

    Home / Qt 学习之路 2 / Qt 学习之路 2(50):自定义可编辑模型 Qt 学习之路 2(50):自定义可编辑模型 豆子 2013年5月13日 Qt 学习之路 2 13条评论 上一章我们 ...

  6. Qt 学习之路 2(49):自定义只读模型

    Qt 学习之路 2(49):自定义只读模型 豆子 2013年5月5日 Qt 学习之路 2 18条评论 model/view 模型将数据与视图分割开来,也就是说,我们可以为不同的视图,QListView ...

  7. Qt 学习之路 2(71):线程简介

    Qt 学习之路 2(71):线程简介 豆子 2013年11月18日 Qt 学习之路 2 30条评论 前面我们讨论了有关进程以及进程间通讯的相关问题,现在我们开始讨论线程.事实上,现代的程序中,使用线程 ...

  8. Qt 学习之路 2(67):访问网络(3)

    Qt 学习之路 2(67):访问网络(3) 豆子 2013年11月5日 Qt 学习之路 2 16条评论 上一章我们了解了如何使用我们设计的NetWorker类实现我们所需要的网络操作.本章我们将继续完 ...

  9. Qt 学习之路 2(66):访问网络(2)

    Home / Qt 学习之路 2 / Qt 学习之路 2(66):访问网络(2) Qt 学习之路 2(66):访问网络(2)  豆子  2013年10月31日  Qt 学习之路 2  27条评论 上一 ...

随机推荐

  1. scala中存在的问题

    2017-12-27 scala学习中存在的问题: 1.表达式的概念要搞清楚 2.八种基本数据类型要搞清楚 Byte\Short\Int\Long\Char\Boolean\Double\Float都 ...

  2. java基础之集合长度可变的实现原理

    首先我们要明白java中的集合Collection,List,ArrayList之间的关系: ArrayList是具体的实现类,实现了List接口 List是接口,继承了Collection接口 Li ...

  3. 刷题向》关于线段树的区间开根号 BZOJ3211(NORMAL+)

    这是一道关于线段树的区间开根号的裸题,没什么好讲的. 值得注意的是,因为有区间开根号的性质,所以我们每一次更改操作只能把更改区间所覆盖的所有元素全部查找,当然你直接找效率明显爆炸... 能够注意到,指 ...

  4. Servlet接口应用(开发servlet三种方式)

    参见 文库/java/javaEE全新学习教程2.2节 1.通过URL调用 2通过提交表单 3超链接 4 javascript写一个函数,调用这个函数 1,首先在工程的WebRoot文件夹下建立一个j ...

  5. PCL struct“flann::SearchParams参数错误

    最近在使用PCL的KdTreeFLANN的时候报错:error C2079: “pcl::KdTreeFLANN<PointT>::param_radius_”使用未定义的 struct“ ...

  6. Hyperledger子项目

    Hyperledger由五个子项目构成: • BlockChain Explorer 展⽰和查询区块链块.事务和相关数据的 Web应⽤ • Fabric 区块链技术的⼀个实现(主要项目) • STL ...

  7. c语言学习笔记 if语句执行流程和关系运算符

    回想现实生活中,我们会遇到这样的情况,如果下雨了就带伞上班,如果没下雨就不带伞上班,这是很正常的逻辑.程序是解决生活中的问题的,那么自然在程序中也需要这样的判断,当满足某个条件的时候做一件事情,这种东 ...

  8. MFC可视化

    当你修改了变量的值,而希望对话框控件更新显示,就应该在修改变量后调用UpdateData(FALSE):如果你希望知道用户在对话框中到底输入了什么,就应该在访问变量前调用UpdateData(TRUE ...

  9. ssh试卷

    2.简述Hibernate的工作原理. 答:首先,Configuration读取Hibernate的配置文件及映射文件中的信息,即加载配置文件和映射文件,并通过Hibernate配置文件生成一个多线程 ...

  10. HTML5拓扑3D机房,电力工控Web SCADA

    http://www.hightopo.com/cn-index.html 一套丰富的JavaScript界面类库, 提供完整的基于HTML5图形界面组件库.使用HT for Web您可以轻松构建现代 ...