1. 程式人生 > 實用技巧 >60.QT-QabstractTableModel模型、重寫sort方法排序

60.QT-QabstractTableModel模型、重寫sort方法排序

在之前25.QT-模型檢視章節中,沒有具體描述如何重寫model模型,所以本章以QabstractTableModel為例,來談談model如何實現.

1.QabstractTableModel常用功能

QAbstractTableModel子類化時,必須覆寫:

Int rowCount();
//返回顯示的行數

int columnCount();
//返回顯示的列數

Qvariant headerData(int section, Qt::Orientation orientation, int role);
//返回標題role角色對應的值
// section:段號,從0開始,對於Qt::Horizontal水平標題,則是每列的標題名,
對於Qt::Vertical垂直標題,則是每行的左側標題名
//orientation:標題型別
//role:對應值是Qt:: ItemDataRole列舉, 對於role角色,常用的有: //Qt::DisplayRole :以文字方式顯示資料(QString) //Qt::DecorationRole :將資料作為圖示來裝飾(QIcon,QPixmap) //Qt::EditRole :可編輯的資料資訊顯示(QString) //Qt::ToolTipRole :作為工具提示顯示(QString) //Qt::StatusTipRole :作為狀態列中顯示的資料(QString) //Qt::WhatsThisRole :作為幫助資訊欄中顯示的資料(QString)
//Qt::FontRole :設定字型(QFont) //Qt::TextAlignmentRole :設定模型資料的文字對齊(Qt::AlignmentFlag) //Qt::BackgroundRole :設定模型資料的背景色(QBrush) //Qt::ForegroundRole : 設定模型資料的前景色,比如字型(QBrush) //Qt::SizeHintRole : 設定模型資料的大小
QVariant data(const QModelIndex &index, int role);
//返回index單元格下的role角色資料。通過index可以獲取行號和列號

bool setData(const
QModelIndex &index, const QVariant &value, int role); //將index單元格下的role角色設定為value //對於可編輯模型,必須重寫該函式,然後還需要重寫flags() //返回值為true:表示設定成功,然後還需要顯式發射dataChanged訊號

2.QabstractTableModel可編輯功能

如果不想實現QabstractTableModel可編輯功能, 則呼叫QTableView->setEditTriggers(QAbstractItemView::NoEditTriggers)即可.

如果要實現的話,則需要覆寫下面函式:

Qt::ItemFlags  flags(const QModelIndex &index);
//設定每個單元格的flag,對於可編輯模型,必須重寫它,新增Qt::ItemIsEditable(可編輯屬性)
//然後當我們雙擊時,會預設建立一個編輯元件(這是由 delegate 完成的)然後delegate會呼叫QAbstractTableModel ::data(index, Qt::EditRole)讀取預設編輯值
//當我們編輯完成後, delegate會呼叫QAbstractTableModel :: setData (index, value, Qt::EditRole)告訴我們是否儲存資料.

如果對於可調整行列的模型,可以重寫insertRows()、removeRows()、insertColumns()、removeColumns().在實現這些函式時,還需要呼叫合適的父類函式,用來通知model調整了哪些內容:

insertRows():
//在向資料結構插入新行之前需要呼叫父類的beginInsertRows(),並且必須在之後立即呼叫endInsertRows()。

insertColumns():
//在向資料結構插入新列之前需要呼叫父類的beginInsertColumns(),並且必須在之後立即呼叫endInsertColumns()。

RemoveRows():
//在刪除行之前需要呼叫父類的beginRemoveRows(),並且必須在之後立即呼叫endRemoveRows()。

RemoveColumns():
//在刪除列之前需要呼叫父類的beginRemoveColumns(),並且必須在之後立即呼叫endRemoveColumns()。

注意:如果要重新重新整理model資料,則必須在重新整理model之前呼叫beginResetModel(),然後重新整理之後呼叫endResetModel。

或者在重新整理之後,emit dataChanged(index(0,0),index(rowCount,columnCount))來進行重新整理檢視

3.model排序之重寫sort方法

首先需要呼叫QtableView->setSortingEnabled(true)使能排序,sort函式宣告如下所示:

void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
//當用戶點選標題進行降序/升序排序時,會呼叫該方法
//或者呼叫QtableView->sortByColumn()時,也會呼叫該方法
// column:第幾列進行排序
// order:升序(AscendingOrder)、降序(DescendingOrder)

排序方法則使用std::sort()來實現.然後寫個sort類來配合column和order實現排序.

進行排序的時候,必須得呼叫beginResetModel(),endResetModel()進行介面重新整理.

4.程式碼實現

介面如下所示:

custommodel.h如下所示:

#ifndef CUSTOMMODEL_H
#define CUSTOMMODEL_H

#include <QObject>
#include <QAbstractTableModel>
#include <QModelIndex>
#include <QFont>
#include <QPixmap>

//排序類
class DataSort
{
public:

