Qt MetaObject 详解
这个系列的几篇文章通过阅读Qt帮助文档和相关的源代码来学习研究Qt meta-object所提供的功能,及其实现的方式。
Qt meta-object系统基于三个方面:
1、QObject提供一个基类,方便派生类使用meta-object系统的功能;
2、Q_OBJECT宏,在类的声明体内激活meta-object功能,比如动态属性、信号、槽;
3、Meta Object编译器(MOC),为每个QObject派生类生成代码,以支持meta-object功能。
QObject和QMetaObject:
顾名思义,QMetaObject包含了QObject的所谓的元数据,也就是QObject信息的一些描述信息:除了类型信息外,还包含QT中特 有的signal&slot信息。
这样每个QObject类都有一个对应的QMetaObject类,形成一个平行的类型层次。
QMetaObject提供的信息:
下面通过QMetaObject的接口来解读QMetaObject提供的信息:
const char * className () const;
const QMetaObject * superClass () const
2、classinfo: 提供额外的类信息。其实就是一些名值对。 用户可以在类的声明中以Q_CLASSINFO(name, value)方式添加。
int classInfoCount () const
int classInfoOffset () const
QMetaClassInfo classInfo ( int index ) const
int indexOfClassInfo ( const char * name ) const
QMetaMethod constructor ( int index ) const
int constructorCount () const
int indexOfConstructor ( const char * constructor ) const
QMetaEnum enumerator ( int index ) const
int enumeratorCount () const
int enumeratorOffset () const
int indexOfEnumerator ( const char * name ) const
5、method:描述类中所包含方法信息:包括property,signal,slot等,包括祖先类,如何组织暂时不确定。
QMetaMethod method ( int index ) const
int methodCount () const
int methodOffset () const
QMetaProperty property ( int index ) const
int propertyCount () const
int propertyOffset () const
int indexOfProperty ( const char * name ) const
QMetaProperty userProperty () const //返回类中设置了USER flag的属性,(难道只能有一个这样的属性?)
注意:对于类里面定义的函数,构造函数,枚举,只有加上一些宏才表示你希望为方法提供meta信息。比如 Q_ENUMS用来注册宏,
Q_INVACABLE用来注册方法(包括构造函数)。Qt这么设计的原因应该是避免meta信息的臃肿。
QMetaObject的数据定义:
QMetaObject包含唯一的数据成员如下(见头文件qobjectdefs.h)
QMetaObjectPrivate的数据定义:
下文利用一个示例QObject子类及其moc文件,来分析QMetaObject的信息结构。
示例类TestObject:
TestObject类继承自QObject,定义了两个Property:propertyA,propertyB;两个classinfo:Author,Version;一个枚举:TestEnum。
#include
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 ."
#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\0Long Huihu\0Author\0" "TestObjectV1.0\0Version\0\0clicked()\0" "pressed()\0onEventA(QString)\0onEventB(int)\0" "QString\0propertyA\0propertyB\0TestEnum\0" "EnumValueA\0EnumValueB\0"
};
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::metaObject() const:重写了QObject::metaObject函数,返回上述的MetaObject实例指针。
static const uint qt_meta_data_TestObject[] = {
数据块一:
// content:
4, // revision
0, // classname
2, 38, // properties
1, 44, // enums/sets
0, 0, // constructors
0, // flags
2, // signalCount
这块数据可以被看做meta信息的头部,正好和QMetaObjectPrivate数据结构相对应,在QMetaObject的实现中,正是将这块数据映射为QMetaObjectPrivate进行使用的。
第五行数据“2,38”,指明property的信息,模式同上;
第六行数据“1,14”,指明enum的信息,模式同上。
数据块二:
// classinfo: key, value
22, 11,
44, 29,
数据块四:
// 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,
property性信息,模式类signal和slots,105如何和type对应暂未知。
enum信息,第一行定义的是枚举名,flag,值的数目,data48不知是什么。
几行定义的是各枚举项的名称和值。名称同上都是qt_meta_stringdata_TestObject的索引值。
static const char qt_meta_stringdata_TestObject[] = {
可以看出,meta信息在moc文件中以静态数据的形式被定义,其排列有点类似可执行文件中静态数据信息的排布。
QtMetaObjectsysmtem详解之三:QMetaObject接口实现
本篇从Qt MetaObject源代码解读相关接口的实现,这些接口都定义于qmetaobject.cpp中。
QMetaObject::className()
inline const char *QMetaObject::className() const
{ return d.stringdata; }
从前一篇可知,d.stringdata就是那块字符串数据,包含若干c字符串(以'\0')结尾。如果把d.stringdata当做一个c字符串指针的话,就是这个字符串序列的第一个字符串,正是类名。
QMetaObject::superClass()
inline const QMetaObject *QMetaObject::superClass() const
{ return d.superdata; }
QMetaObject::classInfoCount()
从代码可以看出,返回该类的所有classinfo数目,包括所有基类的。
static inline const QMetaObjectPrivate *priv(const uint* data)
{ return reinterpret_cast(data); }
由前一篇可知,d.data指向的是那块二进制信息,priv将d.data解释为QMetaObjectPrivate。
QMetaObject:: classInfoOffset ()
该类的含义是返回这个类所定义的classinfo的起始索引值,相当于它的祖先类所定义的classinfo的数量。
QMetaObject:: classInfo (int index)
QMetaClassInfo classInfo ( int index ) const
QMetaObject:: indexOfClassInfo ()
int indexOfClassInfo ( const char * name ) const
按照继承层次,从下往上寻找名字为name的classinfo。
int constructorCount () const
QMetaMethod constructor ( int index ) const
int indexOfConstructor ( const char * constructor ) const
QMetaEnum enumerator ( int index ) const
int indexOfEnumerator ( const char * name ) const
int methodCount () const 略;
int methodOffset () const 略;
QMetaMethod method ( int index ) const
int i = index;
i -= methodOffset();
if (i < 0 && d.superdata)
return d.superdata->method(index);
int indexOfMethod ( const char * method ) const 略;
int indexOfSignal ( const char * signal ) const
可以看出,查找signal的特别之处在于,通过method元数据的第五项来判断这是不是一个signal。
int indexOfSlot ( const char * slot ) const 略;
int propertyCount () const 略;
int propertyOffset () const 略;
int indexOfProperty ( const char * name ) const 略;
QMetaProperty property ( int index ) const
int i = index;
i -= propertyOffset();
if (i < 0 && d.superdata)
return d.superdata->property(index);
该函数的特别之处在于,如果这个propery是一个枚举类型的话,就为返回值QMetaPropery赋上正确QMetaEnum属性值。
QMetaProperty userProperty () const
从这个函数的实现来看,一个QObject应该只会有一个打开USER flag的property。
Qt MetaObject System详解之四:meta call
QMetaObject::invokeMethod():
bool invokeMethod ( QObject * obj , const char * member , Qt::ConnectionType type , 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() )
QMetaObject这个静态方法可以动态地调用obj对象名字为member的方法,type参数表明该调用时同步的还是异步的。ret是一个 通用的用来存储返回值的类型,后面的9个参数是用来传递调用参数的,QGenericArgument()是一种通用的存储参数值的类型。(这里让人感觉 比较奇怪的是Qt为什么不将这个参数列表弄成某种动态的形式,而是最多九个)
所调用的方法必须是invocable的,也就是signal,slot或者是加了声明为Q_INVOCABLE的其他方法。
这个方法的实现如下:
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('\0'); int idx = obj->metaObject()->indexOfMethod(sig.constData()); if (idx < 0) { QByteArray norm = QMetaObject::normalizedSignature(sig.constData()); idx = obj->metaObject()->indexOfMethod(norm.constData()); } if (idx < 0 || idx >= obj->metaObject()->methodCount()) return false; QMetaMethod method = obj->metaObject()->method(idx); return method.invoke(obj, type, ret, val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
}
先依据传递的方法名称和参数,构造完整的函数签名(存储在局部变量sig)。参数的类型名就是调用时传递时的参数静态类型,这里可不会有什么类型转换,这是运行时的行为,参数类型转换是编译时的行为。
然后通过这个sig签名在obj中去查找该方法,查询的结果就是一个QMetaMethod值,再将调用委托给QMetaMethod::invoke方法。
bool QMetaMethod::invoke(QObject *object, Qt::ConnectionType connectionType, QGenericReturnArgument returnValue, QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3, QGenericArgument val4, QGenericArgument val5, QGenericArgument val6, QGenericArgument val7, QGenericArgument val8, QGenericArgument val9) const
{ if (!object || !mobj) return false; // check return type if (returnValue.data()) { const char *retType = typeName(); if (qstrcmp(returnValue.name(), retType) != 0) { // normalize the return value as well // the trick here is to make a function signature out of the return type // so that we can call normalizedSignature() and avoid duplicating code QByteArray unnormalized; int len = qstrlen(returnValue.name()); unnormalized.reserve(len + 3); unnormalized = "_("; // the function is called "_" unnormalized.append(returnValue.name()); unnormalized.append(')'); QByteArray normalized = QMetaObject::normalizedSignature(unnormalized.constData()); normalized.truncate(normalized.length() - 1); // drop the ending ')' if (qstrcmp(normalized.constData() + 2, retType) != 0) return false; } } // check argument count (we don't allow invoking a method if given too few arguments) const char *typeNames[] = { returnValue.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) { if (qstrlen(typeNames[paramCount]) <= 0) break; } int metaMethodArgumentCount = 0; { // based on QMetaObject::parameterNames() const char *names = mobj->d.stringdata + mobj->d.data[handle + 1]; if (*names == 0) { // do we have one or zero arguments? const char *signature = mobj->d.stringdata + mobj->d.data[handle]; while (*signature && *signature != '(') ++signature; if (*++signature != ')') ++metaMethodArgumentCount; } else { --names; do { ++names; while (*names && *names != ',') ++names; ++metaMethodArgumentCount; } while (*names); } } if (paramCount <= metaMethodArgumentCount) return false; // check connection type QThread *currentThread = QThread::currentThread(); QThread *objectThread = object->thread(); if (connectionType == Qt::AutoConnection) { connectionType = currentThread == objectThread ? Qt::DirectConnection : Qt::QueuedConnection; } // invoke! void *param[] = { returnValue.data(), val0.data(), val1.data(), val2.data(), val3.data(), val4.data(), val5.data(), val6.data(), val7.data(), val8.data(), val9.data() }; // recompute the methodIndex by reversing the arithmetic in QMetaObject::property() int methodIndex = ((handle - priv(mobj->d.data)->methodData) / 5) + mobj->methodOffset(); if (connectionType == Qt::DirectConnection) { return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, methodIndex, param) < 0; } else { if (returnValue.data()) { qWarning("QMetaMethod::invoke: Unable to invoke methods with return values in " "queued connections"); return false; } int nargs = 1; // include return type void **args = (void **) qMalloc(paramCount * sizeof(void *)); Q_CHECK_PTR(args); int *types = (int *) qMalloc(paramCount * sizeof(int)); Q_CHECK_PTR(types); types[0] = 0; // return type args[0] = 0; for (int i = 1; i < paramCount; ++i) { types[i] = QMetaType::type(typeNames[i]); if (types[i]) { args[i] = QMetaType::construct(types[i], param[i]); ++nargs; } else if (param[i]) { qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'", typeNames[i]); for (int x = 1; x < i; ++x) { if (types[x] && args[x]) QMetaType::destroy(types[x], args[x]); } qFree(types); qFree(args); return false; } } if (connectionType == Qt::QueuedConnection) { QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex, 0, -1, nargs, types, args)); } else { if (currentThread == objectThread) { qWarning("QMetaMethod::invoke: Dead lock detected in " "BlockingQueuedConnection: Receiver is %s(%p)", mobj->className(), object); } // blocking queued connection
#ifdef QT_NO_THREAD QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex, 0, -1, nargs, types, args));
#else QSemaphore semaphore; QCoreApplication::postEvent(object, new QMetaCallEvent(methodIndex, 0, -1, nargs, types, args, &semaphore)); semaphore.acquire();
#endif // QT_NO_THREAD } } return true;
}
代码首先检查返回值的类型是否正确;再检查参数的个数是否匹配,看懂这段代码需要参考该系列之二对moc文件的解析;再依据当前线程和被调对象所属 线程来调整connnection type;如果是directconnection,直接调用 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, methodIndex, param),param是将所有参数值指针排列组成的指针数组。如果不是directconnection,也即异步调用,就通过一个post一个 QMetaCallEvent到obj,此时须将所有的参数复制一份存入event对象。
QMetaObject::metacall的实现如下:
/*! \internal
*/
int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv)
{ if (QMetaObject *mo = object->d_ptr->metaObject) return static_cast(mo)->metaCall(cl, idx, argv); else return object->qt_metacall(cl, idx, argv);
}
如果object->d_ptr->metaObject(QMetaObjectPrivate)存在,通过该metaobject 来调用,这里要参考该系列之三对QMetaObjectPrivate的介绍,这个条件实际上就是object就是QObject类型,而不是派生类型。 否则调用object::qt_metacall。
对于异步调用,QObject的event函数里有如下代码:
case QEvent::MetaCall: { d_func()->inEventHandler = false; QMetaCallEvent *mce = static_cast(e); QObjectPrivate::Sender currentSender; currentSender.sender = const_cast(mce->sender()); currentSender.signal = mce->signalId(); currentSender.ref = 1; QObjectPrivate::Sender * const previousSender = QObjectPrivate::setCurrentSender(this, ¤tSender);
#if defined(QT_NO_EXCEPTIONS) mce->placeMetaCall(this);
#else QT_TRY { mce->placeMetaCall(this); } QT_CATCH(...) { QObjectPrivate::resetCurrentSender(this, ¤tSender, previousSender); QT_RETHROW; }
#endif QObjectPrivate::resetCurrentSender(this, ¤tSender, previousSender); break; }
QMetaCallEvent的代码很简单:
int QMetaCallEvent::placeMetaCall(QObject *object)
{ return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, id_, args_);}
殊途同归。
最后来看一下object->qt_metacall是如何实现的,这又回到了该系统之二所提供的示例moc文件中去了。该文件提供了该方法的实现:
# 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;
# }
这段代码将调用最终转到我们自己的实现的函数中来。这个函数不经提供了metamethod的动态调用,而且也提供了property的动态操作方法。可想而知,property的动态调用的实现方式一定和invocalbe method是一致的。
Qt MetaObject System详解之五:signal&slot
本篇探析signal slot的连接和调用是如何实现的。
宏SLOT,SIGNAL
# define METHOD(a) "0"#a
# define SLOT(a) "1"#a
# define SIGNAL(a) "2"#a
不过是在方法签名之前加了一个数字标记。因为我们既可以将signal连接到slot,也可以将signal连接到signal,所有必须要有某种方法区分一下。
QObject::connect()
bool QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
{ { const void *cbdata[] = { sender, signal, receiver, method, &type }; if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) return true; }
#ifndef QT_NO_DEBUG bool warnCompat = true;
#endif if (type == Qt::AutoCompatConnection) { type = Qt::AutoConnection;
#ifndef QT_NO_DEBUG warnCompat = false;
#endif } if (sender == 0 || receiver == 0 || signal == 0 || method == 0) { qWarning("QObject::connect: Cannot connect %s::%s to %s::%s", sender ? sender->metaObject()->className() : "(null)", (signal && *signal) ? signal+1 : "(null)", receiver ? receiver->metaObject()->className() : "(null)", (method && *method) ? method+1 : "(null)"); return false; } QByteArray tmp_signal_name; if (!check_signal_macro(sender, signal, "connect", "bind")) return false; const QMetaObject *smeta = sender->metaObject(); const char *signal_arg = signal; ++signal; //skip code int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal); if (signal_index < 0) { // check for normalized signatures tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); signal = tmp_signal_name.constData() + 1; smeta = sender->metaObject(); signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal); if (signal_index < 0) { err_method_notfound(sender, signal_arg, "connect"); err_info_about_objects("connect", sender, receiver); return false; } } signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index); int signalOffset, methodOffset; computeOffsets(smeta, &signalOffset, &methodOffset); int signal_absolute_index = signal_index + methodOffset; signal_index += signalOffset; QByteArray tmp_method_name; int membcode = extract_code(method); if (!check_method_code(membcode, receiver, method, "connect")) return false; const char *method_arg = method; ++method; // skip code const QMetaObject *rmeta = receiver->metaObject(); int method_index = -1; switch (membcode) { case QSLOT_CODE: method_index = rmeta->indexOfSlot(method); break; case QSIGNAL_CODE: method_index = rmeta->indexOfSignal(method); break; } if (method_index < 0) { // check for normalized methods tmp_method_name = QMetaObject::normalizedSignature(method); method = tmp_method_name.constData(); switch (membcode) { case QSLOT_CODE: method_index = rmeta->indexOfSlot(method); break; case QSIGNAL_CODE: method_index = rmeta->indexOfSignal(method); break; } } if (method_index < 0) { err_method_notfound(receiver, method_arg, "connect"); err_info_about_objects("connect", sender, receiver); return false; } if (!QMetaObject::checkConnectArgs(signal, method)) { qWarning("QObject::connect: Incompatible sender/receiver arguments" "\n %s::%s --> %s::%s", sender->metaObject()->className(), signal, receiver->metaObject()->className(), method); return false; } int *types = 0; if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection) && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes()))) return false;
#ifndef QT_NO_DEBUG { QMetaMethod smethod = smeta->method(signal_absolute_index); QMetaMethod rmethod = rmeta->method(method_index); if (warnCompat) { if(smethod.attributes() & QMetaMethod::Compatibility) { if (!(rmethod.attributes() & QMetaMethod::Compatibility)) qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)", smeta->className(), signal); } else if(rmethod.attributes() & QMetaMethod::Compatibility && membcode != QSIGNAL_CODE) { qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)", smeta->className(), signal, rmeta->className(), method); } } }
#endif if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, type, types)) return false; const_cast(sender)->connectNotify(signal - 1); return true;
}
bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index, const QObject *receiver, int method_index, int type, int *types)
{ QObject *s = const_cast(sender); QObject *r = const_cast(receiver); QOrderedMutexLocker locker(signalSlotLock(sender), signalSlotLock(receiver)); if (type & Qt::UniqueConnection) { QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists; if (connectionLists && connectionLists->count() > signal_index) { const QObjectPrivate::Connection *c2 = (*connectionLists)[signal_index].first; while (c2) { if (c2->receiver == receiver && c2->method == method_index) return false; c2 = c2->nextConnectionList; } } type &= Qt::UniqueConnection - 1; } QObjectPrivate::Connection *c = new QObjectPrivate::Connection; c->sender = s; c->receiver = r; c->method = method_index; c->connectionType = type; c->argumentTypes = types; c->nextConnectionList = 0; QT_TRY { QObjectPrivate::get(s)->addConnection(signal_index, c); } QT_CATCH(...) { delete c; QT_RETHROW; } c->prev = &(QObjectPrivate::get(r)->senders); c->next = *c->prev; *c->prev = c; if (c->next) c->next->prev = &c->next; QObjectPrivate *const sender_d = QObjectPrivate::get(s); if (signal_index < 0) { sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0; } else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8) { sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f)); } return true;
}
Qt MetaObject 详解相关推荐
- Qt MetaObject 详解之二:QMeta数据以及数据结构信息
如果一个类的声明中包含Q_OBJECT宏,那么qmake将为这个类生成 meta信息,这个信息在前一篇中所提到的moc文件中.这一篇通过解析这个一个示例moc文件来阐述这些meta信息的存储方式和格式 ...
- Qt Phonon详解
Qt Phonon详解(转帖) 前言 Phonon严格来说其实非为Qt的library,Phonon原本就是KDE 4的开放原始码多媒体API,後来与Qt合并与开发,所以简单来说就是Qt使用Phono ...
- Qt QSqlQueryModel详解
1.功能概述 QSqlQueryModel是QSqlTableModel的父类.QSqlQueryModel封装了执行SELECT语句从数据库查询数据的功能,但是QSqlQueryModel只能作为只 ...
- 《Qt 数据库详解》博客系列文章
本文章原创于www.yafeilinux.com 转载请注明出处. 由于该系列文章图床都挂了,特下载原文博客的word版. 详细博客请点附件下载. 21-Qt数据库(一)简介 22-Qt数据库(二)添 ...
- Qt QTreeWidget 详解
Qt系列文章目录 文章目录 Qt系列文章目录 前言 主要API 示例 1.头文件: 2.实现文件 二.源码下载 前言 官方文档 QTreeWidget类是一个方便的类,它提供了一个标准tree小部件与 ...
- Qt QList详解
1.QList是一种表示链表的模板类. 2.QList是Qt的一种泛型容器类.它以链表方式存储一组值,并能对这组数据进行快速索引,还提供了快速插入和删除等操作. 3.QList.QLinkedList ...
- Qt QVector 详解:从底层原理到高级用法
目录标题 引言:QVector的重要性与简介 QVector的常用接口 QVector和std::Vector 迭代器:遍历QVector 中的元素(Iterators: Traversing Ele ...
- VS中使用Qt方法详解
在 Qt Creator 中可以使用 MSVC 编译工具对 Qt 项目进行编译.若有人比较习惯于使用 Visual Studio,或某些项目必须使用 Visual Studio,也可以在 Visual ...
- Qt QSet 详解:从底层原理到高级用法
目录标题 引言:QSet的重要性与简介 QSet 的常用接口 迭代器:遍历Qset 中的元素(Iterators: Traversing Elements in Qset ) 高级用法:QSet 中的 ...
最新文章
- 2021年ACM Fellow名单出炉!唐杰、刘铁岩、谢涛等当选!
- Linux实战教学笔记16:磁盘原理
- 用户画像-撸一部分代码啊
- h5页面的写法_8. 开始构建我们的 index.hbs 页面
- wxWidgets:wxSystemOptions类用法
- 学习究竟是为了什么?
- C# 调用 Microsoft.VisualBasic.Collection
- 最佳适配算法和最差适配算法_影响开放社区的最佳(和最差)方法
- 余世维台湾经典讲座--管理者的EQ(一)
- cocos-creator 脚本逻辑-2
- 主板开启网络唤醒(Wake on lan)
- 目前服务器操作系统版本,Windows操作系统的版本选择
- 不出门远程控制公司电脑,这7个工具让你不用来回跑。
- 计算机教室不安风扇,多媒体教室设备常见故障及解决办法
- office精英俱乐部_开放组织读书俱乐部:收回精英制
- asp.net配置文件connectionStrings加密和解密
- 结对项目——最长英语单词链
- 英文版系统远程桌面无法连接到远程计算机,windows server 2016远程桌面进去,英文系统修改语言...
- Pandas与SQL比较
- 百度地图离线开发demo(热力图)
热门文章
- 百万粉女网红突袭国内手机公司:我来拿你们的源代码了!
- 武装突袭3fps服务器不稳定,《武装突袭3》深不见底:史上最硬核、最复杂的FPS游戏...
- word2016开机后首次打开非常慢_自从用了这些设置方法,电脑开机瞬间提速了50%,辛亏早知道...
- Win11任务栏太宽了怎么变窄?
- Fn键的功能笔记本fn键在哪?
- 数据挖掘实战分享:财政收入影响因素分析及预测(一)
- vue watch 修改滚动条_vue实现滚动监听,点击瞄点平滑滚动,控制内嵌滚动条滚动...
- 一文读懂~国内外区块链发展现状、趋势和政策
- 如何在Excel表格中输入分数?学会这几招轻松搞定!
- 李嘉诚的“自负指数”与盖茨的“自私基因”看成功需要什么?