標籤:
如何使QTreeView快速顯示1000萬條資料,並且記憶體佔用量少呢?這個問題困擾我很久,在網上找了好多相關資料,都沒有找到合理的解決方案,今天在這裡把我的解決方案提供給朋友們,供大家相互學習。
我開始使用的QTreeWidget 控制項來顯示我的資料,發現該控制項在顯示10000行以下的資料還可以應付的過來,但超過10000條,就明顯感覺到螢幕重新整理就會有卡的現象,而且佔據記憶體很大,雖然操作起來簡單方便,但靈活性沒有QTreeView強大。因為我要顯示的資料量是非常大的,甚至過1000萬,因此,採用QTreeWidget來顯示,很顯然不能滿足效能要求,所以打算採用QTreeView來顯示,不羅嗦了,趕快進入正題了,下面講講我是怎麼通過QTreeView來快速顯示1000萬條資料的吧!
1.通過從檔案裡面讀取要顯示的資料,交給QTreeView來顯示,從而不用把資料一次性讀到記憶體,就可以解決記憶體佔用大的問題。 2.我們知道資料顯示是通過重新整理來實現的,通過重新整理時,每次只重新整理螢幕可見地區,其他部分不用重新整理的方法,從而解決速度顯示慢的問題。
解決了上面的兩個問題,顯示1億條資料都不會有任何問題,而且顯示速度和記憶體佔有量與顯示1000條資料相當,聽起來很誘人啊,下面是我是怎麼通過QTreeView來解決這兩個問題的:
1.重載QAbstractItemModel中的如下函數: QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QVariant data(const QModelIndex &index, int role) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; QModelIndex parent(const QModelIndex &index) const;
(1)QVariant headerData(intsection, Qt::Orientationorientation, int role =Qt::DisplayRole)const;顯示樹視的標題,section表示列,從0開始,orientation表示標題的方向(水平還是垂直),role表式標題列顯示的方式,當role的角色為Qt::DisplayRole時,表示顯示文本,當然還有其他角色,大家可以參考Qt開發手冊。範例程式碼: QVariant CMyModel::headerData(int section, Qt::Orientation orientation, int role) const { if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { return pData->headerData(section); } return QVariant(); } 顯示水平標題,其中pData是我定義的一個資料類的對象,在這裡我把該類命名為CData。headerData來取出要顯示的資料。
(2)QVariant data(const QModelIndex &index,int role)const;顯示資料,index表示樹節點索引,樹中的每個節點都有一個對應的該索引,當index = QModelIndex()時,表示該節點是根節點,否則為非根節點。index中存放了該節點在同存節點中的位置資訊(行和列),以及節點的特殊資訊,如index.internalPointer(),這是一個指標,我們可以通過該指標儲存我們想要的節點資訊,角色同上。範例程式碼: QVariant CMyModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); int row = index.row(); int col = index.column(); if(role == Qt::DisplayRole) { return pData->getData(row, col); } return QVariant(); } 通過getData來擷取要顯示的資料,該資料方在我們的檔案裡,每次只需要讀取我們想要顯示的資料,不需要把所有的資料都放到記憶體,從而節省了記憶體空間,這樣就解決了上面講的第一個問題。
(3)int rowCount(const QModelIndex &parent = QModelIndex())const;大家不難猜出該函數是返回該父節點下有多少個子節點,還是來看範例程式碼吧: int CMyModel::rowCount(const QModelIndex &parent)const { return pData->rowCount(); } 為了簡單起見,這裡不考慮父節點,認為任何節點都存在rowCount()個節點。
(4)int columnCount(const QModelIndex &parent = QModelIndex())const;返回父節點有多少列,範例程式碼: int CMyModel::columnCount(const QModelIndex &parent)const { return pData->colCount(); }
(5)接下來看QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const;的實現: QModelIndex CMChModel::index(int row, int column, const QModelIndex &parent) const { return createIndex(row, column, NULL); }
通過該函數建立節點的索引,我這裡簡單一點,只建立一層節點,若大家要建立多層節點,則要通parent索引裡的資料(如internalPointer())來建立對應的節點,通過createIndex的第3個參數來向節點傳入指定的索引資料,由於我這裡不需要建立多層節點,所以我傳入NULL。那麽我們怎麼建立節點的父子關係呢,聰明的你肯定想到了吧,QModelIndex parent(const QModelIndex &index) const;就是建立父子節點的父子關係的,如果只有一層節點,該函數無須重載,至此我們的顯示資料模型已經實現了,接下來就是要把這個模型通過視圖來表現出來(QTreeView)。
我們看看QTreeView是怎麼來顯示我們上面建立的模型的,看一段簡單的代碼,大家就明白了: QTreeView treeView; CMyModel *pModel = new CMyModel(); treeView.setModel(pModel); treeView.show();
2.是不是很簡單,到這裡通過QTreeView來處理大量的資料的實現方式差不多講完了,當你按照上面的方式準備處理你的大批量資料時,你就會發現顯示10000行資料還是會卡,這是什麼原因呢?其實這個也困擾我很久,網上也沒有找到相關資料,沒辦法啊,只有啃QTreeView實現的原始碼,實際上QTreeView確實是顯示可見部分資料(每次顯示1000行資料,按理論上來說1000行足以佔據電腦螢幕,這樣一來不管你資料量是多大,我始終只取1000行資料,所以1億條資料與1000條資料顯示速度是一樣的,只要你一次唯讀出要顯示的資料,記憶體佔有量也是一樣的),既然問題都解決了,那麽為什麼還會有卡的現象呢?
經過進一步研究QTreeView原始碼,最後發現耗時的地方是QTreeView第一次重新整理的時候會計算每一行的行高,這樣重新整理時,就要遍曆所有行資料,原來卡是出在這個地方啊,那麽應該怎麼解決呢?實現方法很簡單,就是上面的代碼多加一行,treeView.setUniformRowHeights(true);這樣一來就不會重新整理計算所有行的行高了,至此用QTreeView顯示大批量資料問題得到解決。
最後再補充一下,該實現方式可以適應其他QTableView, QListView等視圖。
http://blog.csdn.net/rabinsong/article/details/8452946
QTreeView處理大量資料(使用1000萬條資料,每次都只是部分重新整理)