QRowTable表格控制元件(三)-效率優化之-合理使用QStandardItem

朝十晚八發表於2019-07-21

原文連結: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資料,應該算是比較多,可以滿足大多數的應用場景。

腹黑版



QRowTable表格控制元件(三)-效率優化之-合理使用QStandardItem


四、QStandardItem

1、QStandardItem是什麼鬼

Qt的幫助文件是一個好東西,開啟assisant.exe,搜尋QStandardItem類,可以搜尋如下提示資訊。



QRowTable表格控制元件(三)-效率優化之-合理使用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瞭解透徹,原始碼是唯一的途徑。其他什麼各種搜尋引擎都弱爆了。

QRowTable表格控制元件(三)-效率優化之-合理使用QStandardItem

如上圖所示,跟蹤Qt的原始碼發現,當我們通過Model設定資料項時,Qt內部也是為我們構造了一個QStandardItem物件,然後把資料放到這個物件上的。

說明QStandardItem物件的構造並不是效能所在,效能問題還需要進一步分析。

很重要:Qt的Model中把資料又單獨封裝了一層,資料儲存在QStandardItem物件中。本篇文章主要分析的效能瓶頸在QStandardItem物件的使用上,如果想要極致的效能體驗,還有比本篇文章更容易的方式,只是需要自己寫的程式碼就會變得更多,如果有需要的話可自行搜尋自定義Model,然後自己對資料進行管理,這樣就少了QStandardItem物件的構造和很多資料型別的轉換

由於博主使用的場景,表格資料不會超過100行,因此輕量級的處理已經可以滿足需求,沒有進一步去重寫Model資料來源管理。

百度不到的話,歡迎評論區留言,後續博主有時間進一步優化。

b、setData有問題?

先丟擲答案,問題確實處在setData上

如下兩種圖是QStandardItem在設定資料孩子資料時很重要的一個呼叫,把引數中的item設定為當前節點的孩子節點。

仔細看圖中紅色框圈起來的內容,有一個emitChanged變數控制了3個訊號的觸發。

QRowTable表格控制元件(三)-效率優化之-合理使用QStandardItem
QRowTable表格控制元件(三)-效率優化之-合理使用QStandardItem

問題就出現在這個emitChanged變數上,他的意思就是說當前item是否發現了變化。

仔細回想我們優化前的程式碼,QStandardItem物件是不是我們自己構造的,然後設定給了Model,這是不是搬起石頭砸自己的腳。

仔細一分析:好像是這麼回事,優化前的程式碼在行數較少時不會有明細問題,可是當資料量很大時,其實這是有問題的。

c、效能根源

既然知道是多傳送了3個訊號導致了效能問題,那麼接下來就是分析這3個訊號都幹了什麼。

下面按觸發順序來分別解釋每一個訊號

1、layoutAboutToBeChanged

如下圖是幫助文件描述

意譯:該訊號的觸發在model的佈局即將發生變化時觸發。model還有佈局,這是什麼鬼,其實就是說model中的item發現了變化。

這個訊號其實還提供了引數,可以方便我們對某一些節點進行重新整理,當我們指定了父節點和重新整理策略時生效。

QStandardItem的setData這裡沒有指定引數,表示全量重新整理,使用時需要非常注意。

QRowTable表格控制元件(三)-效率優化之-合理使用QStandardItem

2、layoutChanged

如下圖是幫助文件描述

意譯:該訊號的觸發在model的佈局發生變化之後,也就說需要全量重新整理model時,可以通過觸發該訊號達到目的。

比如重新排序、資料來源傳送變化等。

這個訊號不建議大家主動呼叫,資料量大時會導致效能問題

如果非要呼叫,也應該到訊號的引數帶上這樣就是區域性重新整理

博主之前做過一個控制元件,是優化QTreeview控制元件相關的,意思是說想讓QTreeView的行高可以自定義。

做過這塊內容的同學可能都知道,Model在通過data函式獲取資料時有一個欄位role,這個欄位表示了他想獲取什麼樣的資料,解決辦法也就在這裡了,當role等於Qt::SizeHintRole時,表示我們想要獲取的行高,我們通過這裡設定一個合適的行高即可。

Qt為了優化效能,不會每一次都計算樹控制元件的行高,這裡做了一個優化,只有第一次也就說Model發現變化時才從QStandardItem中獲取行高,然後所有的額行高資訊都儲存在了檢視的ViewItem快取中,這直接導致了我們在介面上拖拽垂直表頭行高,內容行高不會發生變化。

這裡就需要用到layoutChanged訊號,當我們給QStandardItem重新設定了行高之後,需要啟用Model佈局發生變化事件。

QRowTable表格控制元件(三)-效率優化之-合理使用QStandardItem

3、itemChanged

如下圖所示,看名字就知道itemChanged這個喜好是幹嘛使得。

QRowTable表格控制元件(三)-效率優化之-合理使用QStandardItem

==分析了以上3個函式,大家心裡是不是對QStandardItem有了一個全新的認識。==


==既然自己構造item這麼坑,博主建議大家乾脆就不要使用new QStandard這句程式碼了。==

凡事總有例外,既然Qt把這個類匯出給我們使用了,總是有他的道理,對於一些特殊場景可能需要自定義item,這時候Qt建議我們是這麼做的。

QRowTable表格控制元件(三)-效率優化之-合理使用QStandardItem

如上圖,我們需要重寫幾個函式,這裡大家知道就行。大家記住,一般情況下都不需要這麼幹。


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表格控制元件(二)-紅漲綠跌





如果您覺得文章不錯,不妨給個打賞,寫作不易,感謝各位的支援。您的支援是我最大的動力,謝謝!!!








QRowTable表格控制元件(三)-效率優化之-合理使用QStandardItem QRowTable表格控制元件(三)-效率優化之-合理使用QStandardItem






很重要--轉載宣告

  1. 本站文章無特別說明,皆為原創,版權所有,轉載時請用連結的方式,給出原文出處。同時寫上原作者:朝十晚八 or Twowords

  2. 如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時通過修改本文達到有利於轉載者的目的。


相關文章