    int  mColumn;
    Qt::SortOrder   mSortOrder;
    DataSort(int column, Qt::SortOrder order)
        : mColumn(column)
        , mSortOrder(order)
    {}
    bool operator()(const QVector<QString>* v1, const QVector<QString>*  v2)
    {
        int compare = 0;        //>0:大於 <0:小於
        bool ret=false;
        switch ( mColumn )
        {
            case 0 :     //序號,需要判斷數字
            case 3 :     //訊號ID,需要判斷數字
                compare = v1->at(mColumn).toInt() -  v2->at(mColumn).toInt();
                break;
            default :    //其它,只判斷字串
                compare = v1->at(mColumn).compare(v2->at(mColumn));
                break;
        }

        if(compare==0)      //相等必須返回flase,否則的話,對於一列相同的值進行降序,那麼會一直返回true,從而死迴圈
        {
            return false;
        }
        else
            ret = compare>0?false:true;

        if ( mSortOrder == Qt::DescendingOrder )    //降序
        {
            ret =  !ret;
        }
        return ret;
    }
};

class CustomModel : public QAbstractTableModel
{
    Q_OBJECT


public:
    explicit CustomModel(QAbstractTableModel *parent = nullptr);

    int rowCount(const QModelIndex &parent = QModelIndex()) const;
    int columnCount(const QModelIndex &parent = QModelIndex()) const;
    QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
    bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
    Qt::ItemFlags flags(const QModelIndex &index) const;

    void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);

public slots:
    void UpdateData(void);

private:
    QList<QVector<QString> * > m_data;
    int m_columnCount;
    int m_rowCount;
    QFont m_Font;
    QPixmap m_icon;

signals:

};

#endif // CUSTOMMODEL_H

custommodel.cpp如下所示:

#include "custommodel.h"
#include <QDateTime>
#include <QDebug>
CustomModel::CustomModel(QAbstractTableModel *parent) : QAbstractTableModel(parent)
{
    m_columnCount = 5;     //5行
    m_rowCount = 60;

    m_Font.setFamily("Microsoft Yahei");
    m_Font.setPixelSize(17);

    m_icon.load(":alarm");
    m_icon = m_icon.scaled(20,22,Qt::KeepAspectRatio,Qt::SmoothTransformation);

    UpdateData();
}


void CustomModel::UpdateData(void)
{
    beginResetModel();
    m_data.clear();

    for (int i = 0; i < m_rowCount; i++) {

        QVector<QString>* line = new QVector<QString>(m_columnCount);
        line->replace(0,QString("%1").arg(i+1));   //序號
        line->replace(1,"顯示器");                   //裝置
        line->replace(2,"未顯示");                   //狀態
        line->replace(3,QString("%1").arg(qrand()%100));
        line->replace(4,QDateTime::currentDateTime().addDays(-10).toString("hh:mm:ss"));
        m_data.append(line);
    }
    endResetModel();
   //emit dataChanged(index(0,0),index(m_data.count()-1,columnCount()-1));   //和beginResetModel()、endResetModel() 本質一樣
}


int CustomModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)        //由於parent未使用,所以通過Q_UNUSED去掉編譯警告
    return m_data.count();
}

int CustomModel::columnCount(const QModelIndex &parent) const      //
{
    Q_UNUSED(parent)
    return m_columnCount;
}

QVariant CustomModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole)
        return QVariant();

    if (orientation == Qt::Horizontal) {
        switch (section) {
            case 0 :
                return "序號";
            case 1 :
                return "裝置";
            case 2 :
                return "狀態";
            case 3 :
                return "訊號ID";
            case 4 :
                return "上報時間";
            default :
                return "";
        }
    } else {
        return QString("%1").arg(section + 1);
    }
}

QVariant CustomModel::data(const QModelIndex &index, int role) const
{
    if (role == Qt::DisplayRole) {              //顯示內容
        return m_data[index.row()]->at(index.column());
    } else if (role == Qt::EditRole) {          //正在啟動編輯,返回當前預設編輯值
        return m_data[index.row()]->at(index.column());
    } else if (role == Qt::TextAlignmentRole) {   //內容排版
        return Qt::AlignCenter;
    } else if (role == Qt::FontRole) {           //字型
        return m_Font;
    } else if (role == Qt::DecorationRole) {       //圖示
        if (index.column() == 2)
            return m_icon;
    }

    return QVariant();
}

bool CustomModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (index.isValid() && role == Qt::EditRole) {   //編輯完成,儲存資料到model,並返回true
        m_data[index.row()]->replace(index.column(), value.toString());
        emit dataChanged(index, index);             //重新實現setData()函式時,必須顯式發出該訊號。
        return true;
    }
    return false;
}

Qt::ItemFlags CustomModel::flags(const QModelIndex &index) const
{
    return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;       //設定item可編輯
}


void CustomModel::sort(int column, Qt::SortOrder order)
{
    beginResetModel();
    DataSort comp(column,order);
    std::sort(m_data.begin(), m_data.end(),comp);
    endResetModel();
}

第二種排序方法則是通過使用QsortFilterProxyModel代理類實現排序,QsortFilterProxyModel類用來為model和view之間提供強大的排序和過濾支援,並且無需對模型中的資料進行任何轉換,也無需對模型在中資料進行修改。

未完待續.