原文连接: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数据,应该算是比较多,能够知足大多数的应用场景。
腹黑版
Qt的帮助文档是一个好东西,打开assisant.exe,搜索QStandardItem类,能够搜索以下提示信息。
什么意思呢!
为了阅读起来更流畅,我这里就行中文内容的意译。
虽然英文解释不少,可是意译成中文后就很简单了,毕竟帮助文档要说的很清晰、场景囊括的会比较全一些
意译:QStandardItem是一个数据结构,他能够存储一个cell的各类信息,好比文本、图标、是否可选、字体、别景色、前景色等等。而且QStandardItem能够有孩子和兄弟,他是为model提供数据存储的节点。
这里我在补充一些内容,让你们对QStandardItem右更进一步的了解。
QTableView:做为表格cell时,有一个做为根节点的QStandardItem,其余节点都是QStandardItem节点的孩子节点,而且都是兄弟节点(这里暂时不考虑多列的状况)。
QTreeView:做为树节点cell时,有一个做为根节点的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数据源管理。
百度不到的话,欢迎评论区留言,后续博主有时间进一步优化。
先抛出答案,问题确实处在setData上
以下两种图是QStandardItem在设置数据孩子数据时很重要的一个调用,把参数中的item设置为当前节点的孩子节点。
仔细看图中红色框圈起来的内容,有一个emitChanged变量控制了3个信号的触发。
问题就出如今这个emitChanged变量上,他的意思就是说当前item是否发现了变化。
仔细回想咱们优化前的代码,QStandardItem对象是否是咱们本身构造的,而后设置给了Model,这是否是搬起石头砸本身的脚。
仔细一分析:好像是这么回事,优化前的代码在行数较少时不会有明细问题,但是当数据量很大时,其实这是有问题的。
既然知道是多发送了3个信号致使了性能问题,那么接下来就是分析这3个信号都干了什么。
下面按触发顺序来分别解释每个信号
一、layoutAboutToBeChanged
以下图是帮助文档描述
意译:该信号的触发在model的布局即将发生变化时触发。model还有布局,这是什么鬼,其实就是说model中的item发现了变化。
这个信号其实还提供了参数,能够方便咱们对某一些节点进行刷新,当咱们指定了父节点和刷新策略时生效。
QStandardItem的setData这里没有指定参数,表示全量刷新,使用时须要很是注意。
二、layoutChanged
以下图是帮助文档描述
意译:该信号的触发在model的布局发生变化以后,也就说须要全量刷新model时,能够经过触发该信号达到目的。
好比从新排序、数据源发送变化等。
这个信号不建议你们主动调用,数据量大时会致使性能问题
若是非要调用,也应该到信号的参数带上这样就是局部刷新
博主以前作过一个控件,是优化QTreeview控件相关的,意思是说想让QTreeView的行高能够自定义。
作过这块内容的同窗可能都知道,Model在经过data函数获取数据时有一个字段role,这个字段表示了他想获取什么样的数据,解决办法也就在这里了,当role等于Qt::SizeHintRole时,表示咱们想要获取的行高,咱们经过这里设置一个合适的行高便可。
Qt为了优化性能,不会每一次都计算树控件的行高,这里作了一个优化,只有第一次也就说Model发现变化时才从QStandardItem中获取行高,而后全部的额行高信息都存储在了视图的ViewItem缓存中,这直接致使了咱们在界面上拖拽垂直表头行高,内容行高不会发生变化。
这里就须要用到layoutChanged信号,当咱们给QStandardItem从新设置了行高以后,须要激活Model布局发生变化事件。
三、itemChanged
以下图所示,看名字就知道itemChanged这个喜爱是干吗使得。
==分析了以上3个函数,你们内心是否是对QStandardItem有了一个全新的认识。==
==既然本身构造item这么坑,博主建议你们干脆就不要使用new QStandard这句代码了。==
凡事总有例外,既然Qt把这个类导出给咱们使用了,老是有他的道理,对于一些特殊场景可能须要自定义item,这时候Qt建议咱们是这么作的。
如上图,咱们须要重写几个函数,这里你们知道就行。你们记住,通常状况下都不须要这么干。
一、原则上QStandardItem不须要咱们去构造,使用Model的index函数访问cell时Qt内部会帮咱们构造,特别是对于数据量大时,Qt内部构造会有很大的效率提高。
二、Model的setItem使用上须要注意,除非一些特殊场景(好比咱们自定义item),不然尽可能不要使用。
自定义item,须要重写不少东西;设置item时,原有item将会被删除
三、对于须要设置cell自定义窗口用法
经过指定行列设置
setCellWidget(int row, int column, QWidget *widget)
![]() |
![]() |
很重要--转载声明