原文链接:QRowTable表格控件(三)-效率优化之-合理使用QStandardItem

一、开心一刻

砖家在河边看到两只乌龟缩着一动不动,问一农民:“它们在干吗?”。

农民说:“在比赛!”。

砖家不解:“动都没动过,比什么赛?”。

农民:“在比装死!”。

砖家:“可是壳上有甲骨文的那只,早就死了啊?”。

这时,一只乌龟猛然探出头来骂道:“MD,死了也不吭一声!”。

突然另一只也伸出头来:“傻子!砖家的话你也信,你输了 !”。

二、概述

最近换了一家新单位,工作的内容也发了一些变化。接触了一些大牛,也让我对Qt有了一个新的认识。

不看源码真他妈不行呀

这不今天就给大家说一说我最近工作中遇到的一个坑,而这个坑只有看了源码后才明白。

上一篇QRowTable表格控件(二)-红涨绿跌文章讲到了我们怎么往表格中添加数据,并且是了一个简单的股票组件。可以存放各种行情数据、持仓和订单等等。

下面问题就来了,我这个demo中的数据只有不到10行,当你真的把这个控件投入的生产环境时就会发现,demo就是demo,它就是个demo而已

博主自己大概测试了下把数据调到10000行,等了几分钟界面还没有出来,就放弃了。

测试代码如下:

