特典——Model的排序與篩選

代碼:

demo5

model的排序?

我們在運行demo4的時候發(fā)現(xiàn),這些文件的排列是雜亂的,如果我們要讓他們變得有序該怎么辦呢?

最簡單直接的想法是,每次model改變的時候,我們對數(shù)據(jù)進行重新排序,但是這對于需要多種排序方式或者需要提供正向反向排序的視圖來說,設(shè)計這樣一個model是困難的。

model的篩選?

如果我們不想讓隱藏的文件顯示在我們的view中,該怎么辦呢?

最簡單直接的想法是,每次篩選的時候,我們需要從數(shù)據(jù)中剔除不符合條件的數(shù)據(jù),但是這種方式會對數(shù)據(jù)造成損失,對于樹狀結(jié)構(gòu),實現(xiàn)剔除也是比較麻煩的。

QSortFilterProxyModel——專為排序和篩選而生的Model

好在qt提供了這樣一個model解決上面兩個問題,它就是QSortFilterProxyModel。QSortFilterProxyModel是一種model所以它也可以結(jié)合view使用,但是與一般的model不同的是,QSortFilterProxyModel它本身不像其它model一樣和數(shù)據(jù)掛鉤,而是和model進行綁定。view和model可以通過QSortFilterProxyModel這一個中介實現(xiàn)數(shù)據(jù)無損的排序和篩選,我們下面看看它具體是如何實現(xiàn)的。

以demo4中的model為例,我們希望我們的model可以按名稱進行排序,并且文件夾在文件之前,同時我們不希望顯示隱藏文件。

雖然QSortFilterProxyModel提供了一些默認的接口用于排序和篩選,但是考慮到我們的model并不那么規(guī)范,數(shù)據(jù)也不是特別統(tǒng)一,我們采用繼承QSortFilterProxyModel并且實現(xiàn)自定義篩選和排序的形式,自定義QSortFilterProxyModel的篩選和排序,首先要override這兩個方法:

bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override;
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;

注意:這兩個方法中的index屬于source model,我們在proxy model中使用source model的index()方法時,一定要小心,因為這個時候可能index可能無法正常創(chuàng)建,這也是和開發(fā)這的代碼流程相關(guān)的,我們盡量使用parent的internalPointer(并結(jié)合sourceRow)進行數(shù)據(jù)的檢索,這樣調(diào)試起來也會方便一些。

我們看看它的具體實現(xiàn):

bool SortFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
??? //qDebug()<<sourceParent;
??? //return QSortFilterProxyModel::filterAcceptsRow(sourceRow, sourceParent);

??? FileNode *node = static_cast<FileNode*>(sourceParent.internalPointer());
??? if (node) {
??????? QString displayName = node->m_children.at(sourceRow)->m_info->displayName;
??????? return !displayName.startsWith(".");
??? } else {
??????? SimpleGVfsTreeModel *model = static_cast<SimpleGVfsTreeModel*>(sourceModel());
??????? QString displayName = model->m_root_node->m_children.at(sourceRow)->m_info->displayName;
??????? return !displayName.startsWith(".");
??? }
}

bool SortFilterProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
{
??? qDebug()<<"lessThan"<<source_left.column();
??? FileNode *left = static_cast<FileNode*>(source_left.internalPointer());
??? FileNode *right = static_cast<FileNode*>(source_right.internalPointer());
??? switch (sortColumn()) {
??? case 0: {
??????? if (!left->hasChildren() && !right->hasChildren()) {
??????????? return left->m_info->displayName < right->m_info->displayName;
??????? }
??????? if (left->hasChildren() && right->hasChildren()) {
??????????? return left->m_info->displayName < right->m_info->displayName;
??????? }
??????? bool isAscendingOrder = sortOrder()==Qt::AscendingOrder;
??????? if (left->hasChildren()) {
??????????? return isAscendingOrder?true:false;
??????? }
??????? if (right->hasChildren()) {
??????????? return isAscendingOrder?false:true;
??????? }
??????? return left->m_info->displayName < right->m_info->displayName;
??? }
??? default:
??????? return QSortFilterProxyModel::lessThan(source_left, source_right);
??? }
}

我目前只針對第一列進行了排序,大家可以自行完善之后幾列的排序。

