本文將介紹自定義Model過程中資料庫數據源的獲取方法,我使用過以下三種方式獲取資料庫數據源: 創建 存儲對應資料庫所有欄位的 結構體,將結構體置於容器中返回,然後根據索引值(QModelIndex)取出最終的欄位值; 創建 存儲對應資料庫所有欄位的 類,將類對象置於容器中返回,然後利用內省機制獲取 ...
本文將介紹自定義Model過程中資料庫數據源的獲取方法,我使用過以下三種方式獲取資料庫數據源:
- 創建 存儲對應資料庫所有欄位的 結構體,將結構體置於容器中返回,然後根據索引值(QModelIndex)取出最終的欄位值;
- 創建 存儲對應資料庫所有欄位的 類,將類對象置於容器中返回,然後利用內省機制獲取對象相應欄位(屬性)值。
- 不用自己造輪子,直接使用QVariantList類,將QVariantList 對象置於容器中,如QVector<QVariantList >,然後根據索引值(QModelIndex)取出最終的欄位值;
1 //Parameter.h 2 //加粗單詞為成員變數 3 //假設資料庫中某表有三個欄位Index,Name,Describe 4 5 class Parameter : public QObject 6 { 7 Q_OBJECT 8 public: 9 explicit Parameter( QObject *parent = 0); 10 11 Q_INVOKABLE QVariant getIndex() const { return index;} //用Q_INVOKABLE聲明後才能被元對象(QMetaObject)調用 12 Q_INVOKABLE void setIndex(const QVariant &value) { index = value.toInt(); } 13 14 Q_INVOKABLE QVariant getName() const { return name; } 15 Q_INVOKABLE void setName(const QVariant &value) { name = value.toString(); } 16 17 Q_INVOKABLE QVariant getDecribe() const { return describe; } 18 Q_INVOKABLE QVariant setDescribe(const QVariant &value) { describe = value; } 19 20 QMap<int, int> getMethodGETIndexs()const;//獲得“取值器”函數(即getXX函數) 的索引值列表,這些函數都被Q_INVOKABLE聲明過 21 QMap<int, int> getMethodSETIndexs() const;//獲得“設置器”函數(即setXX函數) 的索引值列表,這些函數都被Q_INVOKABLE聲明過 22 23 private: 24 void setMethodGETIndexs(); //設置“取值器”函數(即getXX函數) 的索引值列表,這些函數都被Q_INVOKABLE聲明過 25 void setMethodSETIndexs(); //設置“設置器”函數(即setXX函數) 的索引值列表,這些函數都被Q_INVOKABLE聲明過 26 27 static int getNewIndex(); 28 29 int index; 30 QString name; 31 QString describe; 32 33 QMap<int,int> methodGETIndexs; 34 QMap<int,int> methodSETIndexs; 35 };
1 //Parameter.cpp 2 3 Parameter::Parameter(QObject *parent) : 4 QObject(parent), 5 index(getNewIndex()), 6 name("Unnamed"), 7 describe("") 8 { 9 setMethodGETIndexs(); 10 setMethodSETIndexs(); 11 } 12 13 void Parameter::setMethodGETIndex() 14 { 15 int index1 = this->metaObject()->indexOfMethod("getIndex()"); 16 methodGETIndexs.insert(0,index1); 17 18 int index2 = this->metaObject()->indexOfMethod("getName()"); 19 methodGETIndexs.insert(1,index2); 20 21 int index3 = this->metaObject()->indexOfMethod("getDecribe()"); 22 methodGETIndexs.insert(2,index3); 23 24 } 25 26 void Parameter::setMethodSETIndexs() 27 { 28 int index1 = this->metaObject()->indexOfMethod("setIndex(QVariant)"); 29 methodSETIndexs.insert(0,index1); 30 31 int index2 = this->metaObject()->indexOfMethod("setName(QVariant)"); 32 methodSETIndexs.insert(1,index2); 33 34 int index3 = this->metaObject()->indexOfMethod("setDescribe(QVariant)"); 35 methodSETIndexs.insert(2,index3); 36 } 37 38 QMap<int, int> Parameter::getMethodSETIndexs() const 39 { 40 return methodSETIndexs; 41 } 42 43 QMap<int, int> Parameter::getMethodGETIndexs() const 44 { 45 return methodGETIndexs; 46 } 47 48 int Parameter::getNewIndex() 49 { 50 //查詢資料庫 51 //返回最新的Index欄位 52 }Parameter類聲明瞭對應資料庫表中欄位(field)的成員變數,並分別為這些成員變數編寫了setxx()函數和getxx()函數,並對這些函數進行Q_INVOKABLE聲明 然後,在setMethodGETIndexs()函數 與 setMethodSETIndexs()函數中,使用QMetaObject::indexOfMethod(...)函數獲取每個函數在QMetaObject對象中的索引值,將該按順序索引值存入到容器中,其插入順序與TableModel中的欄位順序一致。 最後,在TableModel中調用Parameter類的getMethodSETIndexs()函數與getMethodGETIndexs()函數獲得索引值列表。
1 //TableModel.h 2 class TableModel : public QAbstractTableModel 3 { 4 Q_OBJECT 5 public: 6 explicit TableModel(QObject *parent = 0); 7 int rowCount(const QModelIndex &parent = QModelIndex()) const;// 8 int columnCount(const QModelIndex &parent = QModelIndex()) const; 9 QVariant data(const QModelIndex &index, int role) const; 10 bool setData(const QModelIndex &index, const QVariant &value, int role); 11 private: 12 static QList<Parameter*> getTableParameters(); //該函數用於初始化dataParameters 13 14 QVariant specificIndexValue(const QModelIndex &index) const; 15 int getMethodGETIndex(const QModelIndex &index) const; 16 QMetaMethod getMetaMethod(const QModelIndex &index,int methodIndex) const; 17 bool setSpecificData(const QModelIndex &index, const QVariant &value); 18 int getMethodSETIndex(const QModelIndex &index); 19 20 QList<Parameter*> dataParameters; //存儲從資料庫表中查詢所得的值,每個Parameter對象代表一條記錄
//tablemodel.cpp成員變數 QList<Parameter*> dataParameters中存儲了資料庫表中的欄位值,且每個Parameter對象代表一條記錄。 我們知道,TableModel數據的顯示與rowCount()、columnCount()、data()函數息息相關,我們重載了這三個函數。 為了讓model的行和列與dataParameters一一對應: 令rowCount()函數返回dataParameters的條目數(行數目); 令columnCount()返回dataParameters中每條記錄的欄位數目(列數目)。 對於Variant data(const QModelIndex &index, int role) const函數,在選定的role下,調用specificIndexValue(const QModelIndex &index)函數,根據索引值獲得行號和列號,先根據行號確定容器中某一個Parameter對象(即某一條記錄),然後再根據列號,獲得該Parameter對象中支持 元對象調用的 函數的索引值(如getMethodGETIndex()函數所示),獲取函數索引值後,如getMetaMethod()所示,可獲得QMetaMethod對象,然後調用invoke()函數,賦予合適的參數值,就等價於調用當前函數索引值對應的那個函數。 這樣做的好處在於,可直接通過行號與列號進行定址,避免了條件判斷語句,使代碼大大提高了簡潔性與復用性。 setData()函數與data()函數類似,不再詳述。 總結:利用內省機制獲得對象的成員函數,並調用之,能夠避免複雜的條件判斷邏輯,能夠提高復用性。但是,在這裡沒有提及的有性能問題,我沒有研究對性能會有什麼影響,當然,簡單的PC軟體是基本看不到影響的,其次,利用Parameter類存儲資料庫表欄位值,使Parameter只能用於同一個表,那麼每個返回資料庫欄位值的函數也就只能服務於同一個表,這樣也會有很多重覆代碼產生。所以接下來,我將進一步改進,放棄利用自定義類而使用QVariantList類來存儲資料庫表的每一條記錄。
TableModel::TableModel(QObject *parent) : QAbstractTableModel(parent), dataParameters(getTableParameters()) { } static TableModel::QList<Parameter*> getTableParameters() { //查詢資料庫,返回欄位值列表 } int TableModel::rowCount(const QModelIndex &parent = QModelIndex()) { dataParameters.size(); } int TableModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent); return dataParameters.getMethodGETIndexs().size(); } QVariant TableModel::data(const QModelIndex &index, int role) const { if(!index.isValid()){ return QVariant(); } return specificData(index,role); } QVariant TableModel::specificData(const QModelIndex &index, int role)const { switch(role) { case Qt::TextAlignmentRole: return int(Qt::AlignHCenter | Qt::AlignVCenter); case Qt::DisplayRole: return specificIndexValue(index); case Qt::EditRole: return specificIndexValue(index); default: return QVariant(); } return QVariant(); } QVariant TableModel::specificIndexValue(const QModelIndex &index) const { QVariant retValue; int methodIndex = methodGETIndex(index); QMetaMethod getMethod = getMetaMethod(index,methodIndex); getMethod.invoke(dataParameters.at(index.row()),Qt::DirectConnection,Q_RETURN_ARG(QVariant,retValue)); return retValue; } int TableModel::getMethodGETIndex(const QModelIndex &index) const { int methodIndex = dataParameters.at(index.row())->getMethodGETIndexs().value(index.column()); return methodIndex; } QMetaMethod TableModel::getMetaMethod(const QModelIndex &index,int methodIndex) const { QMetaMethod method = dataParameters.at(index.row())->metaObject()->method(methodIndex); return method; } bool TableModel::setData(const QModelIndex &index, const QVariant &value, int role) { if(!isIndexValid(index)) return false; if(role == Qt::EditRole && setSpecificData(index,value)) ResParameters::instance().modifyRecord(index); return QAbstractTableModel::setData(index,value); } bool TableModel::setSpecificData(const QModelIndex &index, const QVariant &value) { if( specificIndexValue(index) != value){ int methodIndex = getMethodSETIndex(index); QMetaMethod setMethod = getMetaMethod( index, methodIndex); return setMethod.invoke( dataParameters.at(index.row()), Qt::DirectConnection, Q_ARG(QVariant,value) ); } return false; } int TableModel::getMethodSETIndex(const QModelIndex &index) { int methodIndex = dataParameters.at(index.row())->getMethodSETIndexs().value(index.column()); return methodIndex; }