Qt Model/View 学习(1) - 是什么和为什么?


0. 前言

一直想学一下Qt下的Model/View编程方法,但是搜了一圈CSDN上可能还没有一套比较完整的博客。就想着好好学,啃一啃官方文档,结合自己的理解,开这个Qt Model/View专栏,将自己学习理解的过程记录下来。

本专栏基本上参考官方教程Qt Model/View Programming,也可以在安装的Qt Assistant中搜索Model/View找到官方教程,Qt5以上的版本应该教程区别不会太大。

本文主要探究Qt的Model/View框架到底是什么,它能在什么情况下给我们带来何种便利


1. 初识Model/View

1.1 故弄玄虚

在Qt中最容易看到的关于该框架内容的位置应该在下图,在Qt Creator中编辑界面时的控件,有Model-BasedItem-Based两种。
在这里插入图片描述
我自己的体会是:一般就用下面的Widget了,它的接口理解起来比较简单,也不像上面的View那样,不但运行起来啥也没有,好不容易找到一个setModel()接口,但使用之后一言不合就容易崩溃

这给人一种故弄玄虚的感觉:明明放在很显眼的位置,但使用起来却门槛过高。不是说Qt很容易嘛?

1.2 优越感

另外,在使用Item-Based控件时,官方文档中无时无刻不在提醒着Model-Based控件的高级性

比如官方文档在QListWidget的介绍中写道:如果想要一个更灵活的列表控件,请使用一个带有标准模型的QListView
在这里插入图片描述
又如在QTableWidget的介绍中:如果想使用自己的数据模型,你应该用QTableView而不是本类
在这里插入图片描述
好吧,那么充满高级感的功能,不得学习一下(去装X )?打开官方文档的Model/View Programming教程看一眼,这个篇幅的全英文文档,当场劝退了,直呼“我不配”。
在这里插入图片描述
在这里插入图片描述
在认怂了相当多次,发现了一些可能确实需要使用Model/View框架的场合之后,终于下定决心啃一啃了,来吧。


2. 什么是Model/View

2.1 起源

QtModel/View来源于SmallTalk语言的MVC框架Model-View-Controller,模型-视图-控制器),模型指底层数据的模型,视图指上层表现形式,控制器负责将数据与视图连结,实现数据与视图分开的效果。

数据和视图的分开本质上是工作内容解耦,提升工作效率和专注度,比如前端和后端的分开也是解耦的结果。

MVC提出已经过去几十年了,出现了很多变种,Qt的Model/View也可以看做一种变种,它将视图与控制器结合了,所以只剩下ModelView了。

2.2 初探

官方教程的图片对于这二者的关系描述得再清楚不过了:
在这里插入图片描述
不过看到这个图首先就会有一个疑问:Model不是数据模型吗?怎么还有一个Data?

这或许是一个理解MVC的难点,很多人容易将二者混淆。

Data是代码中具体的数据结构Model是对这些数据的解析方法View根据Model的解析方法来展示视图内容。

此处举一个简单例子帮助理解:
在这里插入图片描述

从这里可以看出来,底层数据可以很散漫,甚至不一定是一维数组,一堆散装变量也没问题,经过Model解析映射以后可以基本看不出原有的样子了,然后View的表现形式可以很丰富,甚至与数据可能看起来毫不相干。

另外,还应该有一个问题:不是说好的Model/View,这个多出来的Delegate是什么鬼?

这个👻建议现阶段暂时不要深究它,理由如下:

  1. 在以实现基本功能为目标的时候,可以不使用它;
  2. Delegate的上手难度大于ModelView,可能会存在一些和QPainter、QStyle之类的梦幻联动,前期去接触它容易劝退;

这里大概讲一下它能干什么:

  1. 在不主动使用Delegate时,Qt会提供一个默认的Delegate来完成视图的渲染(rendering); 它的做法基本是将模型中的数据解析为字符串QString),然后设置到视图对应位置;
  2. 它提供了在View编辑数据的功能;

3. 为什么使用Model/View

现在,我们已经知道了Model/View大致的思路就是:我们自己有一个数据结构Data(甚至没有结构,只有一堆散装变量也无伤大雅),然后搞一个Model来解析它们,最后通过ViewData显示出来。

那本着批判性思维,可以毫不客气地说:就这?我不搞那么麻烦又ModelView的,直接拖一堆控件一个个对应上就好了,友谊的小船我一个人也能划!

