C++ Qt屬性系統(tǒng)詳細介紹
C++ Qt屬性系統(tǒng)詳細介紹
Qt提供了一個絕妙的屬性系統(tǒng)。跟那些由編譯器提供的屬性差不多。然而,作為一個獨立于編譯器和平臺的庫,Qt不依賴于非標準的編譯特性,比如__property 或[property]。Qt可以在任何平臺上的標準編譯器下編譯。Qt屬性系統(tǒng)基于元數(shù)據(jù)對象系統(tǒng)--就是那個提供了對象內(nèi)置信號和槽通訊機制的家伙。
聲明屬性需要什么
要聲明一個屬性,需在繼承自QObject的類中使用Q_PROPERTY()宏。
Q_PROPERTY(type name READ getFunction [WRITE setFunction] [RESET resetFunction] [NOTIFY notifySignal] [DESIGNABLE bool] [SCRIPTABLE bool] [STORED bool] [USER bool] [CONSTANT] [FINAL])
下面是一些典型的聲明屬性的示例:
Q_PROPERTY(bool focus READ hasFocus) Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)
- 一個屬性的行為就像類的數(shù)據(jù)成員,但是它還具有附加的特性,這些特性可以被元數(shù)據(jù)對象系統(tǒng)操作。這些特性是:需要一個READ訪問器函數(shù)。用于讀屬性的值。理想情況下,有一個不變的函數(shù)用于此目的,并且它必須返回屬性的類型的值或指針或引用。例如,QWidget::focus是一個只讀的屬性,它對應一個讀函數(shù):QWidget::hasFocus()。
- 一個可選的WRITE訪問器函數(shù)。它用于設置屬性的值。它必須返回空并且至少具有一個參數(shù),參數(shù)是屬性類型的值或指針或引用。例如:QWidget::enabled具有WRITE函數(shù)QWidget::setEnable()。只讀屬性不需要寫函數(shù)。例如,QWidget::focus沒有對應的寫函數(shù)。
- 一個可選的RESET函數(shù)。用于設置屬性的值到它的默認值。例如:QWidget::cursor具有典型的READ和WRITE函數(shù),QWidget::cursor()和QWidget::setCursor(),并且它也具有一個RESET函數(shù),QWidget::unsetCursor()。RESET函數(shù)必須返回void并且不帶有任何參數(shù)。
- 一個可選的NOTIFY信號。如果被定義了,信號將在屬性的值改變時發(fā)出。信號必須帶有一個參數(shù),這個參數(shù)的類型必須與屬性相同;參數(shù)保存的是屬性的新值。
- 一個DESIGNABLE變量表明此屬性是否在界面設計器的屬性編輯器中出現(xiàn)。大多數(shù)屬性是可見的,除了為這個變量傳入true或false,你還可以指定一個bool型的成員函數(shù)。
- SCRIPTABLE變量表明這個屬性是否可以被一個腳本引擎操作(默認是true)。你也可以賦予它true或false或bool型函數(shù)。
- STORED變量表明了屬性是否被認為是獨立存在還是依賴于其它的值而存在。它也表明是否在保存對象狀態(tài)時保存此屬性的值。大多數(shù)屬性都是需要保存的,但是,如QWidget::minimumWidth()就是不被保存的,因為它的值是從另一個屬性QWidget::minimumSize()得來的。
- USER變量表明屬性是否被設計為面向用戶的或用戶可修改的類屬性。通常,每個類只有一個USER屬性。例如,QAbstractButton::checked是按鈕類的用戶可修改屬性。注意QItemDelegate獲取和設置widget的USER屬性。
- CONSTANT的出現(xiàn)表明屬性的值是不變的。對于一個object實例,常量屬性的READ方法在每次被調(diào)用時必須返回相同的值。此常量值可能在不同的object實例中不相同。一個常量屬性不能具有WRITE方法或NOYIFY信號。
- FINAL變量的出現(xiàn)表明屬性不能被派生類所重寫。有些情況下,這可以用于效率優(yōu)化,但不是被moc強制的。程序員必須永遠注意不能重寫一個FINAL屬性。
READ,WRITE和RESET函數(shù)都可以被繼承。它們也可以是虛函數(shù)。當它們在被多重繼承中被繼承時,它們必須出現(xiàn)在第一個被繼承的類中。
屬性的類型可以是被QVariant支持的所有類型,也可以是用戶定義的類型。在下面的例子中,類QDate被當作用戶自定義類型。
Q_PROPERTY(QDate data READ getDate WRITE setDate)
因為QDate是用戶定義的,你必須包含<QDate>頭文件。
對于QMap,QList和QValueList屬性,屬性的值是一個QVariant,它包含整個list或map。注意Q_PROPERTY字符串不能包含逗號,因為逗號會劃分宏的參數(shù)。因此,你必須使用QMap作為屬性的類型而不是QMap<QString,QVariant>。為了保持一致性,也需要用QList和QValueList而不是QList<QVariant>和QValueList<QVariant>。
通過元數(shù)據(jù)對象系統(tǒng)讀寫屬性
一個屬性可以使用常規(guī)函數(shù)QObject::property()和QObject::setProperty()進行讀寫,不用知道屬性所在類的任何細節(jié),除了屬性的名字。在下面的小代碼片段中,調(diào)用QAbstractButton::setDown()和QObject::setProperty()都把屬性設置為“down”。
QPushButton *button = new QPushButton; QObject *object = button; button->setDown(true); object->setProperty("down", true);
通過WRITE操作器來操作一個屬性是上面兩者中更好的,因為它快并且在編譯時給于更好的診斷幫助,但是以這種方式設置屬性要求你必須在編譯時了解其類。通過名字來操作屬性使你可以操作在編譯器你不了解的類。你可以在運行時發(fā)現(xiàn)一個類的屬性們,通過查詢它的QObject,QMetaObject和QMetaProerties。
QObject *object = ... const QMetaObject *metaobject = object->metaObject(); int count = metaobject->propertyCount(); for (int i=0; i<count; ++i) { QMetaProperty metaproperty = metaobject->property(i); const char *name = metaproperty.name(); QVariant value = object->property(name); ... }
在上面的代碼片段中,QMetaObject::property()被用于獲取未知類中的屬性的metadata。從metadata中獲取屬性名然后傳給QObject::property()來獲取
一個簡單例子
假設我們有一個類MyClass,它從QObject派生并且在它的private區(qū)使用 了Q_OBJECT宏。我們想在MyClass類中聲明一個屬性來持續(xù)追蹤一個Priorty值。屬性的值叫做priority,并且它的類型是一個在類MyClass中定義的叫做Priority的枚舉。
我們在類的private區(qū)使用Q_PROPERTY()來聲明屬性。READ函數(shù)叫做priority,并且我們包含一個WRITE函數(shù)叫做setPriority。枚舉類型必須使用Q_ENUMS()注冊到元數(shù)據(jù)對象系統(tǒng)中。注冊一個枚舉類型使得枚舉的名字可以在調(diào)用QObject::setProperty()時使用。我們還必須為READ和WRITE函數(shù)提供我們自己的聲明。MyClass的聲明看起來應該是這樣的:
class MyClass : public QObject { Q_OBJECT Q_PROPERTY(Priority priority READ priority WRITE setPriority) Q_ENUMS(Priority) public: MyClass(QObject *parent = 0); ~MyClass(); enum Priority { High, Low, VeryHigh, VeryLow }; void setPriority(Priority priority); Priority priority() const; };
READ函數(shù)是const的并且返回屬性的類型。WRITE函數(shù)返回void并且具有一個屬性類型的參數(shù)。元數(shù)據(jù)對象編譯器強制做這些事情。
在有了一個指向MyClass實例的指針時,我們有兩種方法來設置priority屬性:
MyClass *myinstance = new MyClass; QObject *object = myinstance; myinstance->setPriority(MyClass::VeryHigh); object->setProperty("priority", "VeryHigh");
在此例子中,枚舉類型在MyClass中聲明并被使用Q_ENUMS()注冊到元數(shù)據(jù)對象系統(tǒng)中。這使得枚舉值可以在調(diào)用setProperty()時做為字符串使用。如果枚舉類型是在其它類中聲明的,那么我們就需要用枚舉的全名(如OtherClass::Priority),并且這個其它類也必須從QObject中派生并且也要注冊枚舉類型。
另一個簡單的Q_FLAGS()也是可用的。就像Q_ENUMS(),它注冊一個枚舉類型,但是它把枚舉類型作為一個flag的集合,也就是,值可以用OR操作來合并。一個I/O類可能具有枚舉值Read和Write并且QObject::setProperty()可以接受 Read|Write。此時應使用Q_FLAGS()來注冊枚舉值。
動態(tài)屬性
Qobject::setProperty()也可以用來在運行時向一個類的實例添加新的屬性。當使用一個名字和值調(diào)用它時,如果一個對應的屬性已經(jīng)存在,并且如果值的類型與屬性的類型兼容,那么值就被存儲到屬性中,然后返回true。如果值類型不兼容,屬性的值就不會發(fā)生改變,就會返回false。但是如果對應名字的屬性不存在,那么一個新的屬性就誕生了,以傳入的名字為名,以傳入的值為值,但是依然會返回false。這表示返回值不能用于確定一個屬性是否被設置值,除非你已經(jīng)知道這個屬性已經(jīng)存在于QObject中了。
注意動態(tài)屬性被添加到單個實現(xiàn)的基礎中,也就是,被添加到QObject,而不是QMetaObject。一個屬性可以從一個實例中刪除,通過傳入屬性的名字和非法的QVariant值給QObject::setProperty()。默認的QVariant構(gòu)造器構(gòu)造一個非法的QVariant。
動態(tài)屬性可用QObject::property()來查詢,就行使用Q_PROPERTY()聲明的屬性一樣。
屬性和自定義類型
被屬性使用的自定義類型需要使用Q_DECLARE_METATYPE()宏注冊,以使它們的值能被保存在QVariant對象中。這使得它們可以用于被Q_PROPERTY()聲明的靜態(tài)類型中,也可以被用于動態(tài)類型中。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
上一篇:關于雙向鏈表的增刪改查和排序的C++實現(xiàn)
欄 目:C語言
本文標題:C++ Qt屬性系統(tǒng)詳細介紹
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/1951.html
您可能感興趣的文章
- 04-02c語言沒有round函數(shù) round c語言
- 01-10深入理解C++中常見的關鍵字含義
- 01-10使用C++實現(xiàn)全排列算法的方法詳解
- 01-10c++中inline的用法分析
- 01-10用C++實現(xiàn)DBSCAN聚類算法
- 01-10全排列算法的非遞歸實現(xiàn)與遞歸實現(xiàn)的方法(C++)
- 01-10C++大數(shù)模板(推薦)
- 01-10淺談C/C++中的static與extern關鍵字的使用詳解
- 01-10深入C/C++浮點數(shù)在內(nèi)存中的存儲方式詳解
- 01-10深入理解C/C++混合編程


閱讀排行
本欄相關
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對數(shù)函數(shù)的表達式 c語言中對
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機閱讀
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-10SublimeText編譯C開發(fā)環(huán)境設置
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10delphi制作wav文件的方法
- 01-10C#中split用法實例總結(jié)
- 01-11ajax實現(xiàn)頁面的局部加載
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 04-02jquery與jsp,用jquery
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 08-05DEDE織夢data目錄下的sessions文件夾有什