另外,由于view通過proxy model展示數(shù)據(jù),我們從view中獲取的index是proxy model 的index,如果需要獲取對應(yīng)source model的index,我們需要使用proxy model的mapToSource()方法。大家也可以嘗試仿照demo2對demo5進行優(yōu)化。

使用QSortFilterProxyModel需要注意Model設(shè)計的規(guī)范性

我們在demo3中做這樣一個實驗——將insertRow的row永遠設(shè)置為0,你會發(fā)現(xiàn)view的結(jié)果任然沒有變化。把row永遠設(shè)為0顯然是不合理的,但是我們?nèi)匀坏玫搅苏_的結(jié)果,這是因為在model中,我們的index位置實際上已經(jīng)由我們的數(shù)據(jù)結(jié)構(gòu)定死了,insertRow就變成了一個觸發(fā)model更新和view重繪的方法,row這個參數(shù)自然就失去了它的意義。

然而在proxy model中,如果我們不按照邏輯的進行insert/remove,則會出現(xiàn)問題,這是因為proxy model在響應(yīng)model index的insert/remove時,是按照正常的insert/remove邏輯進行的,如果我們的邏輯出現(xiàn)問題,展示出來的結(jié)果自然也會有問題。

如果想要使用proxy model,則必須規(guī)范model修改的邏輯,當(dāng)然你也可以不使用proxy model,自己處理model的sorting,但是這對于多類型的sorting是很復(fù)雜的,對于開發(fā)者的考驗很大,而且如果你想要篩選數(shù)據(jù),則你也必須在model中刪除不符合條件的data,這對于想要重新進行篩選的model來說是比較困難的,你要么從頭開始,要么把原來的數(shù)據(jù)備份一份,再改變的時候再做一次映射,不管怎么樣都沒有使用proxy model來的方便。

效率的取舍

QSortFilterProxyModel帶來方便的同時,實際上也會帶來效率上的損失,拿上面的demo舉例,我們每次改變model,proxy model會進行大量的重排和篩選操作。

為了盡可能的減少上面的問題,我們可以在proxy model進行排序或者篩選的同時對model的數(shù)據(jù)進行同樣的邏輯操作,然而這樣對于需要快速改變排序方式的view來說也是一個很大的負擔(dān)。

不管怎么樣,使用QSortFilterProxyModel帶來的效率上的問題是不可避免的,我們需要做出取舍,尤其是在model數(shù)據(jù)過于龐大和復(fù)雜的時,一點點效率上的問題都會被無限放大,我們圖方便的做法可能就會導(dǎo)致用戶體驗受到影響了。

盡可能的發(fā)掘QSortFilterModel的潛力

當(dāng)然在大部分的情況下我更偏向于使用QSortFilterProxyModel進行model的排序篩選與數(shù)據(jù)展示。QSortFilterProxyModel的潛力遠不止我代碼中所寫的這些,我們甚至可以使用它進行我們平時所認為的“搜索功能”,比如按文件名或者日期搜索等等,它其實上是通過篩選得到的結(jié)果。然而,我們應(yīng)該能夠發(fā)現(xiàn),篩選受到了數(shù)據(jù)集,也就是sourceModel的限制,想要打破這一限制,我們需要獲取更加完善的數(shù)據(jù)集,而不是僅僅只局限于一個目錄之下。如果有時間,我會另外寫一篇關(guān)于如何實現(xiàn)文件搜索的文章,告訴大家如何綜合運用QSortFilterProxyModel和搜索框架。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹 對...
    cosWriter閱讀 11,653評論 1 32
  • feisky云計算、虛擬化與Linux技術(shù)筆記posts - 1014, comments - 298, trac...
    不排版閱讀 4,346評論 0 5
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 4,011評論 0 11
  • 今天又是星期一,這個星期上夜班,有時間接送老大,早上老大6點40起床,吃完飯7點多點就送去學(xué)校,如果要坐校車?yán)洗笮?..
    仲昊惟閱讀 227評論 1 0
  • 不是不配擁有愛情,是需要改變這種直來直去的交流方式,多點細心,多點換位思考,多點點委婉的表達。 有些男生和女生聊天...
    思遠236閱讀 2,413評論 0 1

友情鏈接更多精彩內(nèi)容