Qt的Model/View Framework解析(数据是从真正的“肉(raw)”里取得,Model提供肉,所以读写文件、操作数据库、网络通讯等一系列与数据打交道的工作就在model中做了)

最近在看Qt的Model/View Framework,在网上搜了搜,好像中文的除了几篇翻译没有什么有价值的文章。E文的除了Qt的官方介绍,其它文章也很少。看到一个老外在blog中写道Model/View是他认为Qt中最不好的一部分了。真的是这样吗?为了回馈开源社区,我写了这篇blog,写的是我认为比较有价值的东东。题目起得是解析,但也没有特别细节的介绍,点到为止,有兴趣的Tx可以继续讨论。我所看的资料有《C++ GUI Programming with Qt 4, Second Edition》、Qt官网和Qt源代码。
      在UI中,最常用的就是list/grid/tree了(在Qt中,grid被称为table)。尤其是做那些数据库相关的程序,可能每个界面都要用到list或grid。在Qt中,它们被归为item view class。有两种实现,一种叫item based,这些类名以widget结尾,如QListWidget等。另一种叫model based,这些类以view结尾,如QListView等。
      item based widget使用起来很简单,与MFC中的CListCtrl/CTreeCtrl等差不多。这些类既是数据容器(data container),又负责显示(presentation),还要处理与用户的交互(user action)。但使用这些类的缺点就是在处理大量数据显示时显得力不从心。我记得使用CListCtrl的report mode显示超过5万条记录时需要等待很长时间。因为它要把所有的数据都拷贝进来,再显示,比如这个list一次只能显示50条记录,但它把剩下的49950条也拷贝进了内存,并为每条记录的显示都加上格式等额外的信息。我想这种实现对于专业的软件来说是无法接受的。那么怎么提高它们的性能呢?有些人想出了virtual list/grid。它们显示之前并不需要把所有的数据都装进来,如有5万条记录,刚开始时它显示前50条,显示哪条时去取哪条。也就是说后49950条它不管,当用户移动滚动条或上下键改变显示内容时,它会根据一系列参数计算出目前需要显示哪几条。如1000-1050条,那么再去取这几条数据显示。由于实现起来有一定的难度,所以很多MFC实现的virtual list/grid显示出来都是“素颜”的(能快速显示出来已经不错了)。当然,coding无止境,这种取数据的方式我们称为delayed fetch,如果实现不好的化,当电脑速度慢且用户急速拖滚动条时也有可能出现白屏、拖尾、模糊等情况。所以还可以做prefetch和caching来尽力避免这些情况。好了,这里铺垫的已经够多了,下面我们直奔主题。
      model based view就是我们今天要重点“解析”的。对应的UI类有QListView/QTreeView/QTableView。那么这些view与上面的widget有什么区别呢?名字就是最大的区别。widget本来就是指小东东,而view可是很“大气”的视图哦。那么这些view大哥是怎么显示的呢?下面就说说我不愿多说但又不得不说的一个设计模式:MVC (Model/View/Controller),model代表数据(data set),view代表显示(presentation),controller处理与用户交互(user action)。MVC模式源自smalltalk,现在好像被用的很多了。它的作用其实是把上面介绍的类似CListCtrl这样的类解放出来,因为它们要干很多事情,太累了。还有就是对于有些程序员来说,实现一个功能复杂的类可能会力不从心。分成3个类每个类解决不同的问题可能更好实现。从软件工程的角度上讲,这种方式的decoupling可以降低系统风险。好了,关于MVC,大家想了解更多的话可以继续在网上搜索。
      模式是MVC,那么Qt是怎么实现的呢?我打开了Qt的官网介绍(http://qt.nokia.com/doc/4.6/model-view-programming.html)。我晕,看到了几十个类,而且很多都有继承关系。很容易让人迷失啊。不过不要紧,经过2天的学习,我对它们做了总结,见下图。Tx们可以从我这幅图开始,抓住该framework的主线,免得迷失在茫茫泪(类)海:-)


 
      一般来说,Model里面并没有真正存储数据(数据少的话也可以直接存储在Model里),它的数据是从真正的“肉(raw)”里取得,如一个disk file,或database的query result set等等。那么这个model究竟是干什么用的呢?说白了吧,它就是负责将“肉”数据获取并提供给view,然后将view所做的对“肉”数据的修改更新至真正的“肉”中。所以,读写文件、操作数据库、网络通讯等一系列与数据打交道的工作就在model中做了。有的时候“肉”可能真的很肥,所以model还有一项重要的工作就是把这些“肉”编号。这样就出现了Model Index这个非常重要的类。一般来说,它使用一个2维的编号(row/colum)来对“肉”编号。但对于tree这种有层次结构的数据来说,又加上一个parent index作为第3个编号。即一个父亲下面的叶子也是从0,0开始编号,获取model index的时候用递归来实现。OK,现在model已经有了一堆编好号码的“肉”了,谁来买啊?“肉”便宜了。。。。。。
      View适时出现,注意,很多view可以同时来买同一块“肉”。(汗,不开玩笑了,这篇blog快水了)。当view需要显示某些数据时,它们通过model index从model中获取数据(调用model的data函数,当model的data变化时,它也会自动发dataChanged signal给所有的view以便它们更新)。当然,在view中也可以调用model的setData函数来设定某个model index所对应的数据。这里要说明一下model中的数据,用QVarient来承载,可以是所有Qt支持的类型,比较贴心的是,数据可以分成多个角色(role),例如Qt::DisplayRole专用于显示,Qt::BackgroundRole用于显示背景色等等。所以在model中,你不光可以对“肉”进行编号,还可以对“肉”进行“深加工”,使它们更“好看”或是更“美味”。View组织这些数据并显示,但却没有做真正的显示工作,真正的工作留给了delegate。
      Delegate就是MVC中的C。view让它显示时它就在paint函数中显示。当然,你可以重载这个函数并实现你自己的显示。你还可以给一个view设定row delegate和colum delegate专用于row和colum。当用户触发了view的edit trigger时(如双击鼠标或回车),view开始in place edit (beginEditing)。Delegate会在合适的地方创建一个合适的widget(如line edit或combo box等)处理用户的输入,用户输入完成以后delegate获取用户的输入并返回。这些输入可以通过调用model的setData函数保存到真正的“肉”中。所以Delegate其实就是负责最终显示数据和处理用户交互的。
      既然由用户交互,最重要的肯定是用户的选择了。说一下selection model。View将用户选择的item index全部存入selection model中,显示的时候根据selection model的内容显示。另外,多个view可以共享同一个selection model,这样,当你选中其中的一个时,另一个view中的相应item也会被选中。

最后总结一下,Smalltalk怎么实现的MVC我不清楚,但是Qt model/view framework对MVC的实现完全是基于C++的虚基类和虚函数特性。MVC各部分之间的联通除了应用signal/slot之外,就全是虚函数了。Tx们如果有兴趣的话,可以看看model/view framework源代码中那些QAbstract开头的类,它们定义了统一的接口虚函数后,派生类只需要重新实现这些函数即可。这里再重复一下学习这部分时Tx们应该关注的2条主线。
      无论什么view,作用都是把数据显示出来,那么第一条主线是数据怎样从数据源显示到view中。
      显示的数据可能被修改,所以第二条主线就是修改后的数据怎样再更新至数据源中。如果你能把这2幅sequence map画出来,相信你就已经完全明白了。

我不知道Qt在这里的设计是不是做了太多的decoupling。反正灵活性是足够了(model/view/delegate都可以继承),但无法让程序员快速掌握。虽然Qt已经弱化了delegate并而且为view提供默认的delegate。但在做具体项目时,程序员大部分时候还是要根据具体情况重写派生类数的。这对于那些想快速做界面并显示的程序员来说,可能会难以接受。总之,这个轮子做的够精细,但一般在使用之前你还要自己拧点螺丝,上点油。

http://blog.csdn.net/superjoel/article/details/5112120

时间: 2024-10-25 09:19:43

Qt的Model/View Framework解析(数据是从真正的“肉(raw)”里取得,Model提供肉,所以读写文件、操作数据库、网络通讯等一系列与数据打交道的工作就在model中做了)的相关文章

几种通过JDBC操作数据库的方法,以及返回数据的处理

1.SQL TO String :只返回一个查询结果 例如查询某条记录的总数 rs = stmt.executeQuery(replacedCommand);             if (rs != null && rs.next()) // rs only contains one row and one column             {                    String tempStr = rs.getString(1);                 

c++中读写文件操作

读写文件这个,不常用,每次用的时候都会百度一下,每次写法还都不一样,所有总是记混.今天利用点时间总结下之前工程中用过的.以后就安照这种方法写了. 搞acmicpc的时候喜欢用freopen(),这个是c语言里面的用法如下: #include<stdio.h> int main(){ freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); int n,m

oracle中utl_file包读写文件操作实例学习

在oracle中utl_file包提供了一些操作文本文件的函数和过程,学习了一下他的基本操作 1.创建directory,并给用户授权 复制代码 代码如下: --创建directory create or replace directory TESTFILE as '/home/oracle/zxx/test'; --给用户授权 grant read, write on directory TESTFILE to zxx; 详细介绍 http://download.oracle.com/docs

基于Html5 Plus + Vue + Mui 移动App开发(三)-文件操作(读取、保存、更新数据)

实全资讯采用基于Html5 Plus + Vue + Mui 移动App.主要实现功能包括: 实现搜索站点设置 实现搜索关键字定义 实现搜索资讯保存.删除功能. 主界面实现关键字搜索.预定义关键字搜索,下拉刷新,支持搜索结果保存.分享. 我的界面主要是展示自己保存的搜索资讯,支持删除.分享. 设置界面主要实现搜索站点.初始搜索关键字定义. 搜索站点目前支持:凤凰资讯.参考信息.腾讯资讯.百度资讯: 读取数据 /*读取内容*/ shiquan.readNews = function(callbac

【Python】解析Python中的文件操作

目录结构: contents structure [-] 简介 Python中的文件类型 内置函数的文件操作 open()函数 Mode 创建文本文件 读取文本文件 循环文件对象 关闭文件 With语句 os模块 fileinput模块 1.简介 在Python中无需引入额外的模块来进行文件操作,Python拥有内置的文件操作函数(除了内置文件操作函数,Python语言也提供了额外的文件操作模块,它们具有更加强大的功能). os模块提供了在操作系统上可移植的文件操作方法.如果只是想要读取和写入数

Android数据存储-文件操作

一.预备知识 1.Android中的MVC设计模式 MVC (Model-View-Controller):M是指逻辑模型,V是指视图模型,C则是控制器.一个逻辑模型可以对于多种视图模型,比如一批统计数据你可以分别用柱状图.饼图来表示.一种视图模型也可以对于多种逻辑模型.使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式,而C存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新,这与<设计模式>中的观察者模式是完全一样. MVC好处:从用户的角度出发,用户可

【c++笔记十三】c++中的输入、输出和文件操作

2015年2月6日 周五 晴 很快到星期五了,感觉这一个星期都是在复习,这两周过后我觉得c++的基本知识应该掌握的差不多了,可以追求一点更高层次的东西了. 今天讲一讲c++中的输入.输出和文件操作,差不多c++基本语法就结束了.可能以后的笔记中不会再强调基本的语法知识. --------------分割线-------------- 其实在c语言中,我们就已经学习了基本的输入输出和文件操作,像什么printf.scanf.fopen.fclose之类的.其实c++的IO和文件都和c差不多,只不过

背水一战 Windows 10 (90) - 文件系统: 获取 Package 中的文件, 可移动存储中的文件操作, “库”管理

[源码下载] 背水一战 Windows 10 (90) - 文件系统: 获取 Package 中的文件, 可移动存储中的文件操作, “库”管理 作者:webabcd 介绍背水一战 Windows 10 之 文件系统 获取 Package 中的文件 可移动存储中的文件操作 “库”管理 示例1.演示如何获取 Package 中的文件FileSystem/PackageData/Demo.xaml <Page x:Class="Windows10.FileSystem.PackageData.D

Android 如何在linux kernel 中读写文件

前言 欢迎大家我分享和推荐好用的代码段~~ 声明 欢迎转载,但请保留文章原始出处: CSDN:http://www.csdn.net 雨季o莫忧离:http://blog.csdn.net/luckkof 正文 [Description] 如何在linux kernel 中读写文件 [Keyword] linux kernel read write file 读写文件 [Solution] 通常我们只会在linux native/app 层 读写文件,但可能有一些非常特别的情况下,我们需要直接在