目录
一, 开心一刻
二, 问题分析
三, 重写数据源
1, 自己存储数据
2, 重写 data 接口
四, 比较
一, 开心一刻
一程序员第一次上女朋友家她妈板着脸问 : 你想娶我女儿, 有多少存款?
程序员低了下头: 五百!
她妈更鄙视了: 才五百块, 买个厕所都不够!
程序员忙说: 不是人民币!
她妈: 就算是美元, 还是不够买厕所!
程序员: 其实是比特币!
她妈: 哇, 贤婿, 我给你买只大龙虾去
二, 问题分析
前边已经写了 3 篇关于表格控件的功能, 分别是 QRowTable 表格控件 - 支持 hover 整行, checked 整行, 指定列排序等, QRowTable 表格控件 (二)- 红涨绿跌和 QRowTable 表格控件 (三)- 效率优化之 - 合理使用 QStandardItem, 这三篇文章主要是围绕实现核心功能来讲述的一般实现方式, 当数据量多大时就会出现性能问题.
既然出现问题, 当然是需要解决的. 本篇文章就来讲述怎么处理大量数据的情况.
首先我们先来分析下上述几种实现方式为什么会比较消耗时间, 首先代码量也不大, 在代码里随机打几个断点, 我们就会发现, 代码在循环构造 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, 自己存储数据
重写 data 接口, 返回数据
1, 自己存储数据
自己存储数据有一个好处, 那就是我们在给 Model 设置数据时, 最大的性能损耗就是数据拷贝的过程, 仔细想想这个是不是都不是问题.
上述代码中的 TradeOrderInfoList 这个接口提就是我们自己定义的一个容器接口, 方便存储我们的表格数据, 当视图绘制时, 会从这里拿数据.
2, 重写 data 接口
数据已经准备完毕, 接下里就是 View 如何优雅的拿到数据并绘制了, 这里我们重点讲述怎么拿数据, 如何绘制是 QStyledItemDelegate 这个类的事, 感兴趣的可以自己研究研究.
仔细查看 Model 的版主文档们就会发现有一个 data 接口函数, 他的声明可能像下面这样
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
们的任务就是重写这个接口, 返回指定索引上的指定类型数据
index: 表格 cell 的索引, 包含有行和列序号
role: 表格数据类型, 每一个 cell 上都包含有一系列键值对, 方便存储单元格上的各种数据, 比如说前景色, 背景色, 字体, 位置, 高亮色, 背景画刷等等.
- 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 接口更新下.
数据源重写好以后, 再试试我们的性能是不是杠杠滴.
四, 比较
本篇文章应该是实现表格功能的最后一篇文章了, 可以满足大多数的产品需求.
后续可能还会陆续出一些更友好的交互优化, 敬请期待.
下面是一个表格, 包含了传统的表格数据源和重写后的表格数据源优劣比较.
比较项目 | 传统方式 | 重写 Model --- | --- | --- 难易程度 | 简单 | 复杂 代码里 | 少 | 多 性能 | 差 | 好 推荐度 | 两颗星 | 五颗星
来源: https://www.cnblogs.com/swarmbees/p/11490909.html