原文地址: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 }