那什么情况下我们使用Model/View会更好呢?以下是笔者能想到的场景,仅供参考~

3.1 数据有规律/规模/维度变化

  • 规律:典型地,如果底层是一个二维数组,采用QTableView就几乎完美;
  • 规模:20个的数据拖控件还是可以接受的,如果200个要拖控件那就可以裂开了;但是如果数据是有规律的,只需要定义Model的解析模型即可,剩下的交给View
  • 维度变化:如果一开始不知道有多少数据,这个要想拖控件就不太灵了;当然也可以采用new运行时创建控件,再用合适的QLayout进行布局,但是如果创建/删除比较频繁(比如10Hz)很容易导致界面卡死;当然,也可以先创建好预留对象,等使用的时候再显示出来,形成一套缓存机制……emmm,开心就好 😃……反观Model/View底层的效率一般比自己实现的效率要更高(大佬请别喷),该用就用吧~

3.2 视图与数据不是对象关联

听起来比较晦涩,举个例子:

界面要显示学生信息,但是有很多个学生,界面上的数据就不是与某个对象关联了。更直观地说:没法拖控件然后connect;当然,也可以采用一个指针指向当前学生的对象地址,然后使用lambda表达式连接相关信号,当更换显示的学生时emit一个刷新界面的信号 😃……

似乎总能想到办法来用connect和创建控件结合,以绕过Model/View的使用……但是,接下来的内容,可能就不会那么容易了。

3.3 数据需要在视图中大面积修改

比如一个要直接显示5个学生的信息,每个学生的信息还需要在界面直接能修改,再采用connect来做就直接裂开吧。

这边涉及到了在视图上更改数据,采用connect方式的话,比起只读取显示,代码量直接翻倍了。

3.4 时间紧任务重

为什么现在岗位分成了前端后端,浅显地理解就是一个人在规定时间无法完成前后端的工作

到此可能都有点忘了,Model/View的初衷就是视图显示与底层数据分开降低工作耦合

所以,在软件开发中使用Model/View思想无疑是正确的,即使时间足够充裕,没有那么多苛刻条件和奇怪需求,也建议使用Model/View


4. 小结

  1. Qt中的Model/View框架起源于MVC框架,核心思想是降低工作耦合(前后端分开);
  2. 思路是先有底层数据Data,然后采用一个Model定义数据映射解析规则,最后View通过与Model交互实现数据的显示和修改;
  3. Delegate这个👻先不用去理会它(以免被劝退 );
  4. 无论什么情况,都建议遵循降低耦合的思想去编码,建议使用Model/View框架。

如有错误欢迎指正,共同进步~


今天你学废了吗?

  • 64
    点赞
  • 95
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
Qt Model/View/Delegate 是 Qt 提供的一种用于实现 MVC 设计模式的框架,其中 Model 负责存储数据,View 负责显示数据,Delegate 负责定制数据的显示方式。 在 Model 中,我们可以通过实现 `roleNames()` 函数来定义数据的角色名称。这些角色名称可以被 View 和 Delegate 使用来访问 Model 中的数据。 `roleNames()` 函数返回一个 `QHash<int, QByteArray>` 类型的值,其中键是角色的编号,值是角色的名称。对于一个标准的 ModelQt 提供了一些已经定义好的角色,例如 `Qt::DisplayRole` 用于显示数据,`Qt::EditRole` 用于编辑数据等。 我们也可以自定义角色,例如在一个存储学生信息的 Model 中,我们可以自定义一个 `Qt::UserRole` 角色,用于存储学生的分数信息。在 `roleNames()` 中,我们可以这样定义: ```cpp QHash<int, QByteArray> StudentModel::roleNames() const { QHash<int, QByteArray> roles; roles[Qt::DisplayRole] = "display"; roles[Qt::UserRole] = "score"; return roles; } ``` 在 View 中,我们可以使用 `data()` 函数来访问 Model 中的数据,同时指定要访问的角色。例如: ```cpp QVariant score = model->data(modelIndex, Qt::UserRole); ``` 在 Delegate 中,我们可以重写 `setModelData()` 函数来更新 Model 中的数据,同时指定要更新的角色。例如: ```cpp void ScoreDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QLineEdit *lineEdit = static_cast<QLineEdit*>(editor); QString score = lineEdit->text(); model->setData(index, score, Qt::UserRole); } ``` 通过定义和使用自定义角色,我们可以更加灵活地操作 Model 中的数据,并将数据以更加自然的方式展示给用户。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值