如何使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