原文地址:http://blog.csdn.net/ilvu999/article/details/8049908html
使用 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 程序對該宏的處理
宏定義app
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函數async
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 處理ide
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 QMetaObject
2 {
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_TRANSLATION
10 // ### Qt 4: Merge overloads
11 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_TRANSLATION
16
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 connect
48 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 disconnect
52 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 connect
57 static void connectSlotsByName(QObject *o);
58
59 // internal index-based signal activation
60 static void activate(QObject *sender, int signal_index, void **argv); //obsolete
61 static void activate(QObject *sender, int from_signal_index, int to_signal_index, void **argv); //obsolete
62 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); //obsolete
64
65 // internal guarded pointers
66 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 };
其中呢,
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的數據成員看看,是否是很簡單:
uint數組
接下來咱們能夠看看 uint 數組,這個數組中存放的是一些索引值,來指導咱們從char字符串中提取信息
1 static const uint qt_meta_data_HPixmapScene[] = {
2
3 // content:
4 4, // revision
5 0, // classname
6 0, 0, // classinfo
7 2, 14, // methods
8 0, 0, // properties
9 0, 0, // enums/sets
10 0, 0, // constructors
11 0, // flags
12 0, // signalCount
13
14 // slots: signature, parameters, type, tag, flags
15 18, 14, 13, 13, 0x0a,
16 41, 37, 13, 13, 0x0a,
17
18 0 // eod
19 };
對比QMetaObject用到的數據結構 QMetaObjectPrivate,看看,是否是豁然開朗了:uint 數組中的第一段 就對應這個結構體
1 struct QMetaObjectPrivate
2 {
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,我不清楚該宏起什麼做用的。
整理思路太累了,寫完這個,還不清楚何時再寫(二)
參考
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能夠讀取或設置屬性
Q_PROPERTY
Qt屬性是靠 Q_PROPERTY 宏進行聲明的
1 Q_PROPERTY(type name READ getFunction
2 [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文件:
自測以下-->
源碼:
結果:
動態屬性
經過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 type
4
5 type of the property
6
7 fget
8
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 = startval
4 def readPP(self):
5 return self.ppval
6 def setPP(self,val):
7 self.ppval = val
8 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 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 code
2 int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false);
3 if (signal_index < 0) {
4 // check for normalized signatures
5 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 else
5 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 added
11 // 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 or
22 // put into the event queue
23 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_THREAD
28 } 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 #endif
47 }
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 #else
68 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 #endif
82 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 //調用metacall
95 #if defined(QT_NO_EXCEPTIONS)
96 metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
97 #else
98 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代碼
1 int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv)
2 {
3 if (QMetaObject *mo = object->d_ptr->metaObject)
4 return static_cast<QAbstractDynamicMetaObject*>(mo)->metaCall(cl, idx, argv);
5 else
6 return object->qt_metacall(cl, idx, argv);
7 }
前面的 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 arguments
7 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_BUILD
4 d_func()->inEventHandler = false;
5 #endif
6 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 }
參考
基本理解
- Q_DECLARE_METATYPE
- 若是要使自定義類型或其餘非QMetaType內置類型在QVaiant中使用,必須使用該宏。
- 該類型必須有公有的 構造、析構、複製構造 函數
- qRegisterMetaType 必須使用該函數的兩種狀況
- 若是非QMetaType內置類型要在 Qt 的屬性系統中使用
- 若是非QMetaType內置類型要在 queued 信號與槽 中使用
兩者關係
兩者的代碼:
兩者的聯繫:
兩個qRegisterMetaType 的聯繫
- 無參的qRegisterMetaType函數會經過該成員調用帶參數的qRegisterMetaType()
這兩個東西真難理清,不妨看看源碼吧。
代碼來源: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
qRegisterMetaType(const char *typeName)
代碼來源:src/corelib/kernel/qmetatype.h
1 template <typename T>
2 int qRegisterMetaType(const char *typeName
3 #ifndef qdoc
4 , T * dummy = 0
5 #endif
6 )
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_QOBJECT
9 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 }
函數功能:
qRegisterMetaType()
看manual,能夠知道,qRegisterMetaType 還有一個無參的重載函數。
1 template <typename T>
2 inline int qRegisterMetaType()
3 {
4 return qMetaTypeId(static_cast<T *>(0));
5 }
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_QOBJECT
8 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;
這兩個宏在這看起來真蠻繞的,由於這個例子太簡單了,兩個宏的威力發揮不出來。反正核心就是:
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 兩個指針變量。
因而:
d_ptr與q_ptr
繞這麼大圈,爲何不直接用 d_ptr 與 q_ptr 呢。在,在咱們的例子中,確實能夠直接用,並且會更直接更簡單。官方這麼用了,或許是爲了和其餘類保持一致吧。
但在其餘狀況下,這麼作顯然是有意義的,由於 d_ptr 與 d,q_ptr 與 q 的類型並不一致(好比QObject系列)。這也是爲什麼宏展開後有cast的緣由。
參考
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 QObject
5 {
6 Q_OBJECT
7 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) const
14 {
15 if (!object || !mobj)
16 return false;
17
18 Q_ASSERT(mobj->cast(object));
19
20 // check return type
21 if (returnValue.data()) {
22 const char *retType = typeName();
23 if (qstrcmp(returnValue.name(), retType) != 0) {
24 // normalize the return value as well
25 // the trick here is to make a function signature out of the return type
26 // so that we can call normalizedSignature() and avoid duplicating code
27 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 type
87 QThread *currentThread = QThread::currentThread();
88 QThread *objectThread = object->thread();
89 if (connectionType == Qt::AutoConnection) {
90 connectionType = currentThread == objectThread
91 ? Qt::DirectConnection
92 : Qt::QueuedConnection;
93 }
94
95 #ifdef QT_NO_THREAD
96 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 }