原文连接:QRowTable表格控件(四)-效率优化之-优化数据源程序员
一程序员第一次上女友家她妈板着脸问 :你想娶我女儿,有多少存款?浏览器
程序员低了下头:五百!缓存
她妈更鄙视了:才五百块,买个厕所都不够!ide
程序员忙说:不是人民币!函数
她妈:就算是美圆,仍是不够买厕所!性能
程序员:实际上是比特币!字体
她妈:哇,贤婿,我给你买只大龙虾去优化
前边已经写了3篇关于表格控件的功能,分别是QRowTable表格控件-支持hover整行、checked整行、指定列排序等、QRowTable表格控件(二)-红涨绿跌和QRowTable表格控件(三)-效率优化之-合理使用QStandardItem,这三篇文章主要是围绕实现核心功能来说述的通常实现方式,当数据量多大时就会出现性能问题。spa
既然出现问题,固然是须要解决的。本篇文章就来说述怎么处理大量数据的状况。
首先咱们先来分析下上述几种实现方式为何会比较消耗时间,首先代码量也不大,在代码里随机打几个断点,咱们就会发现,代码在循环构造QStandardItem这个结构中耗费的时间比较久,而且当for循环出现上万次循环时尤其明显。
找到问题后,就是想办法怎么能够更少的调用构造QStandardItem这个流程,固然了Qt也给咱们提供了很好的解决方案,那就是重写数据源(Model)。
Qt中包含有经典的MVC模式,好比咱们常用的QStandardItemModel、QTableView和QStyledItemDelegate,当咱们要实现一个高效的表格控件时,重写这3个类基本就能够完成咱们所须要的功能。
固然了Qt还提供了了一层数据缓存层QSortFilterProxyModel,这个类能够帮助咱们更好的实现排序、模糊搜索功能
本篇文章这里只讲解重写数据源,关于其余两个类的重写前面文章中应该有所讲述,这里再也不过多解释。
下面一块儿来看下数据源的重写方式,咱们这里选择继承自QStandardItemModel这个类来实现咱们的数据源,这里是一个偷懒的方式,正常状况下是须要重写QAbstractItemModel类,若是重写QAbstractItemModel类的话,那么就须要重写更多的接口。
class QRowModel : public QStandardItemModel { Q_OBJECT public: explicit QRowModel(QObject * parent = 0); ~QRowModel(); public: //设置数据源 void SetSourceData(const TradeOrderInfoList & data); ... protected: virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; virtual void sort(int column, Qt::SortOrder order /* = Qt::AscendingOrder */) override; private: ... TradeOrderInfoList itemList; QColor m_CheckedColor = QColor("#4F4F4F"); mutable std::map <int, int> m_AlignmentList;//列对其方式 friend class QRowTable; };
上次代码是重写Model类的头文件,其中有一些不相干的代码我选择了隐藏,重写Model最重要的就是须要咱们本身去存储数据,而且在Qt的调用机制调用获取数据时给他返回便可。
关键点
本身存储数据有一个好处,那就是咱们在给Model设置数据时,最大的性能损耗就是数据拷贝的过程,仔细想一想这个是否是都不是问题。
上述代码中的TradeOrderInfoList这个接口提就是咱们本身定义的一个容器接口,方便存储咱们的表格数据,当视图绘制时,会从这里拿数据。
数据已经准备完毕,接下里就是View如何优雅的拿到数据并绘制了,这里咱们重点讲述怎么拿数据,如何绘制是QStyledItemDelegate这个类的事,感兴趣的能够本身研究研究。
仔细查看Model的版主文档们就会发现有一个data接口函数,他的声明可能像下面这样
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
们的任务就是重写这个接口,返回指定索引上的指定类型数据
QVariant QRowModel::data(const QModelIndex &index, int role) const { if (Qt::DecorationRole == role) { int r = index.row(); int c = index.column(); if (r >= itemList.size()) { return ""; } const TradeOrderInfo & info = itemList.at(r); switch (c) { case 0: return QPixmap(stock_helper::getCurrencyIcon(info.market, info.symbol).c_str()); default: ""; break; } } else if (Qt::ForegroundRole == role) { int r = index.row(); int c = index.column(); if (r >= itemList.size()) { return ""; } const TradeOrderInfo & info = itemList.at(r); switch (c) { case 4://"方向" if (info.action.compare("SELL") == 0) { return QColor("#218DF2"); } else { return QColor("#FF4A4A"); } default: return QColor("#dddddd"); } } else if (Qt::DisplayRole == role) { //本身从model中拿数据给view //"名称" int r = index.row(); int c = index.column(); if (r >= itemList.size()) { return ""; } const TradeOrderInfo & info = itemList.at(r); switch (c) { case 0://"名称" return stock_helper::OrderDisplayName(&info); case 1://"代码" return stock_helper::OrderDisplaySymbol(&info, m_strAccount); case 2://"成交量" 数字居右 return QString::number(info.totalQuantity); case 3://"成交均价" 数字居右 return stock_helper::PriceDisplayName(info.symbol, info.market, info.secType, info.avgFillPrice); case 4://"方向" if (info.action.compare("SELL") == 0) { return QUI_LOAD_STRING(TTS_ORDER_DIR_SELL); } else { return QUI_LOAD_STRING(TTS_ORDER_DIR_BUY); } default: ""; break; } } return QStandardItemModel::data(index, role); }
别忘啦,当数据源发生变化的时候使用SetSourceData接口更新下。
数据源重写好之后,再试试咱们的性能是否是杠杠滴。
本篇文章应该是实现表格功能的最后一篇文章了,能够知足大多数的产品需求。
后续可能还会陆续出一些更友好的交互优化,敬请期待。
下面是一个表格,包含了传统的表格数据源和重写后的表格数据源优劣比较。
值得一看的优秀文章:
![]() |
![]() |
很重要--转载声明