QT內省機制、自定義Model、資料庫

来源:http://www.cnblogs.com/sfy5848/archive/2016/11/04/6030493.html
-Advertisement-
Play Games

本文將介紹自定義Model過程中資料庫數據源的獲取方法,我使用過以下三種方式獲取資料庫數據源: 創建 存儲對應資料庫所有欄位的 結構體,將結構體置於容器中返回,然後根據索引值(QModelIndex)取出最終的欄位值; 創建 存儲對應資料庫所有欄位的 類,將類對象置於容器中返回,然後利用內省機制獲取 ...


本文將介紹自定義Model過程中資料庫數據源的獲取方法,我使用過以下三種方式獲取資料庫數據源:
  • 創建 存儲對應資料庫所有欄位的 結構體,將結構體置於容器中返回,然後根據索引值(QModelIndex)取出最終的欄位值;
  • 創建 存儲對應資料庫所有欄位的 類,將類對象置於容器中返回,然後利用內省機制獲取對象相應欄位(屬性)值。
  • 不用自己造輪子,直接使用QVariantList類,將QVariantList 對象置於容器中,如QVector<QVariantList >,然後根據索引值(QModelIndex)取出最終的欄位值;
本文重點介紹第二種,即利用QT的內省機制來獲取數據。   1.自定義Model過程(通過內省功能獲得欄位值,也就是第二種方法) 本文中自定義Model繼承於QAbstractTableModel ,重點描述setData(..)函數與data(...)函數的重載過程。     首先需要介紹 Parameter類,該類用於存儲查詢資料庫中某表所得的欄位值。
 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

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; }
成員變數 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類來存儲資料庫表的每一條記錄。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • C#中枚舉的使用 1.原則上全部使用枚舉,不使用常量,除非常量是一個,不是一組 2.如果一組常量中增減常量後要對代碼修改,則要將這組常量定義為枚舉 3.如果一組常量中增減常量後代碼不需要修改,則要將這組常量存儲到字碼主檔中,由資料庫進行維護 枚舉擴展方法 1.將字元串轉為枚舉 2.獲取枚舉的描述,在 ...
  • Orchard CMS安裝後,配置文件和資料庫保存在App_Data目錄中。這個目錄是受保護的,是不能通過網址訪問到的。 如果要完全重裝你的站點,你可以刪除此目錄中的所有文件,但是最好先備份!刪除後重新訪問站點,就可以重新安裝Orchard CMS。 如果遇到問題,可參考以下鏈接:http://or ...
  • 一、 ID是設計的時候所指定的ID。 ClientID是當這個控制項生成到客戶端頁面時候,需要在客戶端訪問時候用的。 UniqueID是當需要參與服務端回傳的時候用的。 備註:當控制項是子控制項的時候(例如在用戶控制項中的Button),ClientID在HTML頁面中是作為控制項的ID屬性,UniqueID ...
  • C# DateTime與時間戳的相互轉換,包括JavaScript時間戳和Unix的時間戳。 ...
  • 面向過程就是分析解決問題所需要的步驟,然後用函數把這些步驟一步一步的實現出來,使用的時候一個一個依次調用就可以了。 面向對象就是把構成問題事務分解成各個對象,建立對象的目的不是為了完成一個步驟,而是為了描述某個事物在整個解決問題的步驟中的行為。以功能來劃分問題,而不是步驟 ...
  • 1.1概述 將對象組合成樹形結構以表示“部分-整體”的層次結構。組合(Composite)使用戶對單個對象和組合對象的使用具有一致性。這就是組合模式的定義。 如果一個對象包含另一個對象的引用,稱這樣的對象為組合對象。如果將當前組合對象作為一個整體的話,那麼它所包含的對象就是該整體的一部分。如果一個對 ...
  • 1.1概述 保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。這就是單件模式的定義。 在某些情況下,我們可能需要某個類只能創建一個對象,即不讓用戶用該類實例化出多於兩個的實例。 例如,在一個公文管理系統中,公文類的實例“公文文件”,需要將公章類的實例作為自己的一個成員,以表明自己是一個有效的公文 ...
  • 1.1概述 用原型實例指定創建對象的種類,並且通過複製這些原型創建新的對象。這就是原型模式的定義。 在某些情況下,可能不希望反覆使用類的構造方法創建許多對象,而是希望使用該類創建一個對象後,以該對象為原型得到該對象的若幹個複製品。也就是說,將一個對象定義為原型對象,要求改原型對象提供一個方法,使該原 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...