研一的时候开始使用Qt,感觉用Qt开发图形界面比MFC的一套框架来方便的多。后来由于项目的需要,也没有再接触Qt了。现在要重新拾起来,于是要从基础学起。
Now,开始学习Qt事件处理机制。
元对象系统的构成
- QObject为所有需要利用元对象系统的对象提供一个基类。
- Q_OBJECT宏,在类的声明体内激活meta-object功能,比如动态属性、信号和槽。
- Meta Object Compiler(MOC),为每个QObject派生类生成代码,以支持meta-object功能。
- QObject定义了从一个QObject对象访问meta-object功能的接口,Q_OBJECT宏用来告诉编译器该类需要激活meta-object功能,编译器在扫描一个源文件时,如果发现类的声明中有这个宏,就会生成一些代码来为支持meta-object功能——主要是生成该类对应MetaObject类以及对QObject的函数override(重载)。
QObject和QMetaObject
QMetaObject包含了QObject的所谓的元数据,也就是QObject信息的一些描述信息:除了类型信息外,还包含QT中特有的signal&slot信息。
virtual QObject::metaObject();
该方法返回一个QObject对应的metaObject对象,如上文所说,如果一个类的声明中包含了Q_OBJECT宏,编译器会生成代码来实现这个类对应的QMetaObject类,并重载QObject::metaObject()方法来返回这个QMetaObject类的实例引用。这样当通过QObject类型的引用调用metaObejct方法时,返回的是这个引用的所指的真实对象的metaobject。
如果一个类从QObject派生,确没有声明Q_OBJECT宏,那么这个类的metaobject对象不会被生成,这样这个类所声明的signal slot都不能使用,而这个类实例调用metaObject()返回的就是其父类的metaobject对象,这样导致的后果就是你从这个类实例获得的元数据其实都是父类的数据,这显然给你的代码埋下隐患。因此如果一个类从QOBject派生,它都应该声明Q_OBJECT宏,不管这个类有没有定义signal&slot和Property。
这样每个QObject类都有一个对应的QMetaObject类,形成一个平行的类型层次。
QMetaObject提供的信息
下面通过QMetaObject的接口来解释QMetaObject提供的信息。
1)基本信息
struct Q_CORE_EXPORT QMetaObject { const char *className() const; const QMetaObject *superClass() const;
struct { // private data
const QMetaObject *superdata; //父类QMetaObject实例的指针 const char *stringdata; //一段字符串内存块,包含MetaObject信息之字符串信息 const uint *data; //一段二级制内存块,包含MetaObject信息之二进制信息 const void *extradata; //额外字段,暂未使用 } d;
...
};
2)classinfo:提供额外的类信息-名值对。用户可以在类的生命中以Q_CLASSINFO(name,value)的方式添加。
int classInfoOffset() const; int classInfoCount() const; int indexOfClassInfo(const char *name) const; QMetaClassInfo classInfo(int index) const;
example:
class MyClass : public QObject { Q_OBJECT Q_CLASSINFO("author", "Sabrina Schweinsteiger") Q_CLASSINFO("url", "http://doc.moosesoft.co.uk/1.0/") public: ... };
3)constructor:提供该类的构造方法信息。
int constructorCount() const; int indexOfConstructor(const char *constructor) const; QMetaMethod constructor(int index) const;
4)enum:描述该类声明体重所包含的枚举类型信息。
int enumeratorOffset() const; int enumeratorCount() const; int indexOfEnumerator(const char *name) const; QMetaEnum enumerator(int index) const;
5)method:描述类中所包含方法信息:包括property,signal,slot等。
int methodOffset() const; int methodCount() const; int indexOfMethod(const char *method) const; int indexOfSignal(const char *signal) const; int indexOfSlot(const char *slot) const; QMetaMethod method(int index) const;
6)property:类型的属性信息。
int propertyOffset() const; int propertyCount() const; int indexOfProperty(const char *name) const; QMetaProperty property(int index) const;
注意:对于类里面定义的函数,构造函数,枚举,只有加上一些宏才表示你希望为方法提供meta信息。比如 Q_ENUMS用来注册宏,Q_INVACABLE用来注册方法(包括构造函数)。Qt这么设计的原因应该是避免meta信息的臃肿。
举例说明MOS(Meta Object System)
TestObject继承QObject,定义了两个Property:PropertyA和PropertyB;两个classinfo:Author,version;一个枚举:TestEnum。
#include <QObject> class TestObject : public QObject { Q_OBJECT Q_PROPERTY(QString propertyA READ getPropertyA WRITE getPropertyA RESET resetPropertyA DESIGNABLE true SCRIPTABLE true STORED true USER false) Q_PROPERTY(QString propertyB READ getPropertyB WRITE getPropertyB RESET resetPropertyB) Q_CLASSINFO("Author", "Long Huihu") Q_CLASSINFO("Version", "TestObjectV1.0") Q_ENUMS(TestEnum) public: enum TestEnum { EnumValueA, EnumValueB }; public: TestObject(); signals: void clicked(); void pressed(); public slots: void onEventA(const QString &); void onEventB(int ); }
TestObject的moc文件:
#include "TestObject.h" #if !defined(Q_MOC_OUTPUT_REVISION) #error "The header file 'TestObject.h' doesn't include <QObject>." #elif Q_MOC_OUTPUT_REVISION != 62 #error "This file was generated using the moc from 4.6.0. It" #error "cannot be used with the include files from this version of Qt." #error "(The moc has changed too much.)" #endif QT_BEGIN_MOC_NAMESPACE static const uint qt_meta_data_TestObject[] = { // content: 4, // revision 0, // classname 2, 14, // classinfo 4, 18, // methods 2, 38, // properties 1, 44, // enums/sets 0, 0, // constructors 0, // flags 2, // signalCount // classinfo: key, value 22, 11, 44, 29, // signals: signature, parameters, type, tag, flags 53, 52, 52, 52, 0x05, 63, 52, 52, 52, 0x05, // slots: signature, parameters, type, tag, flags 73, 52, 52, 52, 0x0a, 91, 52, 52, 52, 0x0a, // properties: name, type, flags 113, 105, 0x0a095007, 123, 105, 0x0a095007, // enums: name, flags, count, data 133, 0x0, 2, 48, // enum data: key, value 142, uint(TestObject::EnumValueA), 153, uint(TestObject::EnumValueB), 0 // eod }; static const char qt_meta_stringdata_TestObject[] = { "TestObject Long Huihu Author " "TestObjectV1.0 Version clicked() " "pressed() onEventA(QString) onEventB(int) " "QString propertyA propertyB TestEnum " "EnumValueA EnumValueB " }; const QMetaObject TestObject::staticMetaObject = { { &QObject::staticMetaObject, qt_meta_stringdata_TestObject, qt_meta_data_TestObject, 0 } }; #ifdef Q_NO_DATA_RELOCATION const QMetaObject &TestObject::getStaticMetaObject() { return staticMetaObject; } #endif //Q_NO_DATA_RELOCATION const QMetaObject *TestObject::metaObject() const { return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject; } void *TestObject::qt_metacast(const char *_clname) { if (!_clname) return 0; if (!strcmp(_clname, qt_meta_stringdata_TestObject)) return static_cast<void*>(const_cast< TestObject*>(this)); return QObject::qt_metacast(_clname); } int TestObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a) { _id = QObject::qt_metacall(_c, _id, _a); if (_id < 0) return _id; if (_c == QMetaObject::InvokeMetaMethod) { switch (_id) { case 0: clicked(); break; case 1: pressed(); break; case 2: onEventA((*reinterpret_cast< const QString(*)>(_a[1]))); break; case 3: onEventB((*reinterpret_cast< int(*)>(_a[1]))); break; default: ; } _id -= 4; } #ifndef QT_NO_PROPERTIES else if (_c == QMetaObject::ReadProperty) { void *_v = _a[0]; switch (_id) { case 0: *reinterpret_cast< QString*>(_v) = getPropertyA(); break; case 1: *reinterpret_cast< QString*>(_v) = getPropertyB(); break; } _id -= 2; } else if (_c == QMetaObject::WriteProperty) { void *_v = _a[0]; switch (_id) { case 0: getPropertyA(*reinterpret_cast< QString*>(_v)); break; case 1: getPropertyB(*reinterpret_cast< QString*>(_v)); break; } _id -= 2; } else if (_c == QMetaObject::ResetProperty) { switch (_id) { case 0: resetPropertyA(); break; case 1: resetPropertyB(); break; } _id -= 2; } else if (_c == QMetaObject::QueryPropertyDesignable) { _id -= 2; } else if (_c == QMetaObject::QueryPropertyScriptable) { _id -= 2; } else if (_c == QMetaObject::QueryPropertyStored) { _id -= 2; } else if (_c == QMetaObject::QueryPropertyEditable) { _id -= 2; } else if (_c == QMetaObject::QueryPropertyUser) { _id -= 2; } #endif // QT_NO_PROPERTIES return _id; } // SIGNAL 0 void TestObject::clicked() { QMetaObject::activate(this, &staticMetaObject, 0, 0); } // SIGNAL 1 void TestObject::pressed() { QMetaObject::activate(this, &staticMetaObject, 1, 0); } QT_END_MOC_NAMESPACE
- qt_meta_data_TestObject:定义的正是QMetaObject::d.data指向的信息块;
- qt_meta_stringdata_TestObject:定义的是QMetaObject::d.dataString指向的信息块;
- const QMetaObject TestObject::staticMetaObject:定义TestObject类的MetaObject实例,从中可以看出QMetaObject各个字段是如何被赋值的;
- const QMetaObject *TestObject::metaObject() const:重写了QObject::metaObject函数,返回上述的MetaObject实例指针。
- TestObject::qt_metacall()是重写QObject的方法,依据传入的参数来调用signal&slot或访问property,动态方法调用属性访问正是依赖于这个方法。
- TestObject::clicked()和TestObject::pressed()正是对两个signal的实现,可见,signal其实就是一种方法,只不过这种方法由qt meta system来实现,不用我们自己实现。
TestObject类的所有meta信息就存储在 qt_meta_data_TestObject和qt_meta_stringdata_TestObject这两个静态数据中。 QMetaObject的接口的实现正是基于这两块数据。下面就对这两个数据进行分块说明。
static const uint qt_meta_data_TestObject[] = { 数据块一: // content: 4, // revision 0, // classname 2, 14, // classinfo 4, 18, // methods 2, 38, // properties 1, 44, // enums/sets 0, 0, // constructors 0, // flags 2, // signalCount 这块数据可以被看做meta信息的头部,正好和QMetaObjectPrivate数据结构相对应,在QMetaObject的实现中,正是将这块数据映射为QMetaObjectPrivate进行使用的。 第一行数据“4”:版本号; 第二行数据“0”:类型名,该值是qt_meta_stringdata_TestObject的索引,qt_meta_stringdata_TestObject[0]这个字符串不正是类型名“TestObject”吗。 第三行数据“2,14”,第一个表明有2个classinfo被定义,第二个是说具体的 classinfo信息在qt_meta_data_TestObject中的索引,qt_meta_data_TestObject[14]的位置两个 classinfo名值对的定义; 第四行数据“4,18”,指明method的信息,模式同上; 第五行数据“2,38”,指明property的信息,模式同上; 第六行数据“1,14”,指明enum的信息,模式同上。 数据块二: // classinfo: key, value 22, 11, 44, 29, classinfo信息块。第一行“22,11”,22表明 qt_meta_stringdata_TestObject[22]处定义的字符串是classinfo的key,11表明 qt_meta_stringdata_TestObject[11]处的字符串就是value。第二行“44,29”定义第二个classinfo。 数据块三: // signals: signature, parameters, type, tag, flags 53, 52, 52, 52, 0x05, 63, 52, 52, 52, 0x05, signal信息块。第一行“53, 52, 52, 52, 0x05”定义第一个signal clicked()。qt_meta_stringdata_TestObject[53]是signal名称字符串。parameters 52, type 52, tag 52, flags如何解释暂未知。 数据块四: // slots: signature, parameters, type, tag, flags 73, 52, 52, 52, 0x0a, 91, 52, 52, 52, 0x0a, slots信息,模式类似signal。 数据块五: // properties: name, type, flags 113, 105, 0x0a095007, 123, 105, 0x0a095007, property性信息,模式类signal和slots,105如何和type对应暂未知。 数据块六: // enums: name, flags, count, data 133, 0x0, 2, 48, // enum data: key, value 142, uint(TestObject::EnumValueA), 153, uint(TestObject::EnumValueB), enum信息,第一行定义的是枚举名,flag,值的数目,data48不知是什么。 几行定义的是各枚举项的名称和值。名称同上都是qt_meta_stringdata_TestObject的索引值。 0 // eod }; static const char qt_meta_stringdata_TestObject[] = { 这块数据就是meta信息所需的字符串。是一个字符串的序列。 "TestObject Long Huihu Author " "TestObjectV1.0 Version clicked() " "pressed() onEventA(QString) onEventB(int) " "QString propertyA propertyB TestEnum " "EnumValueA EnumValueB " };
QMetaObjectPrivate的数据定义
QMetaObjectPrivate是QMetaObject的私有实现类,其数据定义部分如下(见头文件qmetaobject_p.h)。该数据结构全是int类型,一些是直接的int型信息,比如classInfoCount、
methodCount等,还有一些是用于在QMetaObject的stringdata和data内存块中定位信息的索引值。
struct QMetaObjectPrivate { int revision; int className; int classInfoCount, classInfoData; int methodCount, methodData; int propertyCount, propertyData; int enumeratorCount, enumeratorData; int constructorCount, constructorData; //since revision 2 int flags; //since revision 3 int signalCount; //since revision 4 // revision 5 introduces changes in normalized signatures, no new members // revision 6 added qt_static_metacall as a member of each Q_OBJECT and inside QMetaObject itself };
QMetaObject::className()
inline const char *QMetaObject::className() const { return d.stringdata; }
d.stringdata就是那块字符串数据,包含若干c字符串(以' ')结尾。如果把d.stringdata当做一个c字符串指针的话,就是这个字符串序列的第一个字符串,正是类名。
QMetaObject::superClass()
inline const QMetaObject *QMetaObject::superClass() const { return d.superdata; }
QMetaObject::classInfoCount()
int QMetaObject::classInfoCount() const { int n = priv(d.data)->classInfoCount; const QMetaObject *m = d.superdata; while (m) { n += priv(m->d.data)->classInfoCount; m = m->d.superdata; } return n; }
从代码可以看出,返回该类的所有classinfo数目,包括所有基类的。
函数priv是一个简单inline函数:
static inline const QMetaObjectPrivate *priv(const uint* data) { return reinterpret_cast<const QMetaObjectPrivate*>(data); }
d.data指向的是那块二进制信息,priv将d.data解释为QMetaObjectPrivate。
QMetaObject::classInfoOffet()
int QMetaObject::classInfoOffset() const { int offset = 0; const QMetaObject *m = d.superdata; while (m) { offset += priv(m->d.data)->classInfoCount; m = m->d.superdata; } return offset; }
该类的含义是返回这个类所定义的classinfo的起始索引值,相当于它的祖先类所定义的classinfo的数量.
QMetaObject::classInfo(int index)
QMetaClassInfo QMetaObject::classInfo(int index) const { int i = index; i -= classInfoOffset(); if (i < 0 && d.superdata) return d.superdata->classInfo(index); QMetaClassInfo result; if (i >= 0 && i < priv(d.data)->classInfoCount) { result.mobj = this; result.handle = priv(d.data)->classInfoData + 2*i; } return result; } class Q_CORE_EXPORT QMetaClassInfo { public: inline QMetaClassInfo() : mobj(0),handle(0) {} const char *name() const; const char *value() const; inline const QMetaObject *enclosingMetaObject() const { return mobj; } private: const QMetaObject *mobj; uint handle; friend struct QMetaObject; };
这个代码的流程比较简单。priv(d.data)->classInfoData是classinfo的信息在d.data中的偏移;每条classinfo信息占2个UINT的大小,因此“priv(d.data)->classInfoData + 2*i”这个表达式的值就是第i个classinfo的信息在d.data中的偏移。
QMetaObject::indexOfClassInfo()
int QMetaObject::indexOfClassInfo(const char *name) const { int i = -1; const QMetaObject *m = this; while (m && i < 0) { for (i = priv(m->d.data)->classInfoCount-1; i >= 0; --i) if (strcmp(name, m->d.stringdata + m->d.data[priv(m->d.data)->classInfoData + 2*i]) == 0) { i += m->classInfoOffset(); break; } m = m->d.superdata; } return i; }
按照继承层次,从下往上寻找名字为name的classinfo。
参考前一函数的解释,表达式m->d.data[priv(m->d.data)->classInfoData + 2*i]的值是第i个classinfo信息的第一个32位值。该值是字符信息块d.stringdata中的索引值。因此 m->d.stringdata+ m->d.data[priv(m->d.data)->classInfoData + 2*i]就是classinfo名称的字符串。
int constructorCount() const
1 int QMetaObject::constructorCount() const 2 { 3 if (priv(d.data)->revision < 2) 4 return 0; 5 return priv(d.data)->constructorCount; 6 }
QMetaMethod constructor ( int index ) const
1 QMetaMethod QMetaObject::constructor(int index) const 2 { 3 int i = index; 4 QMetaMethod result; 5 if (priv(d.data)->revision >= 2 && i >= 0 && i < priv(d.data)->constructorCount) { 6 result.mobj = this; 7 result.handle = priv(d.data)->constructorData + 5*i; 8 } 9 return result; 10 }
int indexOfConstructor ( const char * constructor ) const
1 int QMetaObject::indexOfConstructor(const char *constructor) const 2 { 3 if (priv(d.data)->revision < 2) 4 return -1; 5 for (int i = priv(d.data)->constructorCount-1; i >= 0; --i) { 6 const char *data = d.stringdata + d.data[priv(d.data)->constructorData + 5*i]; 7 if (data[0] == constructor[0] && strcmp(constructor + 1, data + 1) == 0) { 8 return i; 9 } 10 } 11 return -1; 12 }
1 int enumeratorCount () const 2 3 int enumeratorOffset () const 4 5 QMetaEnum enumerator ( int index ) const 6 7 int indexOfEnumerator ( const char * name ) const
查找Signal
一般函数的查找方式
QMetaMethod QMetaObject::method(int index) const { int i = index; i -= methodOffset(); if (i < 0 && d.superdata) return d.superdata->method(index); QMetaMethod result; if (i >= 0 && i < priv(d.data)->methodCount) { result.mobj = this; result.handle = priv(d.data)->methodData + 5*i; } return result; }
int methodCount () const 略; int methodOffset () const 略; int indexOfMethod(const char *method)const;
signal的查找函数
int QMetaObject::indexOfSignal(const char *signal) const { const QMetaObject *m = this; int i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, false); if (i < 0) { m = this; i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, true); } if (i >= 0) i += m->methodOffset(); return i; } int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject, const char *signal, bool normalizeStringData) { int i = indexOfMethodRelative<MethodSignal>(baseObject, signal, normalizeStringData); #ifndef QT_NO_DEBUG const QMetaObject *m = *baseObject; if (i >= 0 && m && m->d.superdata) { int conflict = m->d.superdata->indexOfMethod(signal); if (conflict >= 0) qWarning("QMetaObject::indexOfSignal: signal %s from %s redefined in %s", signal, m->d.superdata->d.stringdata, m->d.stringdata); } #endif return i; }
template<int MethodType> static inline int indexOfMethodRelative(const QMetaObject **baseObject, const char *method, bool normalizeStringData) { for (const QMetaObject *m = *baseObject; m; m = m->d.superdata) { int i = (MethodType == MethodSignal && priv(m->d.data)->revision >= 4) ? (priv(m->d.data)->signalCount - 1) : (priv(m->d.data)->methodCount - 1); const int end = (MethodType == MethodSlot && priv(m->d.data)->revision >= 4) ? (priv(m->d.data)->signalCount) : 0; if (!normalizeStringData) { for (; i >= end; --i) { const char *stringdata = m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5*i]; if (method[0] == stringdata[0] && strcmp(method + 1, stringdata + 1) == 0) { *baseObject = m; return i; } } } else if (priv(m->d.data)->revision < 5) { for (; i >= end; --i) { const char *stringdata = (m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5 * i]); const QByteArray normalizedSignature = QMetaObject::normalizedSignature(stringdata); if (normalizedSignature == method) { *baseObject = m; return i; } } } } return -1; }
可以看出,查找signal的特别之处在于,通过method元数据的第五项来判断这是不是一个signal。
1 int indexOfSlot ( const char * slot ) const 略; 2 int propertyCount () const 略; 3 int propertyOffset () const 略; 4 int indexOfProperty ( const char * name ) const 略;
QMetaProperty QMetaObject::property(int index) const { int i = index; i -= propertyOffset(); if (i < 0 && d.superdata) return d.superdata->property(index); QMetaProperty result; if (i >= 0 && i < priv(d.data)->propertyCount) { int handle = priv(d.data)->propertyData + 3*i; int flags = d.data[handle + 2]; const char *type = d.stringdata + d.data[handle + 1]; result.mobj = this; result.handle = handle; result.idx = i; if (flags & EnumOrFlag) { result.menum = enumerator(indexOfEnumerator(type)); if (!result.menum.isValid()) { QByteArray enum_name = type; QByteArray scope_name = d.stringdata; int s = enum_name.lastIndexOf("::"); if (s > 0) { scope_name = enum_name.left(s); enum_name = enum_name.mid(s + 2); } const QMetaObject *scope = 0; if (scope_name == "Qt") scope = &QObject::staticQtMetaObject; else scope = QMetaObject_findMetaObject(this, scope_name); if (scope) result.menum = scope->enumerator(scope->indexOfEnumerator(enum_name)); } } } return result; }
该函数的特别之处在于,如果这个propery是一个枚举类型的话,就为返回值QMetaPropery赋上正确QMetaEnum属性值。
QMetaProperty QMetaObject::userProperty() const { const int propCount = propertyCount(); for (int i = propCount - 1; i >= 0; --i) { const QMetaProperty prop = property(i); if (prop.isUser()) return prop; } return QMetaProperty(); }
从这个函数的实现来看,一个QObject应该只会有一个打开USER flag的property。
meta call就是通过object的meta system的支持来动态调用object的方法,metacall也是signal&slot的机制的基石。
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()); static inline bool invokeMethod(QObject *obj, const char *member, QGenericReturnArgument ret, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) { return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); } static inline bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) { return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); } static inline bool invokeMethod(QObject *obj, const char *member, QGenericArgument val0 = QGenericArgument(0), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument(), QGenericArgument val4 = QGenericArgument(), QGenericArgument val5 = QGenericArgument(), QGenericArgument val6 = QGenericArgument(), QGenericArgument val7 = QGenericArgument(), QGenericArgument val8 = QGenericArgument(), QGenericArgument val9 = QGenericArgument()) { return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0, val1, val2, val3, val4, val5, val6, val7, val8, val9); }
QMetaObject这个静态方法可以动态地调用obj对象名字为member的方法,type参数表明该调用时同步的还是异步的。ret是一个 通用的用来存储返回值的类型,后面的9个参数是用来传递调用参数的,QGenericArgument()是一种通用的存储参数值的类型。
所调用的方法必须是invocable的,也就是signal,slot或者是加了声明为Q_INVOCABLE的其他方法。
bool QMetaObject::invokeMethod(QObject *obj, const char *member, Qt::ConnectionType type, QGenericReturnArgument ret, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9) { if (!obj) return false; QVarLengthArray<char, 512> sig; int len = qstrlen(member); if (len <= 0) return false; sig.append(member, len); sig.append('('); const char *typeNames[] = {ret.name(), val0.name(), val1.name(), val2.name(), val3.name(), val4.name(), val5.name(), val6.name(), val7.name(), val8.name(), val9.name()}; int paramCount; for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) { len = qstrlen(typeNames[paramCount]); if (len <= 0) break; sig.append(typeNames[paramCount], len); sig.append(','); } if (paramCount == 1) sig.append(')'); // no parameters else sig[sig.size() - 1] = ')'; sig.append('