利用C++11的function和bind功能,實現QStandardItemModel的通用遍歷函數

在使用Qt的樹形視圖和表格視圖QTableView和QTreeView時,經常需要遍歷所有條目,每種功能都寫一個遍歷函數既麻煩又不符合編程最簡原則,因此,寫一個通用的遍歷函數是很必要的(類似於std::for_each),對於遍歷時實現的功能不一樣,可以通過函數指針來代替需要實現的功能。

在講這個之前,需要了解std::function和std::bind,具體可以自行谷歌。

由於這個遍歷函數可能經常用到,因此可以寫一個類的靜態函數或者是全局函數。

下面把這個功能封裝在一個類裏

類的頭文件如下:
#include <QStandardItemModel>
#include <functional>
class QStandardItemModelEx
 {
 public:
     QStandardItemModelEx(){}
     ~QStandardItemModelEx(){}
     ///
     /// \brief callback_ergodicFun_ptr 回調函數指針,bool f(QStandardItem*),bool用於決定是否繼續,如果爲true就繼續遞歸,如果爲false就停止遞歸
     ///
     typedef std::function<bool(QStandardItem*)> callback_ergodicFun_ptr;

     //typedef void(*callback_ergodicFun_ptr)(QStandardItem*);
     static void ergodicAllItem(QStandardItemModel* model,callback_ergodicFun_ptr pFun);
     static bool ergodicItem(QStandardItem* item,callback_ergodicFun_ptr pFun);
 private:
 };

這裏使用C++11的新特性function(環境 VS2010),定義一個函數指針用於回調實現功能。
函數ergodicAllItem用於遍歷所有項目,ergodicItem用於遍歷項目下的所有子項目。回調函數返回bool用於決定是否繼續,如果爲true就繼續遍歷,如果爲false就停止遍歷
爲了簡單起見,下面用遞歸實現遍歷(ergodicItem是尾遞歸函數
 
void QStandardItemModelEx::ergodicAllItem(QStandardItemModel* model,callback_ergodicFun_ptr pFun)
{
    int rows = model->rowCount();
    int column = model->columnCount();
    for (int i=0;i<rows;++i)
    {
        for(int j=0;j<column;++j)
        {
            QStandardItem* item = model->item(i,j);
            if (item)
            {
                if(!ergodicItem(item,pFun))
                    return;
            }
        }
    }
}

bool QStandardItemModelEx::ergodicItem(QStandardItem* item,callback_ergodicFun_ptr pFun)
{
    int rows = item->rowCount();
    int column = item->columnCount();
    for (int i=0;i<rows;++i)
    {
        for(int j=0;j<column;++j)
        {
            QStandardItem* childItem = item->child(i,j);
            if (childItem)
            {
                if(!ergodicItem(childItem,pFun))
                    return false;
            }
        }
    }
    return pFun(item);
}




用這兩個函數就可以遍歷所有的項目了,而遍歷時需要的動作,就通過回調函數實現。

回調函數即可寫在類裏,也可以寫爲全局函數,若除了QStandardItem*還需要別的參數,那麼可以用std::bind幫忙。

下面舉一個例子
例子功能是通過關鍵字高亮條目。在使用QTreeView或QTableView時,在條目多的情況下,需要搜索某些條目,對搜索結果進行高亮,這時,就需要遍歷整個View裏的條目,對符合的條目進行高亮
遍歷的函數已經寫好,現在缺少的是回調的函數,由於遍歷的函數使用的函數指針是std::function,因此回調函數即可寫爲全局函數也可以寫爲類成員函數
回調函數聲明:
bool callback_hightLightItem(QStandardItem* item,const QStringList keys);
回調函數實現:
bool callback_hightLightItem(QStandardItem* item,const QStringList keys)
{   
    QString str = item->text();
    if (is_match_string(str,keys))
    {
        item->setData(QVariant(QColor(237,100,100,180)),Qt::BackgroundRole);
    }
    else
    {
        item->setData(QVariant(),Qt::BackgroundRole);
    }
    return true;
}

使用QStandardItem的setData函數,設置角色爲Qt::BackgroundRole,即可設置背景顏色。
實現函數中is_match_string函數是用來檢測是否符合關鍵字,如果符合關鍵字,就返回true,符合關鍵字就給條目背景賦予不同的顏色,否則就把顏色消除。
回調函數返回true,意味着一直遍歷,直到所有遍歷完成。

通過按鈕pushButton_search來觸發搜索功能。pushButton_search函數的槽如下:
void MainWindow::on_pushButton_search_clicked()
{
    QString str = ui->lineEdit_search->text();//獲取關鍵字
    if (str.isEmpty())
        return;
    QStringList keyWords = str.split(QString(" "));
    StandardItemModel::ergodicAllItem(
        qobject_cast<QStandardItemModel*>(ui->treeView->model())
        ,std::bind(callback_hightLightItem,std::placeholders::_1,keyWords));//對於callback_hightLightItem是全局函數的情況下
}

由於回調函數是兩個參數QStandardItem* item,const QStringList keys,而函數指針只是一個參數,因此需要綁定一個參數,使其變爲參數只有一個的函數指針。這時就需要std::bind來實現。
佔位符_1表示這個參數是對應的函數指針的第一個參數所在位置,如當前函數是:
bool callback_hightLightItem(QStandardItem* item,const QStringList keys);
我們要把他轉換爲如下類型的函數指針
bool(QStandardItem*)
第一個參數是正好對應,所以佔位符_1就位於第一個位置,後面的參數直接傳遞進去就行。

如果回調寫成:
bool callback_hightLightItem(const QStringList keys,QStandardItem* item);
那麼寫法變爲:
std::bind(callback_hightLightItem,keyWords,std::placeholders::_1);
如果callback_hightLightItem是類成員函數,那麼其寫法如下
StandardItemModel::ergodicAllItem(
        qobject_cast<QStandardItemModel*>(ui->treeView->model())
        ,std::bind(&MainWindow::callback_hightLightItem,this,std::placeholders::_1,keyWords));

bind也能輕鬆搞定類成員函數指針,注意要把類的指針傳遞過去。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章