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

原文連接:QRowTable表格控件(三)-效率優化之-合理使用QStandardItem瀏覽器

1、開心一刻

磚家在河邊看到兩隻烏龜縮着一動不動,問一農民:「它們在幹嘛?」。緩存

農民說:「在比賽!」。數據結構

磚家不解:「動都沒動過,比什麼賽?」。函數

農民:「在比裝死!」。佈局

磚家:「但是殼上有甲骨文的那隻,早就死了啊?」。性能

這時,一隻烏龜猛然探出頭來罵道:「MD,死了也不吭一聲!」。測試

忽然另外一隻也伸出頭來:「傻子!磚家的話你也信,你輸了 !」。字體

2、概述

最近換了一家新單位,工做的內容也發了一些變化。接觸了一些大牛,也讓我對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);
    }
}

我槽,上述兩種書寫方式有球區別,怎麼會差異如此之大,下面讓我爲你們細細道來。

3、效果展現

如下是紅漲綠跌效果圖,demo中展現了30000數據,應該算是比較多,能夠知足大多數的應用場景。

腹黑版






4、QStandardItem

一、QStandardItem是什麼鬼

Qt的幫助文檔是一個好東西,打開assisant.exe,搜索QStandardItem類,能夠搜索以下提示信息。






什麼意思呢!

爲了閱讀起來更流暢,我這裏就行中文內容的意譯。

雖然英文解釋不少,可是意譯成中文後就很簡單了,畢竟幫助文檔要說的很清晰、場景囊括的會比較全一些

意譯:QStandardItem是一個數據結構,他能夠存儲一個cell的各類信息,好比文本、圖標、是否可選、字體、別景色、前景色等等。而且QStandardItem能夠有孩子和兄弟,他是爲model提供數據存儲的節點。

這裏我在補充一些內容,讓你們對QStandardItem右更進一步的瞭解。

QTableView:做爲表格cell時,有一個做爲根節點的QStandardItem,其餘節點都是QStandardItem節點的孩子節點,而且都是兄弟節點(這裏暫時不考慮多列的狀況)。

QTreeView:做爲樹節點cell時,有一個做爲根節點的QStandardItem,其餘節點都是他的孩子節點,可是其餘節點也能夠做爲父節點存在(這裏暫時不考慮多列的狀況)。

二、性能分析

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瞭解透徹,源碼是惟一的途徑。其餘什麼各類搜索引擎都弱爆了。

如上圖所示,跟蹤Qt的源碼發現,當咱們經過Model設置數據項時,Qt內部也是爲咱們構造了一個QStandardItem對象,而後把數據放到這個對象上的。

說明QStandardItem對象的構造並非性能所在,性能問題還須要進一步分析。

很重要:Qt的Model中把數據又單獨封裝了一層,數據存儲在QStandardItem對象中。本篇文章主要分析的性能瓶頸在QStandardItem對象的使用上,若是想要極致的性能體驗,還有比本篇文章更容易的方式,只是須要本身寫的代碼就會變得更多,若是有須要的話可自行搜索自定義Model,而後本身對數據進行管理,這樣就少了QStandardItem對象的構造和不少數據類型的轉換

因爲博主使用的場景,表格數據不會超過100行,所以輕量級的處理已經能夠知足需求,沒有進一步去重寫Model數據源管理。

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

b、setData有問題?

先拋出答案,問題確實處在setData上

以下兩種圖是QStandardItem在設置數據孩子數據時很重要的一個調用,把參數中的item設置爲當前節點的孩子節點。

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

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

仔細回想咱們優化前的代碼,QStandardItem對象是否是咱們本身構造的,而後設置給了Model,這是否是搬起石頭砸本身的腳。

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

c、性能根源

既然知道是多發送了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使用上的坑

一、原則上QStandardItem不須要咱們去構造,使用Model的index函數訪問cell時Qt內部會幫咱們構造,特別是對於數據量大時,Qt內部構造會有很大的效率提高。

二、Model的setItem使用上須要注意,除非一些特殊場景(好比咱們自定義item),不然儘可能不要使用。

自定義item,須要重寫不少東西;設置item時,原有item將會被刪除

三、對於須要設置cell自定義窗口用法

經過指定行列設置

setCellWidget(int row, int column, QWidget *widget)

5、相關文章

  1. Qt實現表格控件-支持多級列表頭、多級行表頭、單元格合併、字體設置等

  2. Qt高仿Excel表格組件-支持凍結列、凍結行、內容自適應和合並單元格

  3. 屬性瀏覽器控件QtTreePropertyBrowser編譯成動態庫(設計師插件)

  4. 超級實用的屬性瀏覽器控件--QtTreePropertyBrowser

  5. Qt之表格控件螞蟻線

  6. QRowTable表格控件-支持hover整行、checked整行、指定列排序等

  7. QRowTable表格控件(二)-紅漲綠跌





若是您以爲文章不錯,不妨給個 打賞,寫做不易,感謝各位的支持。您的支持是我最大的動力,謝謝!!!














很重要--轉載聲明

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

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

相關文章
相關標籤/搜索