玩转Qml(9)-Model和View
本文于 1047 天之前发表,文中内容可能已经过时。
- 简介
- 源码
- 界面、数据和逻辑分离
- Qt内置的Model-View
- 整数做model
- ListModel
- XmlListModel
- ObjectModel
- C++导出Model
- ListView缺失的灵魂
简介
本文是《玩转Qml》系列文章的第九篇,涛哥将教大家,Qml中Model和View的知识。
源码
《玩转Qml》系列文章,配套了一个优秀的开源项目:TaoQuick
github https://github.com/jaredtao/TaoQuick
访问不了或者速度太慢,可以用国内的镜像网站gitee
https://gitee.com/jaredtao/TaoQuick
界面、数据和逻辑分离
界面架构的理念发展的非常快,主要在Web技术的驱动下,就有这么多架构:
MVC、MVP、 MVVM、 Flux、Redux。
架构太多太复杂,只要抓住一些关键点就够了:界面、数据和逻辑要分别处理,最终要能够正确处理用户输入并显示结果。
先来看一下Qt中提供的架构:
Model代表数据,View代表界面,这个Delegate嘛,就是用来定制View的显示方式和Controll的调用,也应该算进View里面去。
这样看来Qt是M-V架构 ? 其实Qt算是MVC架构,这个Controll一般是自己实现的,和Model放在一起的。
不过Qt有信号/槽机制,在QtQuick中以属性绑定的方式出现。信号/槽相当于Gof设计模式中的观察者模式,也相当于Flux中的订阅/发布模式。
涛哥按自己的实践和理解,画了一个Qt的Model-View架构草图:
Qt内置的Model-View
View包括 ListView、TableView、TreeView这三种
(ComboBox也可以算作ListView)
对应的Model包括 ListModel、TableModel、TreeModel
Qt提供了一些抽象的Model类,需要自己去继承并实现接口,也有一些可以直接用。
下图是涛哥整理的Qt中model继承关系:
其中的QStringListModel不是抽象类,可以直接用在ListView中。
QStandardItemModel也不是抽象类,可以直接用在任意一种View中。
在数据量大、有性能要求的地方,需要继承QAbstractItemModel类,重新实现一个model。
对于性能要求不高的数据展示,会有一些更加方便、取巧的方式,接着往下看吧。
(友情提示:涛哥不关心QWidget,只说QtQuick/Qml)
整数做model
在ListView中,一个整数作为model,就可以创建多个delegate实例。
整数作为model,也可以用在GridView、Combobox、Repeater等需要model的地方。
<Qml组件化编程6-进度条定制>一文中,展示渐变效果,就用的整数作为model
1 | import QtQuick 2.9 |
关于delegate
简单说一下delegate:
上面GridView的 model设置为180,表示这个View要产生180个相同的构件实例,按照Grid的方式布局排列。
而delegate就相当于是一个模板,用来描述这180个相同的构件长啥样。当然每个实例不可能完全长得一样,我们可以通过
绑定delegate提供的内置属性或其它属性,达到”大同小异”的目的。
delegate中一般会提供一个index和一个modelData,详细的说明需要参考相应的View文档。
View与Repeater的区别
上面的GridView虽然会创建180个实例,但并不是一次创建全部的,而是只创建能看见的那几个,否则会占用很多CPU、内存和GPU资源。
而Repeater这种就是直接生成180个,并没有做任何内置处理。
(Repeater也可以通过自己控制visible的方式,实现部分创建,后面涛哥有个RingView特效会用这种方式)
ListModel
Qml提供了ListModel这样的一个封装,可以直接在Qml中定义静态的model
静态ListModel
1 | import QtQuick 2.0 |
然后在ListView中使用
1 | ListView { |
第一个text通过modelData.name获取到name值
第二个text直接用了cost,其实是modelData.cost省略了modelData。这种写法在静态的ListModel中是可以用的。
动态ListModel
ListModel还提供了一些动态修改数据的接口:
像append、 set、insert这些,参数里的jsobject就是js环境中的Object类型,可以参考JS手册
这里涛哥示例一下,动态添加元素
1 | ... |
更详细的用法,可以参考 涛哥两年前写过的一个Qml表格编辑器
里面有ListModel的JSON序列化和反序列化、动态增、删、改,Ubuntu风格的查找、Redo、UnDo等大部分功能。
TaoQuick项目的插件机制,也是通过JSON动态添加Model元素。TaoQuick
XmlListModel
处理xml的model,可以方便地使用XPath。
1 | XmlListModel { |
ObjectModel
可视对象的集合,做为model,连Delegate都省了
1 | import QtQuick 2.0 |
C++导出Model
除了以上这些,C++中导出的一些类型也可以作为数据model。
这里的导出包括Q_PROPERTY和 Q_INVOKABLE函数的返回值、槽函数的返回值,以及
setContextProperty注册到上下文的可用作model的类型。
一般使用Q_PROPERTY (本质上也是属性的get函数返回值,在js中做了转换)
QList
QList
QList<QObject*> QObject列表,List中的任意一个QObject有一些属性变更时,都能通知到Qml。
QJsonArray
QJsonArray也是可以直接导出给ListView用,不过注意是只读的。
QQmlPropertyMap
QQmlPropertyMap 是一个Map结构, 但是这个结构注册后,Qml中可以直接用”点 + 名字”的方式访问其中的数据
1 | // create our data |
1 | //main.qml |
ListView缺失的灵魂
Qml这个ListView是残缺不全的,很多功能都要自己实现。
搜索与排序
前面提到的QSortFilterProxyModel是一种在数据上实现排序和过滤的方法。
还有一种在View层实现搜索和过滤的方式,即DelegateModelGroup。(已经有案例在用,后续再放出代码)
当然Qt5.12的ListView/TableView提供了行和列 隐藏控制的功能,View层做搜索会更方便一些。(还没有实践)
选中
按住Ctrl 再鼠标点击,多选, 再点击一下反选。
按住Shift再鼠标点击,连选。
旧的QtQuick.Controls 1中也有一个ListView,带SelectonModel功能,直接支持多选、反选。
5.12开始,QtQuick.Controls 1模块被废弃了,而Controls2中的ListView不带这功能了。只能自己记键盘按键来模拟实现。
(顺便吐槽一下,5.12直接把Controls 1的TreeView废掉了,Controls 2又没有TreeView。Controls 1的那个虽然还能用,程序跑起来就是一堆js 异常)
拖拽
拖动和放置功能也得自己做。
特效
ListView提供过度动画,下拉刷新一类的效果很多人已经做了,涛哥就不重复了。
(涛哥正在给TaoQuick开发高级插件TaoEffect,将会包含大量酷炫特效组件,敬请期待)
为众人抱薪者,不可使其冻毙于霜雪