原文連接: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數據,應該算是比較多,能夠知足大多數的應用場景。
腹黑版
Qt的幫助文檔是一個好東西,打開assisant.exe,搜索QStandardItem類,能夠搜索以下提示信息。
什麼意思呢!
爲了閱讀起來更流暢,我這裏就行中文內容的意譯。
雖然英文解釋不少,可是意譯成中文後就很簡單了,畢竟幫助文檔要說的很清晰、場景囊括的會比較全一些
意譯:QStandardItem是一個數據結構,他能夠存儲一個cell的各類信息,好比文本、圖標、是否可選、字體、別景色、前景色等等。而且QStandardItem能夠有孩子和兄弟,他是爲model提供數據存儲的節點。
這裏我在補充一些內容,讓你們對QStandardItem右更進一步的瞭解。
QTableView:做爲表格cell時,有一個做爲根節點的QStandardItem,其餘節點都是QStandardItem節點的孩子節點,而且都是兄弟節點(這裏暫時不考慮多列的狀況)。
QTreeView:做爲樹節點cell時,有一個做爲根節點的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數據源管理。
百度不到的話,歡迎評論區留言,後續博主有時間進一步優化。
先拋出答案,問題確實處在setData上
以下兩種圖是QStandardItem在設置數據孩子數據時很重要的一個調用,把參數中的item設置爲當前節點的孩子節點。
仔細看圖中紅色框圈起來的內容,有一個emitChanged變量控制了3個信號的觸發。
問題就出如今這個emitChanged變量上,他的意思就是說當前item是否發現了變化。
仔細回想咱們優化前的代碼,QStandardItem對象是否是咱們本身構造的,而後設置給了Model,這是否是搬起石頭砸本身的腳。
仔細一分析:好像是這麼回事,優化前的代碼在行數較少時不會有明細問題,但是當數據量很大時,其實這是有問題的。
既然知道是多發送了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不須要咱們去構造,使用Model的index函數訪問cell時Qt內部會幫咱們構造,特別是對於數據量大時,Qt內部構造會有很大的效率提高。
二、Model的setItem使用上須要注意,除非一些特殊場景(好比咱們自定義item),不然儘可能不要使用。
自定義item,須要重寫不少東西;設置item時,原有item將會被刪除
三、對於須要設置cell自定義窗口用法
經過指定行列設置
setCellWidget(int row, int column, QWidget *widget)
![]() |
![]() |
很重要--轉載聲明