原文連接: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接口更新下。
數據源重寫好之後,再試試咱們的性能是否是槓槓滴。
本篇文章應該是實現表格功能的最後一篇文章了,能夠知足大多數的產品需求。
後續可能還會陸續出一些更友好的交互優化,敬請期待。
下面是一個表格,包含了傳統的表格數據源和重寫後的表格數據源優劣比較。
值得一看的優秀文章:
![]() |
![]() |
很重要--轉載聲明