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(constQModelIndex &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之間提供強大的排序和過濾支援,並且無需對模型中的資料進行任何轉換,也無需對模型在中資料進行修改。
未完待續.