Qt Model/View(MVD)模型分析

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

          一般来说,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。但在做具体项目时,程序员大部分时候还是要根据具体情况重写派生类数的。这对于那些想快速做界面并显示的程序员来说,可能会难以接受。总之,这个轮子做的够精细,但一般在使用之前你还要自己拧点螺丝,上点油。

  • 11
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: Qt Model/View Delegate是一个用于实现自定义视图的框架。它可以将数据模型与视图分离,从而简化了数据可视化的实现过程,并且遵循了MVC设计模式。Qt Model/View Delegate框架由三个组件组成:模型、视图和代理,它们分别负责每个领域的功能。 模型负责存储和管理数据,以及通知视图数据的修改。模型可以是一个简单的列表模型,也可以是一个具有层级结构和多个属性的复杂模型。 视图负责在屏幕上显示数据。Qt为我们提供了多种不同的视图类型,以满足不同的需求。不同类型的视图可以以许多不同的方式呈现模型数据,例如表格、树形、列表、图形化等等。 代理负责管理视图中的单元格的外观和行为。通过代理,我们可以自定义单元格的显示方式、编辑方式、数据验证和拖放等等。代理可以获得有关模型的完整信息,从而使开发人员可以轻松地为单元格提供自定义视图。 总之,使用Qt模型视图框架可以帮助我们快速简单地实现数据可视化,将数据与界面分离,提高代码的可维护性和可重用性。在Qt中,这个框架被广泛地应用于各种领域的应用程序开发。 ### 回答2: Qt Model-View-Delegate (MVC)是一种基于模型、视图和委托的设计模式,用于实现Qt框架中的表格、列表和树形等具有良好交互效果的UI控件。其主要思想是将底层模型和数据逻辑与表现形式分离开来,使得用户可以用统一的接口访问数据,而不必了解其内部实现细节。 MVC模式中,模型Model)是数据源,它负责存储和管理数据,可以是数据库、服务器或者本地文件等;视图(View)负责呈现模型中的数据,并与用户进行交互,用户所见到的界面就是视图呈现出来的结果;委托(Delegate)作为模型和视图之间的桥梁,负责处理鼠标、键盘事件等用户的操作,并将其反映到底层的模型中。 Qt框架中的QAbstractItemModel类提供了表格、列表和树形控件所需的模型接口,而QAbstractItemView类则提供了对应的视图接口。QItemDelegate类则是一个通用的委托类,它可以创建和管理单元格、行和列的编辑和显示,为数据提供不同的可视化效果。 使用Qt Model-View-Delegate框架可以帮助开发者简化数据管理和界面设计的任务,提高用户体验,同时增加了代码的可维护性和可扩展性。 ### 回答3: Qt Model-View-Delegate(MVD)是一个重要的框架,它是用于管理和展示大量数据的高效方式。该框架可以将数据与视图分离,从而使得用户可以更加灵活地处理数据和视图的交互。 Model是指QT提供的数据模型View是指数据展示的视图,Delegate则是视图的代理,主要负责对视图的各种单元格进行渲染,由Delegate完成格式化和显示控件等。 代理可以被用来实现视图和数据的进一步定制,改变视图对数据的显示方式或者增加行为特性。代理可以在显示表格视图、树视图等多种类型视图的时候,改变它们的样式和行为特性。 通过使用代理,我们可以自定义复选框、按钮、进度条等控件,添加到视图中以进行用户交互。我们还可以定制排序、筛选、背景颜色等视觉效果,从而创建一个更有效的用户界面。 总之,Qt MVD框架具有高度的灵活性和可扩展性,允许用户对数据和视图进行个性化的定制,提高应用程序的用户体验和效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值