The Property System
感謝MaxValue,hennychen?對本文的翻譯,同時非常感謝Cxt_programmer在百忙中抽出時間對翻譯初稿的認真校驗。才使本文與讀者盡快見面。由于書稿內容多,我們的知識有限,盡管我們進行了細心的檢查,但是還是會存在錯誤,這里懇請廣大讀者批評指正,并發送郵件至BeyondVincent@devdiv.com,在此我們表示衷心的感謝。
注:本文原文地址:The?Property?System。
?
????????????????????????????????????????第一章??????屬性系統
Qt提供了一個成熟的屬性系統,它和某些編譯器廠商提供的屬性系統相似。但是,作為一個編譯器及平臺獨立的程序庫,Qt并不依賴非標準的編譯器特性,如__property或[property]。Qt對屬性系統的解決方案可以在Qt支持的每一個平臺上的任何標準編譯器上工作,它是基于元對象系統(Meta-Object?System)的。對象間通信用的信號和槽機制也是基于元對象系統的。
?
?
?
?
?????????????????????????????????第二章??????定義屬性的要求
在繼承自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])
?
在此,有幾個取自QWidget類的典型的屬性聲明的例子。
Q_PROPERTY(bool focus READ hasFocus)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)
雖然屬性的行為與類數據成員相似,但是,它具有額外的特性,這些特性可以通過元對象系統訪問。
n??READ?存取函數是必須的。它用于讀取屬性的值。原則上,這里使用const函數,并且它必須返回屬性的類型、指針或者引用。例如,QWidget::focus?是與?READ函數QWidget::hasFocus()對應的只讀屬性。
n??WRITE?存取函數是可選的。它用于設置屬性的值。它必須返回void并且帶一個參數,這個參數可以是屬性的類型,指針,或者該類型的屬性。例如,QWidget::enabled?有WRITE?函數QWidget::setEnabled()。 只讀屬性不需要WRITE?函數,如,QWidget::focus?就沒有WRITE函數。
n??RESET?函數是可選的。它用來將屬性設置回它的上下文指定的默認值。如,QWidget::cursor?擁有典型的READ和WRITE?函數:QWidget::cursor()?和?QWidget::setCursor()。同時,還擁有RESET函數QWidget::unsetCursor(),如果沒有調用QWidget::setCursor(),該函數的作用就是重置為上下文指定的光標。?RESET函數必須返回void,并且不帶參數。
n??NOTIFY?信號是可選的。 如果定義了,它應該指定一個在那個類中存在的信號,這個信號在屬性值改變時被發射。
n??DESIGNABLE 指明了該屬性是否對用戶界面設計工具(如,Qt Designer)的屬性編輯器可見。大多數屬性是DESIGNABLE的(默認為true)。 除了true或false,你還可以指定一個boolean成員函數。
n??SCRIPTABLE 指明了該屬性是否能被腳本引擎訪問(默認為true)。除了true或false,你還可以指定一個boolean成員函數。
n??STORED?指明了該屬性是否單獨存在或者依賴于其他值。 它同時也指明了當存儲對象的狀態時,屬性是否被保存。?大多數的屬性都是STORED的(默認值為true),但是,QWidget::minimumWidth()的STORED值為false,因為它的值僅是取自寬度組件屬性QWidget::minimumSize(),它的值是一個QSize。
n??USER?指明了類中該屬性是否被指定為面向用戶的或者用戶可編輯的。通常情況下,每個類中僅有一個USER屬性(其默認值為false),如QAbstractButton::checked即為(可復選)按鈕的用戶可編輯屬性。注意,QItemDelegate能獲取和設置組件的 USER屬性。
n??CONSTANT的存在,表明該屬性是常量。 對于給定的對象實例,常量屬性的READ方法每次調用的時候必須返回相同的值。 該常量的值,對于不同的對象實例可能是不同的。?常量屬性不能有WRITE方法或者NOTIFY信號。
n??FINAL的存在,表明該屬性不能被子類覆蓋。 這可以用于某些類中的性能優化,但是,并不是由moc強制執行的。 一定要注意,絕對不要覆蓋FINAL屬性。
READ, WRITE,?和RESET函數可以被繼承。 它們也可以是virtual的。 當它們以多繼承的方式被繼承時,它們必須是來自第一個被繼承的類。
屬性的類型可以是任何被QVariant支持的類型,或者是用戶自定義的類型。 下面的例子中,QDate類被視為用戶自定義類型。
?
Q_PROPERTY(QDate date READ getDate WRITE setDate)
?
由于QDate是用戶自定義的,所以,你在屬性聲明時必須包含<QDate>頭文件。
對于QMap,?QList,和QValueList屬性,屬性值是QVariant類型的,它的值是整個list或者map。注意Q_PROPERTY中不包含逗號,因為逗號會分隔宏參數。因此,你一定要使用QMap作為屬性類型,而不是QMap<QString,QVariant>。 為了保持一致性,同樣應該使用QList和 QValueList,而不是QList<QVariant>?和?QValueList<QVariant>。
?
?
?
?
???????????????????????第三章??????使用元對象系統讀寫屬性
屬性可以通過通用函數QObject::property()和QObject::setProperty()來讀寫,此時,除了屬性的名稱,不需要了解所擁有類的其他情況。下面的代碼片段中,對QAbstractButton::setDown()的調用,和對QObject::setProperty()都是對“down”屬性的設置。
?QPushButton *button = new QPushButton;
?QObject *object = button;
?
?button->setDown(true);
?object->setProperty("down", true);
通過?WRITE存取函數訪問屬性是上面兩種方式中較好的,因為它更高效,并且在編譯時會給出更多的診斷提示。但是,以該方式設置屬性需要你在編譯時就知道這個類的定義。通過名稱訪問屬性時,不需要知道類的定義。你可以在運行時,通過QObject,?QMetaObject,和?QMetaProperties去查詢類的屬性。
?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(),用于獲取在當前object中屬性的value。
?
?
?
???????????????????????????????????第四章??????一個簡單例子
假設我們有一個MyClass類,它繼承自QObject,并且在private域中使用了Q_OBJECT宏。我們要在MyClass中聲明一個屬性用于保存優先級(priority value)。 屬性的名稱將是priority,它的類型是MyClass中定義的一個枚舉類型Priority。
我們在類的private域中用Q_PROPERTY()宏聲明屬性。 所需的 READ 函數被命名為這里,這里包括一個名為 setPriority 的WRITE函數。 枚舉類型必須與使用 Q_ENUMS()注冊到元對象系統。 注冊一個枚舉類型使枚舉名在調用QObject::setProperty()時使用。我們還必須提供自己的讀取和寫入函數的聲明。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函數是const函數,并返回屬性類型。 WRITE函數返回 void,并且以屬性類型作為參數。 元對象編譯器強制執行這些規定。
我們有兩種方法設置其priority屬性,一個指向MyClass實例的MyClass *或者一個指向MyClass實例的QObject *:
MyClass *myinstance = new MyClass;
QObject *object = myinstance;
?
myinstance->setPriority(MyClass::VeryHigh);
object->setProperty("priority", "VeryHigh");
在這個例子中,屬性類型的枚舉在MyClass類中聲明,并使用 Q_ENUMS() 宏注冊到元對象系統。這使得調用setProperty()時,枚舉值可作為字符串形式傳入。在另一個類中定義的枚舉類型,需要完整的名稱,如:(i.e., OtherClass::Priority), 且該類也必須繼承QObject 并使用 Q_ENUMS()宏注冊該枚舉類型。
Q_FLAGS()宏也可以提供類似的功能。 和Q_ENUMS()類似,在注冊一個枚舉類型的同時,會將該類型標注為一組標記。例如,可以使用或運算合并各個標記。一個I/O類可能有Read和Write枚舉值,并且QObject::setProperty()可以接受Read |Write這樣的‘或’方式,此時應該使用Q_FLAGS()注冊這個枚舉類型。
第五章??????動態的屬性(Properties)
QObject::setProperty() 還可以在運行時將新屬性添加到對象中。調用QObject::setProperty() 時傳入屬性名和屬性值,如果該屬性存在于QObject中,并且屬性值與屬性類型相匹配的話,屬性值將被存儲到屬性中,setProperty返回 true。如果屬性類型與屬性值不匹配,屬性將不會改變,setProperty返回false。但是,如果QObject中沒有給定名稱的屬性(即,如 果這個屬性沒用Q_PROPERTY()聲明,則新屬性和屬性值自動添加到該的 QObject,但setProperty仍返回 false。這意味著不能從setProperty返回fasle來判斷屬性是否被正確設置,除非你預先知道QObject中已經有該屬性。
請注意動態屬性的添加基于每個實例,它們會添加到 Qobject中,而不是 QMetaObject。調用QObject::setProperty()時傳遞一個屬性名和一個無效的QVariant,可以從實例中刪除該屬性。
?QVariant 的默認構造函數構造一個無效的 QVariant。
如同使用Q_PROPERTY().在編譯時聲明屬性,動態屬性可以用QObject::property()查詢。
?
????????????第六章??????屬性(Properties)和自定義類型
使用屬性的自定義類型需要使用 Q_DECLARE_METATYPE() 宏注冊,以便可以在 QVariant 對象中存儲它們的值。這使他們既適合使用 Q_PROPERTY() 宏在類定義中聲明靜態屬性,又適合在運行時動態創建屬性。
?
??????????????????????????????第七章??????給類添加附屬信息
Q_CLASSINFO()宏可以連接到屬性系統,為類的meta-object添加一對name-value,例如:
Q_CLASSINFO("Version", "3.0.0")
?
與其他原數據(meta-data)一樣,類信息可以在運行時通過meta-object訪問,請參閱 QMetaObject::classInfo()。
另請參閱元對象系統(Meta-Object?System)、 信號和槽(Signals and Slots)、Q_DECLARE_METATYPE()、QMetaType和QVariant。
轉載于:https://www.cnblogs.com/senior-engineer/p/11155989.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的The Property System的全部內容,希望文章能夠幫你解決所遇到的問題。