6、Qt Meta Object system 学习
原文地址:http://blog.csdn.net/ilvu999/article/details/8049908
?
使用 meta object system
- 繼承自 QOject
- 類定義中添加 Q_OBJECT 宏
- 使用 moc 程序對包含該宏的文件進行處理
采用 qmake 進行處理時,如果頭文件xxx.h內包含 Q_OBJECT 宏,將生成 moc_xxx.cpp 文件。如果xxx.cpp文件內包含宏,將生成 xxx.moc 文件(這時,我們需要在xxx.cpp文件內添加?#include"xxx.moc")
Q_OBJECT宏
包括兩個方面,
- 該宏在C++中的展開,有編譯預處理器完成
- moc 程序對該宏的處理
宏定義
1 #define Q_OBJECT \2 public: \3 Q_OBJECT_CHECK \4 static const QMetaObject staticMetaObject; \5 Q_OBJECT_GETSTATICMETAOBJECT \6 virtual const QMetaObject *metaObject() const; \7 virtual void *qt_metacast(const char *); \8 QT_TR_FUNCTIONS \9 virtual int qt_metacall(QMetaObject::Call, int, void **); \ 10 private: \ 11 Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; \ 12 Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);?
而宏 QT_TR_FUNCTIONS 將展開為我們使程序國際化經常使用的 tr 與 trUtf8函數
1 # define QT_TR_FUNCTIONS \ 2 static inline QString tr(const char *s, const char *c = 0) \ 3 { return staticMetaObject.tr(s, c); } \ 4 static inline QString trUtf8(const char *s, const char *c = 0) \ 5 { return staticMetaObject.trUtf8(s, c); } \ 6 static inline QString tr(const char *s, const char *c, int n) \ 7 { return staticMetaObject.tr(s, c, n); } \ 8 static inline QString trUtf8(const char *s, const char *c, int n) \ 9 { return staticMetaObject.trUtf8(s, c, n); }?
moc 處理
Q_OBJECT 為我們添加了這么多成員函數,而這些函數我們有沒有自己去實現,那么其定義在哪兒呢? 這就是 moc 為我們做的,自動生成這些成員函數的定義,放于生成的 xxx.moc 或 moc_xxx.cpp 文件內
注意:兩個文件的區別(如果你用cmake或其他工具的話,這點可能很重要)
- 生成的 moc_xxx.cpp 中會自動包含 xxx.h 頭文件,所以它可以被獨立編譯成目標文件(.o 或 .obj)
- 生成的 xxx.moc 是不會包含 xxx.cpp 的(要是包含就出問題了,能發現吧?),因此 xxx.moc 中沒有類定義,它無法被獨立編譯,只能被 include 到 xxx.cpp 中。
QMetaObject
既然 Q_OBJECT 展開成與 QMetaObject 有關的成員函數,看一下QMetaObject 都提供哪些常用功能
- className() 返回類的名字
- superClass() 返回父類的 QMetaObject 對象
- method()與methodCount() 提供meta函數信息(包括signals, slots 與 invokable函數)
- enumerator()與 enumeratorCount() 提供enum類型信息
- propertyCount()與 property() 提供屬性信息
- constructor()與 constructorCount() 提供 meta-constructors 信息
既然meta object能為我們的類提供這么多信息,那么這些信息存放哪兒了(大家肯定都能猜到秘密在moc生成的文件內,但為清楚起見,我們看一下QMetaObject的定義)。
這是用 struct 定義的一個類,我們略過其它,只看其數據成員
1 struct Q_CORE_EXPORT QMetaObject2 {3 const char *className() const;4 const QMetaObject *superClass() const;5 6 QObject *cast(QObject *obj) const;7 const QObject *cast(const QObject *obj) const;8 9 #ifndef QT_NO_TRANSLATION10 // ### Qt 4: Merge overloads11 QString tr(const char *s, const char *c) const;12 QString trUtf8(const char *s, const char *c) const;13 QString tr(const char *s, const char *c, int n) const;14 QString trUtf8(const char *s, const char *c, int n) const;15 #endif // QT_NO_TRANSLATION16 17 int methodOffset() const;18 int enumeratorOffset() const;19 int propertyOffset() const;20 int classInfoOffset() const;21 22 int constructorCount() const;23 int methodCount() const;24 int enumeratorCount() const;25 int propertyCount() const;26 int classInfoCount() const;27 28 int indexOfConstructor(const char *constructor) const;29 int indexOfMethod(const char *method) const;30 int indexOfSignal(const char *signal) const;31 int indexOfSlot(const char *slot) const;32 int indexOfEnumerator(const char *name) const;33 int indexOfProperty(const char *name) const;34 int indexOfClassInfo(const char *name) const;35 36 QMetaMethod constructor(int index) const;37 QMetaMethod method(int index) const;38 QMetaEnum enumerator(int index) const;39 QMetaProperty property(int index) const;40 QMetaClassInfo classInfo(int index) const;41 QMetaProperty userProperty() const;42 43 static bool checkConnectArgs(const char *signal, const char *method);44 static QByteArray normalizedSignature(const char *method);45 static QByteArray normalizedType(const char *type);46 47 // internal index-based connect48 static bool connect(const QObject *sender, int signal_index,49 const QObject *receiver, int method_index,50 int type = 0, int *types = 0);51 // internal index-based disconnect52 static bool disconnect(const QObject *sender, int signal_index,53 const QObject *receiver, int method_index);54 static bool disconnectOne(const QObject *sender, int signal_index,55 const QObject *receiver, int method_index);56 // internal slot-name based connect57 static void connectSlotsByName(QObject *o);58 59 // internal index-based signal activation60 static void activate(QObject *sender, int signal_index, void **argv); //obsolete61 static void activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv); //obsolete62 static void activate(QObject *sender, const QMetaObject *, int local_signal_index, void **argv);63 static void activate(QObject *sender, const QMetaObject *, int from_local_signal_index, int to_local_signal_index, void **argv); //obsolete64 65 // internal guarded pointers66 static void addGuard(QObject **ptr);67 static void removeGuard(QObject **ptr);68 static void changeGuard(QObject **ptr, QObject *o);69 70 static bool invokeMethod(QObject *obj, const char *member,71 Qt::ConnectionType,72 QGenericReturnArgument ret,73 QGenericArgument val0 = QGenericArgument(0),74 QGenericArgument val1 = QGenericArgument(),75 QGenericArgument val2 = QGenericArgument(),76 QGenericArgument val3 = QGenericArgument(),77 QGenericArgument val4 = QGenericArgument(),78 QGenericArgument val5 = QGenericArgument(),79 QGenericArgument val6 = QGenericArgument(),80 QGenericArgument val7 = QGenericArgument(),81 QGenericArgument val8 = QGenericArgument(),82 QGenericArgument val9 = QGenericArgument());83 84 static inline bool invokeMethod(QObject *obj, const char *member,85 QGenericReturnArgument ret,86 QGenericArgument val0 = QGenericArgument(0),87 QGenericArgument val1 = QGenericArgument(),88 QGenericArgument val2 = QGenericArgument(),89 QGenericArgument val3 = QGenericArgument(),90 QGenericArgument val4 = QGenericArgument(),91 QGenericArgument val5 = QGenericArgument(),92 QGenericArgument val6 = QGenericArgument(),93 QGenericArgument val7 = QGenericArgument(),94 QGenericArgument val8 = QGenericArgument(),95 QGenericArgument val9 = QGenericArgument())96 {97 return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3,98 val4, val5, val6, val7, val8, val9);99 } 100 101 static inline bool invokeMethod(QObject *obj, const char *member, 102 Qt::ConnectionType type, 103 QGenericArgument val0 = QGenericArgument(0), 104 QGenericArgument val1 = QGenericArgument(), 105 QGenericArgument val2 = QGenericArgument(), 106 QGenericArgument val3 = QGenericArgument(), 107 QGenericArgument val4 = QGenericArgument(), 108 QGenericArgument val5 = QGenericArgument(), 109 QGenericArgument val6 = QGenericArgument(), 110 QGenericArgument val7 = QGenericArgument(), 111 QGenericArgument val8 = QGenericArgument(), 112 QGenericArgument val9 = QGenericArgument()) 113 { 114 return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2, 115 val3, val4, val5, val6, val7, val8, val9); 116 } 117 118 static inline bool invokeMethod(QObject *obj, const char *member, 119 QGenericArgument val0 = QGenericArgument(0), 120 QGenericArgument val1 = QGenericArgument(), 121 QGenericArgument val2 = QGenericArgument(), 122 QGenericArgument val3 = QGenericArgument(), 123 QGenericArgument val4 = QGenericArgument(), 124 QGenericArgument val5 = QGenericArgument(), 125 QGenericArgument val6 = QGenericArgument(), 126 QGenericArgument val7 = QGenericArgument(), 127 QGenericArgument val8 = QGenericArgument(), 128 QGenericArgument val9 = QGenericArgument()) 129 { 130 return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0, 131 val1, val2, val3, val4, val5, val6, val7, val8, val9); 132 } 133 134 QObject *newInstance(QGenericArgument val0 = QGenericArgument(0), 135 QGenericArgument val1 = QGenericArgument(), 136 QGenericArgument val2 = QGenericArgument(), 137 QGenericArgument val3 = QGenericArgument(), 138 QGenericArgument val4 = QGenericArgument(), 139 QGenericArgument val5 = QGenericArgument(), 140 QGenericArgument val6 = QGenericArgument(), 141 QGenericArgument val7 = QGenericArgument(), 142 QGenericArgument val8 = QGenericArgument(), 143 QGenericArgument val9 = QGenericArgument()) const; 144 145 enum Call { 146 InvokeMetaMethod, 147 ReadProperty, 148 WriteProperty, 149 ResetProperty, 150 QueryPropertyDesignable, 151 QueryPropertyScriptable, 152 QueryPropertyStored, 153 QueryPropertyEditable, 154 QueryPropertyUser, 155 CreateInstance 156 }; 157 158 int static_metacall(Call, int, void **) const; 159 static int metacall(QObject *, Call, int, void **); 160 161 #ifdef QT3_SUPPORT 162 QT3_SUPPORT const char *superClassName() const; 163 #endif 164 165 struct { // private data 166 const QMetaObject *superdata; 167 const char *stringdata; 168 const uint *data; 169 const void *extradata; 170 } d; 171 };其中呢,
-
uperdata,指向父類的MetaObject,容易理解
- extradata 似乎目前未啟用,不用理解
- 剩下兩個是什么呢? 如你所想,就在 moc 生成的文件內
moc生成的文件
隨便找個 moc 文件出來看看
1 static const uint qt_meta_data_HPixmapScene[] = {...}; 2 static const char qt_meta_stringdata_HPixmapScene[] = {...}; 3 const QMetaObject HPixmapScene::staticMetaObject = { 4 { &QGraphicsScene::staticMetaObject, qt_meta_stringdata_HPixmapScene, 5 qt_meta_data_HPixmapScene, 0 } 6 };這是一個QGraphicsScene的子類。對比前面QMetaObject的數據成員看看,是不是很簡單:
- 先分別定義1個 uint 和 char 的數組,
-
用這兩個數組首地址和父類的MetaObject的指針初始化 staticMetaObject
- 這個 staticMetaObject 是我們自己的類的靜態成員變量
uint數組
接下來我們可以看看 uint 數組,這個數組中存放的是一些索引值,來指導我們從char字符串中提取信息
對比QMetaObject用到的數據結構 QMetaObjectPrivate,看看,是不是豁然開朗了:uint 數組中的第一段 就對應這個結構體
1 struct QMetaObjectPrivate2 {3 int revision;4 int className;5 int classInfoCount, classInfoData;6 int methodCount, methodData;7 int propertyCount, propertyData;8 int enumeratorCount, enumeratorData;9 int constructorCount, constructorData; //since revision 2 10 int flags; //since revision 3 11 int signalCount; //since revision 4 12 // revision 5 introduces changes in normalized signatures, no new members 13 // revision 6 added qt_static_metacall as a member of each Q_OBJECT and inside QMetaObject itself 14 15 static inline const QMetaObjectPrivate *get(const QMetaObject *metaobject) 16 { return reinterpret_cast<const QMetaObjectPrivate*>(metaobject->d.data); } 17 18 static int indexOfSignalRelative(const QMetaObject **baseObject, 19 const char* name, 20 bool normalizeStringData); 21 static int indexOfSlotRelative(const QMetaObject **m, 22 const char *slot, 23 bool normalizeStringData); 24 static int originalClone(const QMetaObject *obj, int local_method_index); 25 26 #ifndef QT_NO_QOBJECT 27 //defined in qobject.cpp 28 enum DisconnectType { DisconnectAll, DisconnectOne }; 29 static void memberIndexes(const QObject *obj, const QMetaMethod &member, 30 int *signalIndex, int *methodIndex); 31 static bool connect(const QObject *sender, int signal_index, 32 const QObject *receiver, int method_index_relative, 33 const QMetaObject *rmeta = 0, 34 int type = 0, int *types = 0); 35 static bool disconnect(const QObject *sender, int signal_index, 36 const QObject *receiver, int method_index, 37 DisconnectType = DisconnectAll); 38 static inline bool disconnectHelper(QObjectPrivate::Connection *c, 39 const QObject *receiver, int method_index, 40 QMutex *senderMutex, DisconnectType); 41 #endif 42 };?
char 數組
配合上面的索引數組,繼續看一下:
1 static const char qt_meta_stringdata_HPixmapScene[] = { 2 "HPixmapScene\0\0pix\0setPixmap(QPixmap)\0" 3 "img\0setImage(QImage)\0" 4 };?
索引數組中 className 為0,我們看字符數組中從0開始是什么?恩,就是我們類的名字。
我們類的類中還定義的兩個slot函數
public slots:
void setPixmap(const QPixmap& pix);
void setImage(const QImage& img);
我們看看是怎么對應的,
- 首先看索引數組 methodCount=2, methodData=14,告訴我們有兩個method,數據從索引數組的14開始
-
然后,我們從索引數組的14開始看,{18,???14,???13,???13,?0x0a
- 這兒,18和14對應到我們的char數組了,看一下,分別是setPixmap(QPixmap) 和 pix。怎么樣,和我們定義的函數對應上了吧。
- 其實上面有一處不太對,從開始就不太對。什么不對呢?
Qt manual 對此只提了一句,在QObject的manual中,除此之外,似乎再沒出現過:要用 meta object system,我們不一定要繼承QObject,普通的C++的類也可以部分使用,與此對應,我們不用Q_OBJECT宏,而用Q_GADGET宏,當然,這個只提供部分meta object system 的功能,比如Qt 元對象系統之 "enum自省"?
另外:ActiveQt模塊中,QAxObject和QAxWidget 定義了信號槽,卻沒有使用Q_OBJECT,具體可以看?ActiveQt模塊學習(三)
另外 moc 生成的文件內有一個 Q_NO_DATA_RELOCATION 宏,無論用 Q_OBJECT 還是 Q_GADGET,我不清楚該宏起什么作用的。
整理思路太累了,寫完這個,還不清楚什么時候再寫(二)
參考-
http://doc.qt.nokia.com/latest/object.html
-
http://doc.qt.nokia.com/latest/metaobjects.html
-
Qt MetaObject System詳解之一:簡介
-
Inside Qt Series (五):元對象系統(Meta-Object System)
?
Qt屬性系統(Qt's Property System)
使用
- 使用 Q_PROPERTY 宏來聲明屬性(即 將屬性注冊到meta-object系統中)。
- 通過QObject的metaObject即可獲得用Q_PROPERTY注冊的所有屬性
- QMetaObject::property 返回QMetaProperty對象指針
- QMetaObject::propertyCount 屬性個數(包含父類定義的屬性)
- QMetaObject::propertyOffset 屬性的開始索引
- QMetaObject::indexOfProperty 根據屬性名得到屬性索引
- QMetaObject::userProperty 帶USER的屬性,無參數(只能有一個?)
- 通過QMetaProperty可以獲得屬性的的各種信息
- 使用QObjecty可以讀取或設置屬性
- property
- setProperty
Q_PROPERTY
Qt屬性是靠 Q_PROPERTY 宏進行聲明的
1 Q_PROPERTY(type name READ getFunction2 [WRITE setFunction]3 [RESET resetFunction]4 [NOTIFY notifySignal]5 [DESIGNABLE bool]6 [SCRIPTABLE bool]7 [STORED bool]8 [USER bool]9 [CONSTANT] 10 [FINAL])例子:
1 Q_PROPERTY(bool focus READ hasFocus) 2 Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) 3 Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor) 4 Q_PROPERTY(bool checked READ isChecked WRITE setChecked DESIGNABLE isCheckable NOTIFY toggled USER true)- READ 該項必須!后接無參的const函數
- WRITE 后接無返回值的但參數函數
- RESET 后接無參無返回值的函數
- NOTIFT 后接單參的信號,屬性變化時發射
- DESIGNABLE 布爾量(默認True),是否在designer中出現
- SCRIPTABLE 布爾量(默認True),是否用于腳本
- STORED 布爾量(默認True),是否“不依賴”其他值。比如mimimumWidth 依賴 mimimumSize,故為False
- USER 布爾量(默認False),是否是直接面向用戶的。比如QPushButton的 checked 屬性,則為 True
- CONSTANT 出現則代表常量。不可有 WRITE 及 NOTIFY
- FINAL 出現則代表不能被派生類重載?
從源代碼文件 qobjectdefs.h 中可以看到該宏定義為空
1 #define Q_PROPERTY(TEXT)也就是是說該宏僅對 moc 有意義,moc處理后生成的代碼在 xxx.moc 或 moc_xxx.cpp 中。
moc
看一個例子:
1 Q_PROPERTY(bool colorDialog READ colorDialogEnabled WRITE setColorDialogEnabled)查看對應的moc文件:
-
索引數據在?static?const?uint?qt_meta_data_QtColorPicker[]
-
字符串數據在?static?const?char?qt_meta_stringdata_QtColorPicker[]
- 并在 qt_metacall 函數中生成有相關的代碼
?
自測如下-->
源碼:
結果:
?
動態屬性
通過QObject的setProperty 設置屬性時,如果該屬性不存在,則為設置為動態屬性。動態屬性存儲在QObject中,而不是其QMetaObject對象中。
類型
- 對于 enum 類型,需要用 Q_ENUMS 或Q_FLAGS 將其注冊到 meta-object系統中
- 對自定義類型,如果要用作屬性,需要用 Q_DECLARE_METATYPE()將其注冊到meta-object系統中。
對 PySide 及 PyQt4
1 QtCore.pyqtProperty(type, fget=None, fset=None, freset=None, fdel=None, doc=None, designable=True, scriptable=True, stored=True, user=False, constant=False, final=False)2 3 type4 5 type of the property6 7 fget8 9 getter function 10 11 fset 12 13 None 14 15 setter function 16 17 freset 18 19 None 20 21 function used to reset the value of the property to its default value (only in C++) 22 23 fdel 24 25 None 26 27 function for del'ing the property (only in Python) 28 29 doc 30 31 None 32 33 docstring of the property 34 35 designable 36 37 True 38 39 value of Qt DESIGNABLE flag 40 41 scriptable 42 43 True 44 45 value of Qt SCRIPTABLE flag 46 47 stored 48 49 True 50 51 value of Qt STORED flag 52 53 user 54 55 False 56 57 value of Qt USER flag 58 59 constant 60 61 False 62 63 value of Qt CONSTANT flag 64 65 final 66 67 False 68 69 value of Qt FINAL flag使用舉例:
1 class MyObject(QObject):2 def __init__(self,startval=42):3 self.ppval = startval4 def readPP(self):5 return self.ppval6 def setPP(self,val):7 self.ppval = val8 pp = pyqtProperty(int, readPP, setPP)9 10 obj = MyObject() 11 obj.pp = 47 12 print obj.pp?
-
PySide用Property來取代pyqtProPerty
-
PySide一開始出現一個失誤,用的是QProperty而不是Property
-
PyQt4 為 fget 提供了默認的None,由于Qt要求READ函數必須有,故PySide不為其提供默認值
?
?看 ActiveQt 模塊的源碼,看到信號槽部分,實在看不懂了,只好回來繼續學習 元對象系統 了。
在前面的學習中中簡單整理了Q_OBJECT宏 與 moc 生成的文件,并且從中學習了屬性系統的宏 Q_PROPERTY。現在該看看源碼中信號槽相關的內容了(Qt4.7的源碼,其他版本與此可能不同)
1 QObject::connect非常重要的函數,不是么?
函數一開始是這么一段:
1 bool QObject::connect(const QObject *sender, const char *signal,2 const QObject *receiver, const char *method,3 Qt::ConnectionType type)4 {5 {6 const void *cbdata[] = { sender, signal, receiver, method, &type };7 if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))8 return true;9 } 10 11 。。。。。。 12 }?
這個復合語句的作用看不太懂,QInternal類的定義在 src/corelib/global/qnamespace.h 中,類成員的實現在 src/corelib/global/qglobal.cpp 中。
1 bool QInternal::activateCallbacks(Callback cb, void **parameters)2 {3 Q_ASSERT_X(cb >= 0, "QInternal::activateCallback()", "Callback id must be a valid id");4 5 QInternal_CallBackTable *cbt = global_callback_table();6 if (cbt && cb < cbt->callbacks.size()) {7 QList<qInternalCallback> callbacks = cbt->callbacks[cb];8 bool ret = false;9 for (int i=0; i<callbacks.size(); ++i) 10 ret |= (callbacks.at(i))(parameters); 11 return ret; 12 } 13 return false; 14 }然后檢測4個參數是否都非空
1 if (sender == 0 || receiver == 0 || signal == 0 || method == 0) { 2 qWarning("QObject::connect: Cannot connect %s::%s to %s::%s", 3 sender ? sender->metaObject()->className() : "(null)", 4 (signal && *signal) ? signal+1 : "(null)", 5 receiver ? receiver->metaObject()->className() : "(null)", 6 (method && *method) ? method+1 : "(null)"); 7 return false; 8 }?
然后
1 QByteArray tmp_signal_name; 2 3 if (!check_signal_macro(sender, signal, "connect", "bind")) 4 return false; 5 const QMetaObject *smeta = sender->metaObject();- 檢測與信號連接的“信號或槽”是否符合格式,然后從元對象系統中獲得相應的索引
檢測信號與槽的參數是否匹配,對與Queued連接,檢測參數是否注冊到元對象系統
1 ++signal; //skip code2 int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false);3 if (signal_index < 0) {4 // check for normalized signatures5 tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);6 signal = tmp_signal_name.constData() + 1;7 8 smeta = sender->metaObject();9 signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false); 10 } 11 if (signal_index < 0) { 12 // re-use tmp_signal_name and signal from above 13 14 smeta = sender->metaObject(); 15 signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true); 16 } 17 if (signal_index < 0) { 18 err_method_notfound(sender, signal_arg, "connect"); 19 err_info_about_objects("connect", sender, receiver); 20 return false; 21 } 22 signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index); 23 int signalOffset, methodOffset; 24 computeOffsets(smeta, &signalOffset, &methodOffset); 25 int signal_absolute_index = signal_index + methodOffset; 26 signal_index += signalOffset; 27 28 QByteArray tmp_method_name; 29 int membcode = extract_code(method); 30 31 if (!check_method_code(membcode, receiver, method, "connect")) 32 return false; 33 const char *method_arg = method; 34 ++method; // skip code 35 36 const QMetaObject *rmeta = receiver->metaObject(); 37 int method_index_relative = -1; 38 switch (membcode) { 39 case QSLOT_CODE: 40 method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); 41 break; 42 case QSIGNAL_CODE: 43 method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); 44 break; 45 } 46 47 if (method_index_relative < 0) { 48 // check for normalized methods 49 tmp_method_name = QMetaObject::normalizedSignature(method); 50 method = tmp_method_name.constData(); 51 52 // rmeta may have been modified above 53 rmeta = receiver->metaObject(); 54 switch (membcode) { 55 case QSLOT_CODE: 56 method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false); 57 if (method_index_relative < 0) 58 method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true); 59 break; 60 case QSIGNAL_CODE: 61 method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false); 62 if (method_index_relative < 0) 63 method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true); 64 break; 65 } 66 } 67 68 if (method_index_relative < 0) { 69 err_method_notfound(receiver, method_arg, "connect"); 70 err_info_about_objects("connect", sender, receiver); 71 return false; 72 } 73 74 if (!QMetaObject::checkConnectArgs(signal, method)) { 75 qWarning("QObject::connect: Incompatible sender/receiver arguments" 76 "\n %s::%s --> %s::%s", 77 sender->metaObject()->className(), signal, 78 receiver->metaObject()->className(), method); 79 return false; 80 } 81 82 int *types = 0; 83 if ((type == Qt::QueuedConnection) 84 && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes()))) 85 return false; 86 87 #ifndef QT_NO_DEBUG 88 if (warnCompat) { 89 QMetaMethod smethod = smeta->method(signal_absolute_index); 90 QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset()); 91 check_and_warn_compat(smeta, smethod, rmeta, rmethod); 92 } 93 #endif?
若這一切都通過了,調用 QMetaObjectPrivate 的connect成員。調用成功,則調用 sender 對象的 connectNofify 函數(ActiveQt模塊使用了該函數)
1 if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index_relative, rmeta ,type, types)) 2 return false; 3 const_cast<QObject*>(sender)->connectNotify(signal - 1); 4 return true; 5 }?
QMetaObjectPrivate::connect首先,檢測是否是 uniqueconnection ,如果是,則先搜索已有的連接,找到則函數返回
1 bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,2 const QObject *receiver, int method_index,3 const QMetaObject *rmeta, int type, int *types)4 {5 QObject *s = const_cast<QObject *>(sender);6 QObject *r = const_cast<QObject *>(receiver);7 8 int method_offset = rmeta ? rmeta->methodOffset() : 0;9 QObjectPrivate::StaticMetaCallFunction callFunction = 10 (rmeta && QMetaObjectPrivate::get(rmeta)->revision >= 6 && rmeta->d.extradata) 11 ? reinterpret_cast<const QMetaObjectExtraData *>(rmeta->d.extradata)->static_metacall : 0; 12 13 QOrderedMutexLocker locker(signalSlotLock(sender), 14 signalSlotLock(receiver)); 15 16 if (type & Qt::UniqueConnection) { 17 QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists; 18 if (connectionLists && connectionLists->count() > signal_index) { 19 const QObjectPrivate::Connection *c2 = 20 (*connectionLists)[signal_index].first; 21 22 int method_index_absolute = method_index + method_offset; 23 24 while (c2) { 25 if (c2->receiver == receiver && c2->method() == method_index_absolute) 26 return false; 27 c2 = c2->nextConnectionList; 28 } 29 } 30 type &= Qt::UniqueConnection - 1; 31 }?
創建連接
1 QObjectPrivate::Connection *c = new QObjectPrivate::Connection;2 c->sender = s;3 c->receiver = r;4 c->method_relative = method_index;5 c->method_offset = method_offset;6 c->connectionType = type;7 c->argumentTypes = types;8 c->nextConnectionList = 0;9 c->callFunction = callFunction; 10 11 QT_TRY { 12 QObjectPrivate::get(s)->addConnection(signal_index, c); 13 } QT_CATCH(...) { 14 delete c; 15 QT_RETHROW; 16 } 17 18 c->prev = &(QObjectPrivate::get(r)->senders); 19 c->next = *c->prev; 20 *c->prev = c; 21 if (c->next) 22 c->next->prev = &c->next; 23 24 QObjectPrivate *const sender_d = QObjectPrivate::get(s); 25 if (signal_index < 0) { 26 sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0; 27 } else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8) { 28 sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f)); 29 } 30 31 return true; 32 }?
其他還有部分代碼,看不太懂,暫略(應該不影響理解)
?
QMetaObject::activate
信號的定義體在 moc 生成的文件內,在定義體中會調用 QMetaObject::activate 函數來實現信號的功能。
首先判斷該信號有無被鏈接,是否是block設置(具體含義?)
1 void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,2 void **argv)3 {4 int signalOffset;5 int methodOffset;6 computeOffsets(m, &signalOffset, &methodOffset);7 8 int signal_index = signalOffset + local_signal_index;9 10 if (!sender->d_func()->isSignalConnected(signal_index)) 11 return; // nothing connected to these signals, and no spy 12 13 if (sender->d_func()->blockSig) 14 return;?
和signal spy有關的一些代碼
1 int signal_absolute_index = methodOffset + local_signal_index;2 3 void *empty_argv[] = { 0 };4 if (qt_signal_spy_callback_set.signal_begin_callback != 0) {5 qt_signal_spy_callback_set.signal_begin_callback(sender, signal_absolute_index,6 argv ? argv : empty_argv);7 }8 9 Qt::HANDLE currentThreadId = QThread::currentThreadId(); 10 11 QMutexLocker locker(signalSlotLock(sender)); 12 QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists; 13 if (!connectionLists) { 14 locker.unlock(); 15 if (qt_signal_spy_callback_set.signal_end_callback != 0) 16 qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index); 17 return; 18 } 19 ++connectionLists->inUse;?
用雙重循環依次處理每一個信號的每一個連接
1 const QObjectPrivate::ConnectionList *list;2 if (signal_index < connectionLists->count())3 list = &connectionLists->at(signal_index);4 else5 list = &connectionLists->allsignals;6 7 do {8 QObjectPrivate::Connection *c = list->first;9 if (!c) continue;10 // We need to check against last here to ensure that signals added11 // during the signal emission are not emitted in this emission.12 QObjectPrivate::Connection *last = list->last;13 14 do {15 if (!c->receiver)16 continue;17 18 QObject * const receiver = c->receiver;19 const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId;20 21 // determine if this connection should be sent immediately or22 // put into the event queue23 if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)24 || (c->connectionType == Qt::QueuedConnection)) { //對queued的連接單獨處理25 queued_activate(sender, signal_absolute_index, c, argv ? argv : empty_argv);26 continue;27 #ifndef QT_NO_THREAD28 } else if (c->connectionType == Qt::BlockingQueuedConnection) {29 locker.unlock();30 if (receiverInSameThread) {31 qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: "32 "Sender is %s(%p), receiver is %s(%p)",33 sender->metaObject()->className(), sender,34 receiver->metaObject()->className(), receiver);35 }36 QSemaphore semaphore;37 QCoreApplication::postEvent(receiver, new QMetaCallEvent(c->method_offset, c->method_relative,38 c->callFunction,39 sender, signal_absolute_index,40 0, 0,41 argv ? argv : empty_argv,42 &semaphore));43 semaphore.acquire();44 locker.relock();45 continue;46 #endif47 }48 //對其他連接,繼續(發送與接收者在同一線程,則設置當前發送者,QObject::sender() 需要該功能)49 QObjectPrivate::Sender currentSender;50 QObjectPrivate::Sender *previousSender = 0;51 if (receiverInSameThread) {52 currentSender.sender = sender;53 currentSender.signal = signal_absolute_index;54 currentSender.ref = 1;55 previousSender = QObjectPrivate::setCurrentSender(receiver, ¤tSender);56 }57 const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction;58 const int method_relative = c->method_relative;59 if (callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {60 //we compare the vtable to make sure we are not in the destructor of the object.61 locker.unlock();62 if (qt_signal_spy_callback_set.slot_begin_callback != 0)63 qt_signal_spy_callback_set.slot_begin_callback(receiver, c->method(), argv ? argv : empty_argv);64 65 #if defined(QT_NO_EXCEPTIONS)66 callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);67 #else68 QT_TRY {69 callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);70 } QT_CATCH(...) {71 locker.relock();72 if (receiverInSameThread)73 QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender);74 75 --connectionLists->inUse;76 Q_ASSERT(connectionLists->inUse >= 0);77 if (connectionLists->orphaned && !connectionLists->inUse)78 delete connectionLists;79 QT_RETHROW;80 }81 #endif82 if (qt_signal_spy_callback_set.slot_end_callback != 0)83 qt_signal_spy_callback_set.slot_end_callback(receiver, c->method());84 locker.relock();85 } else {86 const int method = method_relative + c->method_offset;87 locker.unlock();88 89 if (qt_signal_spy_callback_set.slot_begin_callback != 0) {90 qt_signal_spy_callback_set.slot_begin_callback(receiver,91 method,92 argv ? argv : empty_argv);93 }94 //調用metacall95 #if defined(QT_NO_EXCEPTIONS)96 metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);97 #else98 QT_TRY {99 metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv); 100 } QT_CATCH(...) { 101 locker.relock(); 102 if (receiverInSameThread) 103 QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender); 104 105 --connectionLists->inUse; 106 Q_ASSERT(connectionLists->inUse >= 0); 107 if (connectionLists->orphaned && !connectionLists->inUse) 108 delete connectionLists; 109 QT_RETHROW; 110 } 111 #endif 112 113 if (qt_signal_spy_callback_set.slot_end_callback != 0) 114 qt_signal_spy_callback_set.slot_end_callback(receiver, method); 115 116 locker.relock(); 117 } 118 119 if (receiverInSameThread) 120 QObjectPrivate::resetCurrentSender(receiver, ¤tSender, previousSender); 121 122 if (connectionLists->orphaned) 123 break; 124 } while (c != last && (c = c->nextConnectionList) != 0); 125 126 if (connectionLists->orphaned) 127 break; 128 } while (list != &connectionLists->allsignals && 129 //start over for all signals; 130 ((list = &connectionLists->allsignals), true)); 131 132 --connectionLists->inUse; 133 Q_ASSERT(connectionLists->inUse >= 0); 134 if (connectionLists->orphaned) { 135 if (!connectionLists->inUse) 136 delete connectionLists; 137 } else if (connectionLists->dirty) { 138 sender->d_func()->cleanConnectionLists(); 139 } 140 141 locker.unlock(); 142 143 if (qt_signal_spy_callback_set.signal_end_callback != 0) 144 qt_signal_spy_callback_set.signal_end_callback(sender, signal_absolute_index); 145 146 }
接下來看metacall代碼
前面的 QAbstractDynamicMetaObject 部分不清楚作用,后面的 qt_metacall 比較好理解了(代碼在moc生成的文件中)
queued_activate
首先是參數檢測,然后是構建 QMetaCallEvent 事件,并post該事件
1 static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv)2 {3 if (!c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) {4 QMetaMethod m = sender->metaObject()->method(signal);5 int *tmp = queuedConnectionTypes(m.parameterTypes());6 if (!tmp) // cannot queue arguments7 tmp = &DIRECT_CONNECTION_ONLY;8 if (!c->argumentTypes.testAndSetOrdered(0, tmp)) {9 if (tmp != &DIRECT_CONNECTION_ONLY) 10 delete [] tmp; 11 } 12 } 13 if (c->argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate 14 return; 15 int nargs = 1; // include return type 16 while (c->argumentTypes[nargs-1]) 17 ++nargs; 18 int *types = (int *) qMalloc(nargs*sizeof(int)); 19 Q_CHECK_PTR(types); 20 void **args = (void **) qMalloc(nargs*sizeof(void *)); 21 Q_CHECK_PTR(args); 22 types[0] = 0; // return type 23 args[0] = 0; // return value 24 for (int n = 1; n < nargs; ++n) 25 args[n] = QMetaType::construct((types[n] = c->argumentTypes[n-1]), argv[n]); 26 QCoreApplication::postEvent(c->receiver, new QMetaCallEvent(c->method_offset, 27 c->method_relative, 28 c->callFunction, 29 sender, signal, nargs, 30 types, args)); 31 }?
blocking_activate
首先,檢測是否是同一線程,然后發送事件 QMetaCallEvent
既然和事件有關系,就看看事件是怎么回事
1 case QEvent::MetaCall:2 {3 #ifdef QT_JAMBI_BUILD4 d_func()->inEventHandler = false;5 #endif6 QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(e);7 QObjectPrivate::Sender currentSender;8 currentSender.sender = const_cast<QObject*>(mce->sender());9 currentSender.signal = mce->signalId(); 10 currentSender.ref = 1; 11 QObjectPrivate::Sender * const previousSender = 12 QObjectPrivate::setCurrentSender(this, ¤tSender); 13 #if defined(QT_NO_EXCEPTIONS) 14 mce->placeMetaCall(this); 15 #else 16 QT_TRY { 17 mce->placeMetaCall(this); 18 } QT_CATCH(...) { 19 QObjectPrivate::resetCurrentSender(this, ¤tSender, previousSender); 20 QT_RETHROW; 21 } 22 #endif 23 QObjectPrivate::resetCurrentSender(this, ¤tSender, previousSender); 24 break; 25 }?
可以看到,這兒調用了 placeMetaCall
1 void QMetaCallEvent::placeMetaCall(QObject *object) 2 { 3 if (callFunction_) { 4 callFunction_(object, QMetaObject::InvokeMetaMethod, method_relative_, args_); 5 } else { 6 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method_offset_ + method_relative_, args_); 7 } 8 }?
參考
-
http://blog.csdn.net/tingsking18/archive/2009/11/12/4800828.aspx
-
http://blog.csdn.net/tingsking18/archive/2009/12/12/4991563.aspx
?
基本理解
- Q_DECLARE_METATYPE
- 如果要使自定義類型或其他非QMetaType內置類型在QVaiant中使用,必須使用該宏。
- 該類型必須有公有的 構造、析構、復制構造 函數
- qRegisterMetaType 必須使用該函數的兩種情況
- 如果非QMetaType內置類型要在 Qt 的屬性系統中使用
- 如果非QMetaType內置類型要在 queued 信號與槽 中使用
二者關系
?
二者的代碼:
-
Q_DECLARE_METATYPE 展開后是一個特化后的類 QMetaTypeId<TYPE>
-
qRegisterMetaType 將某類型注冊中 MetaType 系統中
二者的聯系:
-
QMetaTypeId<TYPE>的類中的成員包含對qRegisterMetaType的調用
-
我們知道類中的成員函數并不一定會被調用(即,該宏并不確保類型被注冊到MetaType)。
- 通過qRegisterMetaType可以確保類型被注冊
兩個qRegisterMetaType 的聯系
- 無參的qRegisterMetaType函數會通過該成員調用帶參數的qRegisterMetaType()
這兩個東西真難理清,不妨看看源碼吧。
1 Q_DECLARE_METATYPE代碼來源:src/corelib/kernel/qmetatype.h
1 #define Q_DECLARE_METATYPE(TYPE) \2 QT_BEGIN_NAMESPACE \3 template <> \4 struct QMetaTypeId< TYPE > \5 { \6 enum { Defined = 1 }; \7 static int qt_metatype_id() \8 { \9 static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \ 10 if (!metatype_id) \ 11 metatype_id = qRegisterMetaType< TYPE >(#TYPE, \ 12 reinterpret_cast< TYPE *>(quintptr(-1))); \ 13 return metatype_id; \ 14 } \ 15 }; \ 16 QT_END_NAMESPACE?
-
宏展開是一個在Qt的命名空間中的一個類模板的特化 QMetaTypeId<TYPE>
- 該類含一個enum和一個返回!QMetaType的id的成員函數
?
qRegisterMetaType(const char *typeName)
代碼來源:src/corelib/kernel/qmetatype.h
1 template <typename T>2 int qRegisterMetaType(const char *typeName3 #ifndef qdoc4 , T * dummy = 05 #endif6 )7 {8 const int typedefOf = dummy ? -1 : QtPrivate::QMetaTypeIdHelper<T>::qt_metatype_id();9 if (typedefOf != -1) 10 return QMetaType::registerTypedef(typeName, typedefOf); 11 12 typedef void*(*ConstructPtr)(const T*); 13 ConstructPtr cptr = qMetaTypeConstructHelper<T>; 14 typedef void(*DeletePtr)(T*); 15 DeletePtr dptr = qMetaTypeDeleteHelper<T>; 16 17 return QMetaType::registerType(typeName, reinterpret_cast<QMetaType::Destructor>(dptr), 18 reinterpret_cast<QMetaType::Constructor>(cptr)); 19 }?
- 該函數的核心就是調用了registerType 函數
- 兩個Helper模板函數分別對構造和析構函數進行封裝
registerType
代碼來源:src/corelib/kernel/qmetatype.cpp
1 int QMetaType::registerType(const char *typeName, Destructor destructor,2 Constructor constructor)3 {4 QVector<QCustomTypeInfo> *ct = customTypes();5 if (!ct || !typeName || !destructor || !constructor)6 return -1;7 8 #ifdef QT_NO_QOBJECT9 NS(QByteArray) normalizedTypeName = typeName; 10 #else 11 NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); 12 #endif 13 14 int idx = qMetaTypeStaticType(normalizedTypeName.constData(), 15 normalizedTypeName.size()); 16 17 if (!idx) { 18 QWriteLocker locker(customTypesLock()); 19 idx = qMetaTypeCustomType_unlocked(normalizedTypeName.constData(), 20 normalizedTypeName.size()); 21 if (!idx) { 22 QCustomTypeInfo inf; 23 inf.typeName = normalizedTypeName; 24 inf.constr = constructor; 25 inf.destr = destructor; 26 inf.alias = -1; 27 idx = ct->size() + User; 28 ct->append(inf); 29 } 30 } 31 return idx; 32 }函數功能:
-
根據類型名查找其MetaType類型,如果已存在,則直接返回;否則創建后返回。
- 創建一個 !QCustomTypeInfo 對象
- 該對象包含要類型的構造、析構信息,已經規范化后的類型名
- 該對象存入一個全局的!QVector中
qRegisterMetaType()
看manual,可以知道,qRegisterMetaType 還有一個無參的重載函數。
1 template <typename T> 2 inline int qRegisterMetaType() 3 { 4 return qMetaTypeId(static_cast<T *>(0)); 5 }?
- 函數看起來和帶參數的那個似乎區別很大(難道不是么?)。
- 手冊中告訴我們,執行這個的時候,模板參數T必須用 Q_DECLARE_METATYPE() 聲明過
- 能猜到原因嗎?注意看前面 Q_DECLARE_METATYPE() 代碼,
- 對了。類中的成員函數qt_metatype_id中包含對qRegisterMetaType(typeName)的調用
-
這兒就是輾轉調用了這個帶參數的qRegisterMetaType函數?
unregisterType(const char *typeName)
函數的作用是取消自己先前注冊的某個metatype類型。
前面提到注冊信息在一個全局的?QVector<QCustomTypeInfo>中,當取消注冊的時候是怎么樣的呢?直接刪除Vector中相應的項么?源碼告訴我們,不是的。
實際是查找到相應的項,清空該項的內容
1 void QMetaType::unregisterType(const char *typeName)2 {3 QVector<QCustomTypeInfo> *ct = customTypes();4 if (!ct || !typeName)5 return;6 7 #ifdef QT_NO_QOBJECT8 NS(QByteArray) normalizedTypeName = typeName;9 #else 10 NS(QByteArray) normalizedTypeName = QMetaObject::normalizedType(typeName); 11 #endif 12 QWriteLocker locker(customTypesLock()); 13 for (int v = 0; v < ct->count(); ++v) { 14 if (ct->at(v).typeName == typeName) { 15 QCustomTypeInfo &inf = (*ct)[v]; 16 inf.typeName.clear(); 17 inf.constr = 0; 18 inf.destr = 0; 19 inf.alias = -1; 20 } 21 } 22 }?
?
Q_DECLARE_PRIVATE與Q_DECLARE_PUBLIC
這兩個宏在Qt的源碼中隨處可見,重要性不言而喻。在 部落格的 Inside Qt Series 系列文章中,他用了3篇文章來講這個問題。
因為 QObject 本身比較復雜,這兩個宏和一個復雜的東西攪和到一塊,還真是不好理解。不過幸好,這個兩個宏和QObject 沒有必然的聯系。故接下來,忘記 QObject,看一個普通的C++的類
例子
類 QtServiceController 定義:
1 class QtServiceController 2 { 3 Q_DECLARE_PRIVATE(QtServiceController) 4 public: 5 QtServiceController(const QString &name); 6 //省略其他 7 private: 8 QtServiceControllerPrivate *d_ptr; 9 };類 QtServiceControllerPrivate 定義:
1 class QtServiceControllerPrivate 2 { 3 Q_DECLARE_PUBLIC(QtServiceController) 4 public: 5 QString serviceName; 6 QtServiceController *q_ptr; 7 };將所有的private數據成員,獨立出來放于一個獨立的私有的數據對象中。這一點是比較好理解的,那么這兩個宏在這起什么作用呢?
注意:上面定義的兩個指針 d_ptr, q_ptr。
宏定義
宏定義在 QtGlobal(即qglobal.h)頭文件中:
1 #define Q_DECLARE_PRIVATE(Class) \2 inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(qGetPtrHelper(d_ptr)); } \3 inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(qGetPtrHelper(d_ptr)); } \4 friend class Class##Private;5 6 #define Q_DECLARE_PRIVATE_D(Dptr, Class) \7 inline Class##Private* d_func() { return reinterpret_cast<Class##Private *>(Dptr); } \8 inline const Class##Private* d_func() const { return reinterpret_cast<const Class##Private *>(Dptr); } \9 friend class Class##Private; 10 11 #define Q_DECLARE_PUBLIC(Class) \ 12 inline Class* q_func() { return static_cast<Class *>(q_ptr); } \ 13 inline const Class* q_func() const { return static_cast<const Class *>(q_ptr); } \ 14 friend class Class;這兩個宏在這看起來真蠻繞的,因為這個例子太簡單了,兩個宏的威力發揮不出來。反正核心就是:
-
在 QtServiceController 中通過 d_func() 可以獲得 QtServiceControllerPrivate 的指針 d_ptr
-
在 QtServiceControllerPrivate 中通過 q_func() 可以獲得 QtServiceController 的指針 q_ptr
Q_D 與 Q_Q
這是另兩個Qt源碼中隨處可見的宏,那么它們有什么用呢?
1 #define Q_D(Class) Class##Private * const d = d_func() 2 #define Q_Q(Class) Class * const q = q_func()兩個宏展開后分別是對 d_func 和 q_func 兩個函數的調用,返回值分別賦值給 d 和 q 兩個指針變量。
于是:
-
在 QtServiceController 中的成員函數中,我們只需要添加 Q_D(QtServiceController) 宏,在該函數內就可以直接用 d 來指代 d_ptr
-
在 QtServiceControllerPrivate 中的成員函數中,我們只需要添加 Q_Q(QtServiceController)宏,在該函數內就可以直接用 q 來指代 q_ptr
d_ptr與q_ptr
繞這么大圈,為什么不直接用 d_ptr 與 q_ptr 呢。在,在我們的例子中,確實可以直接用,而且會更直接更簡單。官方這么用了,或許是為了和其他類保持一致吧。
但在其他情況下,這么做顯然是有意義的,因為 d_ptr 與 d,q_ptr 與 q 的類型并不一致(比如QObject系列)。這也是為何宏展開后有cast的原因。
參考
-
http://www.qkevin.com/archives/31
-
http://doc.qt.nokia.com/solutions/4/qtservice/qtservicebase.html
?
C++ GUI Qt4 編程?一書多線程部分提到invokeMethod的用法
QMetaObject::invokeMethod(label, SLOT(setText(const QString&)), Q_ARG(QString, "Hello"));
而 Qt Manual 中介紹卻是
- You only need to pass the name of the signal or slot to this function, not the entire signature. For example, to asynchronously invoke the animateClick() slot on a QPushButton, use the following code:
?
QMetaObject::invokeMethod(pushButton, "animateClick");
這可怎么辦?一個是官方的圖書,一個是官方的Manual。是否意味著兩種方式都可以呢,還是說Qt的早期版本用的是前者?
查 Qt4.7/Qt4.6/Qt4.5/Qt4.4/Qt4.3/Qt4.2/Qt4.1/Qt4.0 ,結果發現都沒有提到前面的用法。是不是書的出錯呢?網上搜一下:確實有人抱怨它不工作
測試
本著事實就是的精神,還是先寫個程序測試一下:
1 #include <QtCore/QObject>2 #include <QtCore/QDebug>3 #include <QtCore/QCoreApplication>4 class Test : public QObject5 {6 Q_OBJECT7 public:8 Test(QObject * parent):QObject(parent)9 { 10 connect(this, SIGNAL(sig1(QString)), SLOT(slot1(QString))); 11 QMetaObject::invokeMethod(this, "sig1", Q_ARG(QString, "constructor")); 12 } 13 Q_INVOKABLE void method1(const QString& t) 14 { 15 qDebug()<<"from method:"<<t; 16 } 17 18 signals: 19 void sig1(const QString& t); 20 21 public slots: 22 void slot1(const QString& t) 23 { 24 qDebug()<<"from slot:"<<t; 25 } 26 }; 27 #include "main.moc" 28 int main(int argc, char *argv[]) 29 { 30 QCoreApplication a(argc, argv); 31 Test obj(0); 32 QMetaObject::invokeMethod(&obj, "slot1", Q_ARG(QString, "Hello")); 33 QMetaObject::invokeMethod(&obj, "method1", Q_ARG(QString, "Hello")); 34 QMetaObject::invokeMethod(&obj, SLOT(slot1(QString)), Q_ARG(QString, "Hello with SLOT")); 35 QMetaObject::invokeMethod(&obj, METHOD(method1(QString)), Q_ARG(QString, "Hello with METHOD")); 36 return a.exec(); 37 }確實如他人所說,SLOT這種用法不工作
1 from slot: "constructor" 2 from slot: "Hello" 3 from method: "Hello" 4 QMetaObject::invokeMethod: No such method Test::1slot1(QString)(QString) 5 QMetaObject::invokeMethod: No such method Test::0method1(QString)(QString)順便看看源碼吧
1 bool QMetaObject::invokeMethod(QObject *obj,2 const char *member,3 Qt::ConnectionType type,4 QGenericReturnArgument ret,5 QGenericArgument val0,6 QGenericArgument val1,7 QGenericArgument val2,8 QGenericArgument val3,9 QGenericArgument val4, 10 QGenericArgument val5, 11 QGenericArgument val6, 12 QGenericArgument val7, 13 QGenericArgument val8, 14 QGenericArgument val9) 15 { 16 if (!obj) 17 return false; 18 19 QVarLengthArray<char, 512> sig; 20 int len = qstrlen(member); 21 if (len <= 0) 22 return false;//生成函數原型字符串(從這兒可以看到書中方法不工作的原因) 23 sig.append(member, len); 24 sig.append('('); 25 26 const char *typeNames[] = {ret.name(), val0.name(), val1.name(), val2.name(), val3.name(), 27 val4.name(), val5.name(), val6.name(), val7.name(), val8.name(), 28 val9.name()}; 29 30 int paramCount; 31 for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) { 32 len = qstrlen(typeNames[paramCount]); 33 if (len <= 0) 34 break; 35 sig.append(typeNames[paramCount], len); 36 sig.append(','); 37 } 38 if (paramCount == 1) 39 sig.append(')'); // no parameters 40 else 41 sig[sig.size() - 1] = ')'; 42 sig.append('\0'); 43 //在元對象系統中看該函數信息是否存在 44 int idx = obj->metaObject()->indexOfMethod(sig.constData()); 45 if (idx < 0) { 46 QByteArray norm = QMetaObject::normalizedSignature(sig.constData()); 47 idx = obj->metaObject()->indexOfMethod(norm.constData()); 48 } 49 50 if (idx < 0 || idx >= obj->metaObject()->methodCount()) { 51 qWarning("QMetaObject::invokeMethod: No such method %s::%s", 52 obj->metaObject()->className(), sig.constData()); 53 return false; 54 }
//獲得相應的 QMetaMethod,調用其 invoke 方法 55 QMetaMethod method = obj->metaObject()->method(idx); 56 return method.invoke(obj, type, ret, 57 val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); 58 }
?
接著看看它的源碼,首先:
* 如果指定了返回值,檢查返回值的類型是否和QMetaMethod 的中的一致
1 bool QMetaMethod::invoke(QObject *object,2 Qt::ConnectionType connectionType,3 QGenericReturnArgument returnValue,4 QGenericArgument val0,5 QGenericArgument val1,6 QGenericArgument val2,7 QGenericArgument val3,8 QGenericArgument val4,9 QGenericArgument val5,10 QGenericArgument val6,11 QGenericArgument val7,12 QGenericArgument val8,13 QGenericArgument val9) const14 {15 if (!object || !mobj)16 return false;17 18 Q_ASSERT(mobj->cast(object));19 20 // check return type21 if (returnValue.data()) {22 const char *retType = typeName();23 if (qstrcmp(returnValue.name(), retType) != 0) {24 // normalize the return value as well25 // the trick here is to make a function signature out of the return type26 // so that we can call normalizedSignature() and avoid duplicating code27 QByteArray unnormalized;28 int len = qstrlen(returnValue.name());29 30 unnormalized.reserve(len + 3);31 unnormalized = "_("; // the function is called "_"32 unnormalized.append(returnValue.name());33 unnormalized.append(')');34 35 QByteArray normalized = QMetaObject::normalizedSignature(unnormalized.constData());36 normalized.truncate(normalized.length() - 1); // drop the ending ')'37 38 if (qstrcmp(normalized.constData() + 2, retType) != 0)39 return false;40 }41 }42 43 // check argument count (we don't allow invoking a method if given too few arguments)44 const char *typeNames[] = {45 returnValue.name(),46 val0.name(),47 val1.name(),48 val2.name(),49 val3.name(),50 val4.name(),51 val5.name(),52 val6.name(),53 val7.name(),54 val8.name(),55 val9.name()56 };57 int paramCount;58 for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) {59 if (qstrlen(typeNames[paramCount]) <= 0)60 break;61 }62 int metaMethodArgumentCount = 0;63 {64 // based on QMetaObject::parameterNames()65 const char *names = mobj->d.stringdata + mobj->d.data[handle + 1];66 if (*names == 0) {67 // do we have one or zero arguments?68 const char *signature = mobj->d.stringdata + mobj->d.data[handle];69 while (*signature && *signature != '(')70 ++signature;71 if (*++signature != ')')72 ++metaMethodArgumentCount;73 } else {74 --names;75 do {76 ++names;77 while (*names && *names != ',')78 ++names;79 ++metaMethodArgumentCount;80 } while (*names);81 }82 }83 if (paramCount <= metaMethodArgumentCount)84 return false;85//為了利用現有的代碼來規范化這兒返回值的類型,這兒構造了一個函數_(typeOfReturn)。
????????? //檢查參數個數,傳遞的參數是否不少于需要的參數
????????? //檢查Connection的類型,處理AutoConnection
86 // check connection type87 QThread *currentThread = QThread::currentThread();88 QThread *objectThread = object->thread();89 if (connectionType == Qt::AutoConnection) {90 connectionType = currentThread == objectThread91 ? Qt::DirectConnection92 : Qt::QueuedConnection;93 }94 95 #ifdef QT_NO_THREAD96 if (connectionType == Qt::BlockingQueuedConnection) {97 connectionType = Qt::DirectConnection;98 }99 #endif 100 101 // invoke! 102 void *param[] = { 103 returnValue.data(), 104 val0.data(), 105 val1.data(), 106 val2.data(), 107 val3.data(), 108 val4.data(), 109 val5.data(), 110 val6.data(), 111 val7.data(), 112 val8.data(), 113 val9.data() 114 }; 115 // recompute the methodIndex by reversing the arithmetic in QMetaObject::property() 116 int idx_relative = ((handle - priv(mobj->d.data)->methodData) / 5); 117 int idx_offset = mobj->methodOffset(); 118 QObjectPrivate::StaticMetaCallFunction callFunction = 119 (QMetaObjectPrivate::get(mobj)->revision >= 6 && mobj->d.extradata) 120 ? reinterpret_cast<const QMetaObjectExtraData *>(mobj->d.extradata)->static_metacall : 0; 121 //對于 直連的,直接調 metacall,它進而去調用對象的 qt_metacall 122 if (connectionType == Qt::DirectConnection) { 123 if (callFunction) { 124 callFunction(object, QMetaObject::InvokeMetaMethod, idx_relative, param); 125 return true; 126 } else { 127 return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, idx_relative + idx_offset, param) < 0; 128 } 129 } else if (connectionType == Qt::QueuedConnection) { //對于 Queued 的連接,post 相應的事件,進而轉到對象的event()函數中 130 if (returnValue.data()) { 131 qWarning("QMetaMethod::invoke: Unable to invoke methods with return values in " 132 "queued connections"); 133 return false; 134 } 135 136 int nargs = 1; // include return type 137 void **args = (void **) qMalloc(paramCount * sizeof(void *)); 138 Q_CHECK_PTR(args); 139 int *types = (int *) qMalloc(paramCount * sizeof(int)); 140 Q_CHECK_PTR(types); 141 types[0] = 0; // return type 142 args[0] = 0; 143 144 for (int i = 1; i < paramCount; ++i) { 145 types[i] = QMetaType::type(typeNames[i]); 146 if (types[i]) { 147 args[i] = QMetaType::construct(types[i], param[i]); 148 ++nargs; 149 } else if (param[i]) { 150 qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'", 151 typeNames[i]); 152 for (int x = 1; x < i; ++x) { 153 if (types[x] && args[x]) 154 QMetaType::destroy(types[x], args[x]); 155 } 156 qFree(types); 157 qFree(args); 158 return false; 159 } 160 } 161 162 QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction, 163 0, -1, nargs, types, args)); 164 } else { // blocking queued connection 165 #ifndef QT_NO_THREAD 166 if (currentThread == objectThread) { 167 qWarning("QMetaMethod::invoke: Dead lock detected in " 168 "BlockingQueuedConnection: Receiver is %s(%p)", 169 mobj->className(), object); 170 } 171 //對于 bolckedqueued 的連接,使用了信號量 172 QSemaphore semaphore; 173 QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction, 174 0, -1, 0, 0, param, &semaphore)); 175 semaphore.acquire(); 176 #endif // QT_NO_THREAD 177 } 178 return true; 179 }?
轉自:http://www.cnblogs.com/lfsblack/p/5284602.html轉載于:https://www.cnblogs.com/liushui-sky/p/6474085.html
總結
以上是生活随笔為你收集整理的6、Qt Meta Object system 学习的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CSS之引入方式和选择器
- 下一篇: 07:配对碱基链