最近在看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

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

  1. Qt 之 Graphics View Framework 简介

    Graphics View Framework 交互式 2D 图形的 Graphics View 框架概述.自 Qt4.2 中引入了 Graphics View,以取代其前身 QCanvas.Grap ...

  2. c++中读写文件操作

    读写文件这个,不常用,每次用的时候都会百度一下,每次写法还都不一样,所有总是记混.今天利用点时间总结下之前工程中用过的.以后就安照这种方法写了. 搞acmicpc的时候喜欢用freopen(),这个是 ...

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

    1.SQL TO String :只返回一个查询结果 例如查询某条记录的总数 rs = stmt.executeQuery(replacedCommand);             if (rs ! ...

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

      随着手机的发展,现在越来越多的人选择在手机上看书.无论是专业书籍.文学.英语还是网络小说,在手机上看新闻成了人们处理零碎时间的办法.在智能手机里安装一个资讯APP,可以随时.随地查看自己想看的资讯 ...

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

    在oracle中utl_file包提供了一些操作文本文件的函数和过程,学习了一下他的基本操作 1.创建directory,并给用户授权 复制代码 代码如下: --创建directory create ...

  6. 使用 JDBC 操作数据库时,如何提升读取数据的性能?如 何提升更新数据的性能?

    要提升读取数据的性能,可以指定通过结果集(ResultSet)对象的 setFetchSize() 方法指定每次抓取的记录数(典型的空间换时间策略):要提升更新数据的性能 可以使用 PreparedS ...

  7. Qt Model/View(官方翻译,图文并茂)

    http://doc.trolltech.com/main-snapshot/model-view-programming.html 介绍 Qt 4推出了一组新的item view类,它们使用mode ...

  8. PyQt(Python+Qt)学习随笔:Model/View架构中的Model模型概念

    老猿Python博文目录 专栏:使用PyQt开发图形界面Python应用 老猿Python博客地址 Model/View架构中的Model模型Model与数据源通信,为体系结构中的其他组件提供数据接口 ...

  9. Django框架中的model(操作数据库)

    什么是ORM ORM,即Object-Relational Mapping(对象关系映射),它的作用是在关系型数据库和业务实体对象之间作一个映射,这样,我们在具体的操作业务对象的时候,就不需要再去和复 ...

随机推荐

  1. SQL Server 使用ROW_NUMBER()进行分页

    代码示例: WITH domain AS(SELECT ROW_NUMBER() OVER(ORDER BY ID DESC) ids,* FROM dbo.DomainInfo) SELECT * ...

  2. [转载]VMWare网络连接透析

    http://blog.csdn.net/struggleyb/article/details/1102214 以前在学校,VMWare里面的Gentoo Linux是采用network bridge ...

  3. android EncodingUtils

    EncodingUtils 报错Cannot Resolve Symbol EncodingUtils   提示是:错误:程序包org.apache.http.util不存在 错误:找不到符号  符号 ...

  4. qt学习:信号,槽

    [C.GUI.Qt.4编程(第二版)](加)布兰切特,(英)萨默菲尔德.扫描版[学习库www.xuexi111.com].pdf 信号和槽是Qt编程的基础,它可以让各种对象联系在一起. 1. 使用co ...

  5. 使用Docker官方的Django包【转】

    官方Django docker,并没有安装Django 所以需要 在requirements.txt中配置Django 具体安装流程可以参考:http://www.logme.cn/blog/51/u ...

  6. hadoop笔记之hdfs

    1.HDFS设计基础与目标 1.HDFS设计基础与目标 (1)硬件错误是常态,因此需要冗余. (2)流式数据访问.即数据批量读取而非随机读写,Hadoop擅长做的是数据分析而不是事务处理. (3)大规 ...

  7. Gulp browserify livereload

    Gulp browserify livereload 之前在browserify那个博文中介绍了gulp + browserify 不过那个配置还不能满足日常需要 搬运 https://github. ...

  8. Linux例行工作crontab

    第一步编辑要定时执行的脚本: myScript.sh myScript.sh的内容为:touch /root/`date +%F' '%T`.txt 为myScript.sh增加可执行权限:chmod ...

  9. Git库文件的状态

    git库所在的文件夹(即.git所在的文件夹)中的文件的状态: (1)untracked:未跟踪,此文件在文件夹中,但并没有加入git库,不参与版本控制. 通过”git add”,”git commi ...

  10. [置顶] .NET下枚举类型的Save和Load分析

    今天在写代码的时候,心血来潮对原来的字符串保存状态位的方式很不满意,对于代码里出现了 state == "1" 这样的状态判断很是不爽.那么理想中的判断是怎样的呢?很简单如你所想枚 ...