6、Qt Meta Object system 学习
原文地址:http://blog.csdn.net/ilvu999/article/details/8049908
使用 meta object system
- 继承自 QOject
- 类定义中添加 Q_OBJECT 宏
- 使用 moc 程序对包含该宏的文件进行处理
采用 qmake 进行处理时,如果头文件xxx.h内包含 Q_OBJECT 宏,将生成 moc_xxx.cpp 文件。如果xxx.cpp文件内包含宏,将生成 xxx.moc 文件(这时,我们需要在xxx.cpp文件内添加 #include"xxx.moc")
Q_OBJECT宏
包括两个方面,
- 该宏在C++中的展开,有编译预处理器完成
- moc 程序对该宏的处理
宏定义
1 #define Q_OBJECT \
2 public: \
3 Q_OBJECT_CHECK \
4 static const QMetaObject staticMetaObject; \
5 Q_OBJECT_GETSTATICMETAOBJECT \
6 virtual const QMetaObject *metaObject() const; \
7 virtual void *qt_metacast(const char *); \
8 QT_TR_FUNCTIONS \
9 virtual int qt_metacall(QMetaObject::Call, int, void **); \
10 private: \
11 Q_DECL_HIDDEN static const QMetaObjectExtraData staticMetaObjectExtraData; \
12 Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);
而宏 QT_TR_FUNCTIONS 将展开为我们使程序国际化经常使用的 tr 与 trUtf8函数
1 # define QT_TR_FUNCTIONS \
2 static inline QString tr(const char *s, const char *c = 0) \
3 { return staticMetaObject.tr(s, c); } \
4 static inline QString trUtf8(const char *s, const char *c = 0) \
5 { return staticMetaObject.trUtf8(s, c); } \
6 static inline QString tr(const char *s, const char *c, int n) \
7 { return staticMetaObject.tr(s, c, n); } \
8 static inline QString trUtf8(const char *s, const char *c, int n) \
9 { return staticMetaObject.trUtf8(s, c, n); }
moc 处理
Q_OBJECT 为我们添加了这么多成员函数,而这些函数我们有没有自己去实现,那么其定义在哪儿呢? 这就是 moc 为我们做的,自动生成这些成员函数的定义,放于生成的 xxx.moc 或 moc_xxx.cpp 文件内
注意:两个文件的区别(如果你用cmake或其他工具的话,这点可能很重要)
- 生成的 moc_xxx.cpp 中会自动包含 xxx.h 头文件,所以它可以被独立编译成目标文件(.o 或 .obj)
- 生成的 xxx.moc 是不会包含 xxx.cpp 的(要是包含就出问题了,能发现吧?),因此 xxx.moc 中没有类定义,它无法被独立编译,只能被 include 到 xxx.cpp 中。
QMetaObject
既然 Q_OBJECT 展开成与 QMetaObject 有关的成员函数,看一下QMetaObject 都提供哪些常用功能
- className() 返回类的名字
- superClass() 返回父类的 QMetaObject 对象
- method()与methodCount() 提供meta函数信息(包括signals, slots 与 invokable函数)
- enumerator()与 enumeratorCount() 提供enum类型信息
- propertyCount()与 property() 提供属性信息
- constructor()与 constructorCount() 提供 meta-constructors 信息
既然meta object能为我们的类提供这么多信息,那么这些信息存放哪儿了(大家肯定都能猜到秘密在moc生成的文件内,但为清楚起见,我们看一下QMetaObject的定义)。
这是用 struct 定义的一个类,我们略过其它,只看其数据成员
1 struct Q_CORE_EXPORT 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 };
其中呢,
uperdata,指向父类的MetaObject,容易理解
- extradata 似乎目前未启用,不用理解
- 剩下两个是什么呢? 如你所想,就在 moc 生成的文件内
moc生成的文件
随便找个 moc 文件出来看看
1 static const uint qt_meta_data_HPixmapScene[] = {...};
2 static const char qt_meta_stringdata_HPixmapScene[] = {...};
3 const QMetaObject HPixmapScene::staticMetaObject = {
4 { &QGraphicsScene::staticMetaObject, qt_meta_stringdata_HPixmapScene,
5 qt_meta_data_HPixmapScene, 0 }
6 };
这是一个QGraphicsScene的子类。对比前面QMetaObject的数据成员看看,是不是很简单:
- 先分别定义1个 uint 和 char 的数组,
用这两个数组首地址和父类的MetaObject的指针初始化 staticMetaObject
- 这个 staticMetaObject 是我们自己的类的静态成员变量
uint数组
接下来我们可以看看 uint 数组,这个数组中存放的是一些索引值,来指导我们从char字符串中提取信息
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可以读取或设置属性
- property
- setProperty
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文件:
索引数据在 static const uint qt_meta_data_QtColorPicker[]
字符串数据在 static const char qt_meta_stringdata_QtColorPicker[]
- 并在 qt_metacall 函数中生成有相关的代码
自测如下-->
源码:
结果:
动态属性
通过QObject的setProperty 设置属性时,如果该属性不存在,则为设置为动态属性。动态属性存储在QObject中,而不是其QMetaObject对象中。
类型
- 对于 enum 类型,需要用 Q_ENUMS 或Q_FLAGS 将其注册到 meta-object系统中
- 对自定义类型,如果要用作属性,需要用 Q_DECLARE_METATYPE()将其注册到meta-object系统中。
对 PySide 及 PyQt4
1 QtCore.pyqtProperty(type, fget=None, fset=None, freset=None, fdel=None, doc=None, designable=True, scriptable=True, stored=True, user=False, constant=False, final=False)
2
3 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 QObject::connect
非常重要的函数,不是么?
函数一开始是这么一段:
1 bool QObject::connect(const QObject *sender, const char *signal,
2 const QObject *receiver, const char *method,
3 Qt::ConnectionType type)
4 {
5 {
6 const void *cbdata[] = { sender, signal, receiver, method, &type };
7 if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
8 return true;
9 }
10
11 。。。。。。
12 }
这个复合语句的作用看不太懂,QInternal类的定义在 src/corelib/global/qnamespace.h 中,类成员的实现在 src/corelib/global/qglobal.cpp 中。
1 bool QInternal::activateCallbacks(Callback cb, void **parameters)
2 {
3 Q_ASSERT_X(cb >= 0, "QInternal::activateCallback()", "Callback id must be a valid id");
4
5 QInternal_CallBackTable *cbt = global_callback_table();
6 if (cbt && cb < cbt->callbacks.size()) {
7 QList<qInternalCallback> callbacks = cbt->callbacks[cb];
8 bool ret = false;
9 for (int i=0; i<callbacks.size(); ++i)
10 ret |= (callbacks.at(i))(parameters);
11 return ret;
12 }
13 return false;
14 }
然后检测4个参数是否都非空
1 if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
2 qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
3 sender ? sender->metaObject()->className() : "(null)",
4 (signal && *signal) ? signal+1 : "(null)",
5 receiver ? receiver->metaObject()->className() : "(null)",
6 (method && *method) ? method+1 : "(null)");
7 return false;
8 }
然后
1 QByteArray tmp_signal_name;
2
3 if (!check_signal_macro(sender, signal, "connect", "bind"))
4 return false;
5 const QMetaObject *smeta = sender->metaObject();
- 检测与信号连接的“信号或槽”是否符合格式,然后从元对象系统中获得相应的索引
检测信号与槽的参数是否匹配,对与Queued连接,检测参数是否注册到元对象系统
1 ++signal; //skip 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 }
参考
http://blog.csdn.net/tingsking18/archive/2009/11/12/4800828.aspx
http://blog.csdn.net/tingsking18/archive/2009/12/12/4991563.aspx
基本理解
- Q_DECLARE_METATYPE
- 如果要使自定义类型或其他非QMetaType内置类型在QVaiant中使用,必须使用该宏。
- 该类型必须有公有的 构造、析构、复制构造 函数
- qRegisterMetaType 必须使用该函数的两种情况
- 如果非QMetaType内置类型要在 Qt 的属性系统中使用
- 如果非QMetaType内置类型要在 queued 信号与槽 中使用
二者关系
二者的代码:
Q_DECLARE_METATYPE 展开后是一个特化后的类 QMetaTypeId<TYPE>
qRegisterMetaType 将某类型注册中 MetaType 系统中
二者的联系:
QMetaTypeId<TYPE>的类中的成员包含对qRegisterMetaType的调用
我们知道类中的成员函数并不一定会被调用(即,该宏并不确保类型被注册到MetaType)。
- 通过qRegisterMetaType可以确保类型被注册
两个qRegisterMetaType 的联系
- 无参的qRegisterMetaType函数会通过该成员调用带参数的qRegisterMetaType()
这两个东西真难理清,不妨看看源码吧。
1 Q_DECLARE_METATYPE
代码来源:src/corelib/kernel/qmetatype.h
1 #define Q_DECLARE_METATYPE(TYPE) \
2 QT_BEGIN_NAMESPACE \
3 template <> \
4 struct QMetaTypeId< TYPE > \
5 { \
6 enum { Defined = 1 }; \
7 static int qt_metatype_id() \
8 { \
9 static QBasicAtomicInt metatype_id = Q_BASIC_ATOMIC_INITIALIZER(0); \
10 if (!metatype_id) \
11 metatype_id = qRegisterMetaType< TYPE >(#TYPE, \
12 reinterpret_cast< TYPE *>(quintptr(-1))); \
13 return metatype_id; \
14 } \
15 }; \
16 QT_END_NAMESPACE
宏展开是一个在Qt的命名空间中的一个类模板的特化 QMetaTypeId<TYPE>
- 该类含一个enum和一个返回!QMetaType的id的成员函数
qRegisterMetaType(const char *typeName)
代码来源:src/corelib/kernel/qmetatype.h
1 template <typename T>
2 int qRegisterMetaType(const char *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 }
函数功能:
根据类型名查找其MetaType类型,如果已存在,则直接返回;否则创建后返回。
- 创建一个 !QCustomTypeInfo 对象
- 该对象包含要类型的构造、析构信息,已经规范化后的类型名
- 该对象存入一个全局的!QVector中
qRegisterMetaType()
看manual,可以知道,qRegisterMetaType 还有一个无参的重载函数。
1 template <typename T>
2 inline int qRegisterMetaType()
3 {
4 return qMetaTypeId(static_cast<T *>(0));
5 }
- 函数看起来和带参数的那个似乎区别很大(难道不是么?)。
- 手册中告诉我们,执行这个的时候,模板参数T必须用 Q_DECLARE_METATYPE() 声明过
- 能猜到原因吗?注意看前面 Q_DECLARE_METATYPE() 代码,
- 对了。类中的成员函数qt_metatype_id中包含对qRegisterMetaType(typeName)的调用
这儿就是辗转调用了这个带参数的qRegisterMetaType函数
unregisterType(const char *typeName)
函数的作用是取消自己先前注册的某个metatype类型。
前面提到注册信息在一个全局的 QVector<QCustomTypeInfo>中,当取消注册的时候是怎么样的呢?直接删除Vector中相应的项么?源码告诉我们,不是的。
实际是查找到相应的项,清空该项的内容
1 void QMetaType::unregisterType(const char *typeName)
2 {
3 QVector<QCustomTypeInfo> *ct = customTypes();
4 if (!ct || !typeName)
5 return;
6
7 #ifdef QT_NO_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;
这两个宏在这看起来真蛮绕的,因为这个例子太简单了,两个宏的威力发挥不出来。反正核心就是:
在 QtServiceController 中通过 d_func() 可以获得 QtServiceControllerPrivate 的指针 d_ptr
在 QtServiceControllerPrivate 中通过 q_func() 可以获得 QtServiceController 的指针 q_ptr
Q_D 与 Q_Q
这是另两个Qt源码中随处可见的宏,那么它们有什么用呢?
1 #define Q_D(Class) Class##Private * const d = d_func()
2 #define Q_Q(Class) Class * const q = q_func()
两个宏展开后分别是对 d_func 和 q_func 两个函数的调用,返回值分别赋值给 d 和 q 两个指针变量。
于是:
在 QtServiceController 中的成员函数中,我们只需要添加 Q_D(QtServiceController) 宏,在该函数内就可以直接用 d 来指代 d_ptr
在 QtServiceControllerPrivate 中的成员函数中,我们只需要添加 Q_Q(QtServiceController)宏,在该函数内就可以直接用 q 来指代 q_ptr
d_ptr与q_ptr
绕这么大圈,为什么不直接用 d_ptr 与 q_ptr 呢。在,在我们的例子中,确实可以直接用,而且会更直接更简单。官方这么用了,或许是为了和其他类保持一致吧。
但在其他情况下,这么做显然是有意义的,因为 d_ptr 与 d,q_ptr 与 q 的类型并不一致(比如QObject系列)。这也是为何宏展开后有cast的原因。
参考
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 }
6、Qt Meta Object system 学习的更多相关文章
- Qt Meta Object system 学习
原文地址:http://blog.csdn.net/ilvu999/article/details/8049908 使用 meta object system 继承自 QOject 类定义中添加 Q_ ...
- Qt Meta Object System-元对象系统
研一的时候开始使用Qt,感觉用Qt开发图形界面比MFC的一套框架来方便的多.后来由于项目的需要,也没有再接触Qt了.现在要重新拾起来,于是要从基础学起. Now,开始学习Qt事件处理机制. 元对象系统 ...
- Qt Creator 源码学习笔记04,多插件实现原理分析
阅读本文大概需要 8 分钟 插件听上去很高大上,实际上就是一个个动态库,动态库在不同平台下后缀名不一样,比如在 Windows下以.dll结尾,Linux 下以.so结尾 开发插件其实就是开发一个动态 ...
- Object C学习笔记24-关键字总结
学习Object C也有段时间了,学习的过程中涉及到了很多Object C中的关键字,本文总结一下所涉及到的关键字以及基本语法. 1. #import #import <> 从syste ...
- Object C学习笔记24-关键字总结(转)
学习Object C也有段时间了,学习的过程中涉及到了很多Object C中的关键字,本文总结一下所涉及到的关键字以及基本语法. 1. #import #import <> 从syste ...
- Qt Installer Framework的学习(三)
Qt Installer Framework的学习(三) Qt Installer Framework的样例中.通常是这种:config目录一般放了一个config.xml文件,包括的是安装配置xml ...
- 启用SQLite的Data Provider 运行WECOMPANYSITE时遇到ERROR CREATING CONTEXT 'SPRING.ROOT': ERROR THROWN BY A DEPENDENCY OF OBJECT 'SYSTEM.DATA.SQLITE'
从网上下载的源码WeCompanySite,运行时报错 Error creating context 'spring.root': Error thrown by a dependency of ob ...
- Object C学习笔记22-#define 用法
上一篇讲到了typedef 关键字的使用,可以参考文章 Object C 学习笔记--typedef用法 .而在c中还有另外一个很重要的关键字#define. 一. #define 简介 在C中利用预 ...
- Object C学习笔记21-typedef用法
在上一章的学习过程中遇到了一个关键字typedef,这个关键字是C语言中的关键字,因为Object C是C的扩展同样也是支持typedef的. 一. 基本作用 typedef是C中的关键字,它的主要作 ...
随机推荐
- cygwin下安装c语言开发环境
1.到官网cygwin.com下载安装程序. 2.添加清华的cygwin镜像:https://mirrors.tuna.tsinghua.edu.cn/cygwin/ 3.在安装:vim,git,gc ...
- 【Android】1.0 第1章 C#之Android手机App开发
分类:C#.Android.VS2015:创建日期:2016-01-20 目前Android在全世界市场上大约有75%的占有率,国人Android手机的持有比例更甚,甚至达到90%以上.因此搞计算机的 ...
- xcode代码没颜色的解决方案
转自:http://blog.csdn.net/jiarusun000/article/details/7527631 今天代码使用了svn后发现项目中的所有原文件都没颜色了... 查找半天后发现是因 ...
- [CoreOS 转载] CoreOS实践指南(三):系统服务管家Systemd
转载:http://www.csdn.net/article/2015-01-08/2823477 摘要:CoreOS是采用了高度精简的系统内核及外围定制的操作系统.ThoughtWorks的软件工程 ...
- 每日英语:China's Wistful Wen Gets His Wish
As his term as premier was drawing to a close, Wen Jiabao reflected wistfully on some of the goals h ...
- 让mysql查询强制走索引
select * from slot_value_temp force index(idx_slot_type_id) WHERE slot_type_id = 'xxxxxx'; 不定期更新
- HP管理工具System Management Homepage安装配置
如果用HP的启动光盘进行系统安装时,HP SMH 及相关组件会自动安装完成,但如果是用操作系统光盘进行系统安装,则HP SMH相关组件需要手动进行安装及相关设置!HP SMH由三部分组成:HP Sys ...
- js中取小数整数部分函数;取小数部分
1.丢弃小数部分,保留整数部分 parseInt(23.56); 结果:23 2.向上取整,有小数就整数部分加1 Math.ceil(23.56) 结果:24 3,四舍五入. Math.round(2 ...
- java多线程14 :wait()和notify()/notifyAll()
轮询 线程本身是操作系统中独立的个体,但是线程与线程之间不是独立的个体,因为它们彼此之间要相互通信和协作. 想像一个场景,A线程做int型变量i的累加操作,B线程等待i到了10000就打印出i,怎么处 ...
- CentIOS PHP 扩展库
1.GD库 yum -y install php-gd