int rate = 10000;
model->setRowCount(rate * itemVec.size()); for (int j = 0; j < rate; ++j)
{
for (int i = 0; i < itemVec.size(); ++i)
{
const OptionalMarketItem & info = itemVec.at(i); QStandardItem * item_price = new QStandardItem;
item_price->setText(QString::number(info.price));
item_price->setData(int(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
model->setItem(i, 0, item_price); ...
}
}

既然代码性能不行,我们当然需要去找更好的实现方式了,总不能就这么上线吧。

于是乎,有了如下代码,30000行数据1-2s即可初始化完毕,震惊脸

int rate = 10000;
model->setRowCount(rate * itemVec.size()); for (int j = 0; j < rate; ++j)
{
for (int i = 0; i < itemVec.size(); ++i)
{
const OptionalMarketItem & info = itemVec.at(i); int row = i + j * itemVec.size(); QModelIndex index = model->index(row, 0);
model->setData(index, QString::number(info.price), Qt::DisplayRole);
model->setData(index, int(Qt::AlignRight | Qt::AlignVCenter),Qt::TextAlignmentRole);
}
}

我槽,上述两种书写方式有球区别,怎么会差别如此之大,下面让我为大家细细道来。

三、效果展示

以下是红涨绿跌效果图,demo中展示了30000数据,应该算是比较多,可以满足大多数的应用场景。

腹黑版

四、QStandardItem

1、QStandardItem是什么鬼

Qt的帮助文档是一个好东西,打开assisant.exe,搜索QStandardItem类,可以搜索如下提示信息。

什么意思呢!

为了阅读起来更流畅,我这里就行中文内容的意译。

虽然英文解释很多,但是意译成中文后就很简单了,毕竟帮助文档要说的很清晰、场景囊括的会比较全一些

意译:QStandardItem是一个数据结构,他可以存储一个cell的各种信息,比如文本、图标、是否可选、字体、别景色、前景色等等。并且QStandardItem可以有孩子和兄弟,他是为model提供数据存储的节点。

这里我在补充一些内容,让大家对QStandardItem右更进一步的了解。

QTableView:作为表格cell时,有一个作为根节点的QStandardItem,其他节点都是QStandardItem节点的孩子节点,并且都是兄弟节点(这里暂时不考虑多列的情况)。

QTreeView:作为树节点cell时,有一个作为根节点的QStandardItem,其他节点都是他的孩子节点,但是其他节点也可以作为父节点存在(这里暂时不考虑多列的情况)。

2、性能分析

a、QStandardItem构造慢?

简单了解QStandardItem对象后,下面开始从代码上分析性能问题到底出现在了哪里。

//优化前代码
QStandardItem * item_price = new QStandardItem;
item_price->setText(QString::number(info.price));
item_price->setData(int(Qt::AlignRight | Qt::AlignVCenter), Qt::TextAlignmentRole);
model->setItem(i, 0, item_price); //优化后代码
int row = i + j * itemVec.size(); QModelIndex index = model->index(row, 0);
model->setData(index, QString::number(info.price), Qt::DisplayRole);
model->setData(index, int(Qt::AlignRight | Qt::AlignVCenter),Qt::TextAlignmentRole);

仔细比较以上代码,优化前的diamante是我们自己构造了QStandardItem,然后设置数据并存储到QStandardItemModel中,而优化后的代码我们直接把数据通过QStandardItemModel进行了设置。

这两种方式到底有何区别???

解决这种问题,Qt给我们提供了很好的问题解决方式,直接跟踪源码即可。想要把Qt了解透彻,源码是唯一的途径。其他什么各种搜索引擎都弱爆了。

如上图所示,跟踪Qt的源码发现,当我们通过Model设置数据项时,Qt内部也是为我们构造了一个QStandardItem对象,然后把数据放到这个对象上的。

说明QStandardItem对象的构造并不是性能所在,性能问题还需要进一步分析。

很重要:Qt的Model中把数据又单独封装了一层,数据存储在QStandardItem对象中。本篇文章主要分析的性能瓶颈在QStandardItem对象的使用上,如果想要极致的性能体验,还有比本篇文章更容易的方式,只是需要自己写的代码就会变得更多,如果有需要的话可自行搜索自定义Model,然后自己对数据进行管理,这样就少了QStandardItem对象的构造和很多数据类型的转换

由于博主使用的场景,表格数据不会超过100行,因此轻量级的处理已经可以满足需求,没有进一步去重写Model数据源管理。

百度不到的话,欢迎评论区留言,后续博主有时间进一步优化。

b、setData有问题?

先抛出答案,问题确实处在setData上

如下两种图是QStandardItem在设置数据孩子数据时很重要的一个调用,把参数中的item设置为当前节点的孩子节点。

仔细看图中红色框圈起来的内容,有一个emitChanged变量控制了3个信号的触发。

问题就出现在这个emitChanged变量上,他的意思就是说当前item是否发现了变化。

仔细回想我们优化前的代码,QStandardItem对象是不是我们自己构造的,然后设置给了Model,这是不是搬起石头砸自己的脚。

仔细一分析:好像是这么回事,优化前的代码在行数较少时不会有明细问题,可是当数据量很大时,其实这是有问题的。

c、性能根源

既然知道是多发送了3个信号导致了性能问题,那么接下来就是分析这3个信号都干了什么。

下面按触发顺序来分别解释每一个信号

1、layoutAboutToBeChanged

如下图是帮助文档描述

意译:该信号的触发在model的布局即将发生变化时触发。model还有布局,这是什么鬼,其实就是说model中的item发现了变化。

这个信号其实还提供了参数,可以方便我们对某一些节点进行刷新,当我们指定了父节点和刷新策略时生效。

QStandardItem的setData这里没有指定参数,表示全量刷新,使用时需要非常注意。

2、layoutChanged

如下图是帮助文档描述

意译:该信号的触发在model的布局发生变化之后,也就说需要全量刷新model时,可以通过触发该信号达到目的。

比如重新排序、数据源发送变化等。

这个信号不建议大家主动调用,数据量大时会导致性能问题

如果非要调用,也应该到信号的参数带上这样就是局部刷新

博主之前做过一个控件,是优化QTreeview控件相关的,意思是说想让QTreeView的行高可以自定义。

做过这块内容的同学可能都知道,Model在通过data函数获取数据时有一个字段role,这个字段表示了他想获取什么样的数据,解决办法也就在这里了,当role等于Qt::SizeHintRole时,表示我们想要获取的行高,我们通过这里设置一个合适的行高即可。

Qt为了优化性能,不会每一次都计算树控件的行高,这里做了一个优化,只有第一次也就说Model发现变化时才从QStandardItem中获取行高,然后所有的额行高信息都存储在了视图的ViewItem缓存中,这直接导致了我们在界面上拖拽垂直表头行高,内容行高不会发生变化。

这里就需要用到layoutChanged信号,当我们给QStandardItem重新设置了行高之后,需要激活Model布局发生变化事件。

3、itemChanged

如下图所示,看名字就知道itemChanged这个喜好是干嘛使得。

分析了以上3个函数,大家心里是不是对QStandardItem有了一个全新的认识。


既然自己构造item这么坑,博主建议大家干脆就不要使用new QStandard这句代码了。

凡事总有例外,既然Qt把这个类导出给我们使用了,总是有他的道理,对于一些特殊场景可能需要自定义item,这时候Qt建议我们是这么做的。

如上图,我们需要重写几个函数,这里大家知道就行。大家记住,一般情况下都不需要这么干。


3、QStandardItem使用上的坑

1、原则上QStandardItem不需要我们去构造,使用Model的index函数访问cell时Qt内部会帮我们构造,特别是对于数据量大时,Qt内部构造会有很大的效率提升。

2、Model的setItem使用上需要注意,除非一些特殊场景(比如我们自定义item),否则尽量不要使用。

自定义item,需要重写很多东西;设置item时,原有item将会被删除

3、对于需要设置cell自定义窗口用法

通过指定行列设置

setCellWidget(int row, int column, QWidget *widget)

五、相关文章

  1. Qt实现表格控件-支持多级列表头、多级行表头、单元格合并、字体设置等

  2. Qt高仿Excel表格组件-支持冻结列、冻结行、内容自适应和合并单元格

  3. 属性浏览器控件QtTreePropertyBrowser编译成动态库(设计师插件)

  4. 超级实用的属性浏览器控件--QtTreePropertyBrowser

  5. Qt之表格控件蚂蚁线

  6. QRowTable表格控件-支持hover整行、checked整行、指定列排序等

  7. QRowTable表格控件(二)-红涨绿跌

如果您觉得文章不错,不妨给个打赏,写作不易,感谢各位的支持。您的支持是我最大的动力,谢谢!!!

很重要--转载声明

  1. 本站文章无特别说明,皆为原创,版权所有,转载时请用链接的方式,给出原文出处。同时写上原作者:朝十晚八 or Twowords

  2. 如要转载,请原文转载,如在转载时修改本文,请事先告知,谢绝在转载时通过修改本文达到有利于转载者的目的。


QRowTable表格控件(三)-效率优化之-合理使用QStandardItem的更多相关文章

  1. QRowTable表格控件(四)-效率优化之-优化数据源

    目录 一.开心一刻 二.问题分析 三.重写数据源 1.自己存储数据 2.重写data接口 四.比较 五.相关文章 原文链接:QRowTable表格控件(四)-效率优化之-优化数据源 一.开心一刻 一程 ...

  2. QRowTable表格控件(五)-重写表头排序、支持第三次单击恢复默认排序

    目录 一.原生表格 二.效果展示 三.实现方式 1.排序列定制 2.排序交互修改 四.相关文章 原文链接:QRowTable表格控件(五)-重写表头排序.支持第三次单击恢复默认排序 一.原生表格 开发 ...

  3. QRowTable表格控件-支持hover整行、checked整行、指定列排序等

    目录 一.开心一刻 二.嘴一嘴 三.效果展示 四.浅谈实现 五.自定义数据源 1.data函数 2.flags函数 六.自定义视图 1.目的 2.问题分析 七.测试 八.相关文章 原文链接:QRowT ...

  4. QRowTable表格控件(二)-红涨绿跌

    目录 一.开心一刻 二.概述 三.效果展示 四.任务需求 五.指定列排序 六.排序 七.列对其方式 八.相关文章 原文链接:QRowTable表格控件(二)-红涨绿跌 一.开心一刻 一天,五娃和六娃去 ...

  5. QTableView表格控件区域选择-自绘选择区域

    目录 一.开心一刻 二.概述 三.效果展示 四.实现思路 1.绘制区域 2.绘制边框 3.绘制 五.相关文章 原文链接:QTableView表格控件区域选择-自绘选择区域 一.开心一刻 陪完客户回到家 ...

  6. 纯前端表格控件SpreadJS以专注业务、提升效率赢得用户与市场

    提起华为2012实验室,你可能有点陌生. 但你一定还对前段时间华为的那封<海思总裁致员工的一封信>记忆犹新,就在那篇饱含深情的信中,我们知道了华为为确保公司大部分产品的战略安全和连续供应, ...

  7. 深入浅出ExtJS 第三章 表格控件

    3.1 表格的特性简介 >.Ext中的表格功能:包括排序/缓存/拖动/隐藏某一列/自动显示行号/列汇总/单元格编辑等实用功能; >.表格由类Ext.grid.GridPanel定义,继承自 ...

  8. [Xcode 实际操作]三、视图控制器-(11)在Storyboard中使用表格控件

    目录:[Swift]Xcode实际操作 本文将演示表格控件在故事板中的使用. 点击[显示或隐藏检查器按钮],再界面右侧打开检查器面板. 在控制器根视图上点击鼠标,以选择该根视图. 现在往根视图中添加一 ...

  9. 在GridControl表格控件中实现多层级主从表数据的展示

    在一些应用场景中,我们需要实现多层级的数据表格显示,如常规的二级主从表数据展示,甚至也有多个层级展示的需求,那么我们如何通过DevExpress的GridControl控表格件实现这种业务需求呢?本篇 ...

随机推荐

  1. ML:吴恩达 机器学习 课程笔记(Week9~10)

    Anomaly Detection Recommender Systems Large Scale Machine Learning

  2. MySQL5.7.19版本压缩包安装方式的一些坑

    ySQL社区版下载地址:https://dev.mysql.com/downloads/mysql/,在这里也可以选择之前的版本下载. MySQL进入5.7.7版本以后,压缩包安装需要注意一些地方: ...

  3. 比较DirectX和OpenGL的区别(比较详细)

    OpenGL是个专业的3D程序接口,是一个功能强大,调用方便的底层3D图形库.OpenGL的前身是SGI公司为其图形工作站开发的IRIS GL.IRIS GL是一个工业标准的3D图形软件接口,功能虽然 ...

  4. 关于 win32 下磁盘的遍历方法

    最近要写个在线专杀的东东,虽然是专杀(本来只要清除几个特定的文件和杀几个特定的进程,然后把用户的注册表恢复正常,很多病毒木马最喜欢干的一件事情就是写 映像劫持 然后机器一重启,安全相关的软件全部玩完了 ...

  5. Lamda一行代码实现"36选7"随机自动选号

    南粤风采36选7是广东的一种彩票玩法.非常简单的从1-36个数字选7个. 今天在同事面前炫耀了一把,只用一行Lamda代码实现随机自动选号 Enumerable.Range(, ).Select(x ...

  6. Windows Phone8.1系统新特性

    Windows Phone 8.1 beta SDK已经为大家透露了不少WP8.1系统的新特性,不过这些新特性还不能保证在最终的消费者版本中都有所体现,毕竟它还仅是SDK版本.日前,国外媒体WPCen ...

  7. java集合框架collection(4)HashMap和Hashtable的区别

    HashMap和Hashtable的区别 HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别.主要的区别有:线程安全性,同步(synchronizatio ...

  8. 基于Bert的文本情感分类

    详细代码已上传到github: click me Abstract:    Sentiment classification is the process of analyzing and reaso ...

  9. Idea中maven项目中导入本地jar包

    说一下正确的流程: 1.将jar包导入到本地maven库或者远程nexus仓库,再使用maven引用: 2.导入命令: //通过一下信息引入maven -DgroupId=com.test -Dart ...

  10. Mac下安装redis5.0 与命令

    参考链接:https://blog.csdn.net/zyp1376308302/article/details/84257606 参开链接2:https://www.cnblogs.com/guan ...