QT QObject::connect函数的学习
来源:互联网 发布:osx与ios内核编程 pdf 编辑:程序博客网 时间:2024/06/04 17:52
static bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType = #ifdef qdoc Qt::AutoConnection #else #ifdef QT3_SUPPORT Qt::AutoCompatConnection #else Qt::AutoConnection #endif #endif ); inline bool connect(const QObject *sender, const char *signal, const char *member, Qt::ConnectionType type = #ifdef qdoc Qt::AutoConnection #else #ifdef QT3_SUPPORT Qt::AutoCompatConnection #else Qt::AutoConnection #endif #endif ) const;
其中第二个connect的实现其实只有一句话:
{ return connect(asender, asignal, this, amember, atype); }
所以对于connect函数的学习其实就是研究第一个connect函数。
我们在使用connect函数的时候一般是这样调用的:
connect(sender,SIGNAL(signal()),receiver,SLOT(slot()));
这里用到了两个宏:SIGNAL() 和SLOT();通过connect声明可以知道这两个宏最后倒是得到一个const char*类型。
在qobjectdefs.h中可以看到SIGNAL() 和SLOT()的宏定义:
#ifndef QT_NO_DEBUG# define QLOCATION "\0"__FILE__":"QTOSTRING(__LINE__)# define METHOD(a) qFlagLocation("0"#a QLOCATION)# define SLOT(a) qFlagLocation("1"#a QLOCATION)# define SIGNAL(a) qFlagLocation("2"#a QLOCATION)#else# define METHOD(a) "0"#a# define SLOT(a) "1"#a# define SIGNAL(a) "2"#a#endif
所以这两个宏的作用就是把函数名转换为字符串并且在前面加上标识符。
比如:SIGNAL(read())展开后就是"2read()";同理SLOT(read())展开后就是"1read()"。
connect(sender,SIGNAL(signal()),receiver,SLOT(slot()));实际上就是connect(sender,“2signal()”,receiver,“1slot())”;
搞明白了实际的参数就可以来看connect的真正实现过程了,在QObject.cpp文件中可以找到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; } 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 = smeta->indexOfSignal(signal); if (signal_index < 0) { // check for normalized signatures tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); signal = tmp_signal_name.constData() + 1; signal_index = smeta->indexOfSignal(signal); if (signal_index < 0) { err_method_notfound(sender, signal_arg, "connect"); err_info_about_objects("connect", sender, receiver); return false; } } 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_index).parameterTypes()))) return false; QMetaObject::connect(sender, signal_index, receiver, method_index, type, types); const_cast<QObject*>(sender)->connectNotify(signal - 1); return true;}
上面是去除了debug代码的connect实现。
const void *cbdata[] = { sender, signal, receiver, method, &type }; if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata)) return true;
判断连接是否已经建立。
QInternal::ConnectCallback在qglobal.cpp中实现。
bool QInternal::activateCallbacks(Callback cb, void **parameters){ Q_ASSERT_X(cb >= 0, "QInternal::activateCallback()", "Callback id must be a valid id"); QInternal_CallBackTable *cbt = global_callback_table(); if (cbt && cb < cbt->callbacks.size()) { QList<qInternalCallback> callbacks = cbt->callbacks[cb]; bool ret = false; for (int i=0; i<callbacks.size(); ++i) ret |= (callbacks.at(i))(parameters); return ret; } return false;}
QInternal_CallBackTable 定义为(qglobal.cpp)
struct QInternal_CallBackTable { QVector<QList<qInternalCallback> > callbacks;};
qInternalCallback定义为(qnamespace.h)
typedef bool (*qInternalCallback)(void **);这是一个函数指针 返回值是bool,只有一个参数为void**。这个指针在调用registerCallback加入列表。
if (!check_signal_macro(sender, signal, "connect", "bind")) return false;
判断signal是否合法。
在QObject.cpp文件中可以找到check_signal_macro的实现
static bool check_signal_macro(const QObject *sender, const char *signal, const char *func, const char *op){ int sigcode = extract_code(signal); if (sigcode != QSIGNAL_CODE) { if (sigcode == QSLOT_CODE) qWarning("Object::%s: Attempt to %s non-signal %s::%s", func, op, sender->metaObject()->className(), signal+1); else qWarning("Object::%s: Use the SIGNAL macro to %s %s::%s", func, op, sender->metaObject()->className(), signal); return false; } return true;}
extract的实现也在QObject中,它就是去字符串第一个字符,并且只取低2位的值。
static int extract_code(const char *member){ // extract code, ensure QMETHOD_CODE <= code <= QSIGNAL_CODE return (((int)(*member) - '0') & 0x3);}
这里又有两个宏:QSIGNAL_CODE 和QSLOT_CODE。它们也是在qobjectdefs.h文件中定义的。
#ifdef QT3_SUPPORT#define METHOD_CODE 0 // member type codes#define SLOT_CODE 1#define SIGNAL_CODE 2#endif
这个定义与之前的SIGNAL和SLOT的定义是对应的。
const QMetaObject *smeta = sender->metaObject(); const char *signal_arg = signal; ++signal; //skip code int signal_index = smeta->indexOfSignal(signal); if (signal_index < 0) { // check for normalized signatures tmp_signal_name = QMetaObject::normalizedSignature(signal - 1); signal = tmp_signal_name.constData() + 1; signal_index = smeta->indexOfSignal(signal); if (signal_index < 0) { err_method_notfound(sender, signal_arg, "connect"); err_info_about_objects("connect", sender, receiver); return false; } }
获取signal的索引。
metaObject()是在moc_name.cpp文件中生成的。
return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;
其中staticMetaObject也是在moc文件中定义的
const QMetaObject MainWindow::staticMetaObject = { { &QMainWindow::staticMetaObject, qt_meta_stringdata_MainWindow, qt_meta_data_MainWindow, 0 }};
qt_meta_stringdata_MainWindow(具体名字和类名有关)就是staticconstchar[]类型。它记录了全部的signals和slots等的函数名、返回值和参数表的信息。
qt_meta_data_MainWindow(具体名字和类名有关)是staticconstuint[]类型。它记录了每一个函数的函数名、返回值和参数表在qt_meta_stringdata_MainWindow中的索引。同时它还记录了每一个函数的类型具体在qmetaobject.cpp文件中定义。
enum MethodFlags { AccessPrivate = 0x00, AccessProtected = 0x01, AccessPublic = 0x02, AccessMask = 0x03, //mask MethodMethod = 0x00, MethodSignal = 0x04, MethodSlot = 0x08, MethodConstructor = 0x0c, MethodTypeMask = 0x0c, MethodCompatibility = 0x10, MethodCloned = 0x20, MethodScriptable = 0x40};
indexOfSignal(signal);的实现在qmetaobject.cpp中。其主要作用是利用qt_meta_stringdata_MainWindow 和qt_meta_data_MainWindow查找已经定义了的signal并返回索引。
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; }
校验method并且查找它的索引。过程与signal类似。
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; }
判断signal和method是否兼容,checkConnectArgs函数的在qmetaObject.cpp文件中实现。这个函数校验了signal和method的参数。当两者的参数一致或method参数比signal参数少(method与signal前几个参数一致)的时候返回true,其它返回false。
int *types = 0; if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection) && !(types = queuedConnectionTypes(smeta->method(signal_index).parameterTypes()))) return false;
如果是以发消息的方式执行method就需要对参数类型进行判断。queuedConnectionTypes在QObject.cpp实现。实际上是在QMetatype.cpp中定义了一个
static conststruct{ constchar* typeName;inttype;}types[];在这里记录了全部类型和名称如({"void",QMetaType::Void});Void在Qmetatype.h中定义。
QMetaObject::connect(sender, signal_index, receiver, method_index, type, types);
调用QMetaObject的connect函数,再次不详细写出。
const_cast<QObject*>(sender)->connectNotify(signal - 1);
最后调用虚函数connectNotify表示connect已经执行完成。
- QT QObject::connect函数的学习
- QT QObject::connect函数的学习
- QT QObject::connect函数的学习
- 深入理解QT的SIGNAL\SLOT机制(三):QObject::connect函数
- [QT][源码分析]QObject::connect
- Qt QObject::connect 链接次数
- [QT][源码分析]QObject::connect
- QObject成员函数connect()函数
- QT学习:QObject::sender()的用法
- QT信槽编程,QObject::connect: Cannot connect (null)报错的两种成因
- VS2015 QT,QObject::connect()失败
- Qt函数connectSlotsByName(),即"QMetaObject::connectSlotsByName(QObject *o)"的说明
- 使用Qt的QObject函数实现二叉树或多叉树
- 关于QObject::connect中函数参数
- 关于QObject::connect函数参数问题
- 我的Qt学习笔记 1 从QObject开始
- [Qt]QObject::connect: Cannot queue arguments of type 'QString&'
- Qt: QObject::connect: Cannot queue arguments of type 'XXX'
- 黑盒测试用例设计模式-流程分析(上)
- 本地调试HBase源码
- android布局属性详解
- HttpWatch工具简介及使用技巧
- 自己写的实时爬取 CSDN 2012 博客之星 88位候选人排名
- QT QObject::connect函数的学习
- hdu 3648 Median Filter
- Introduction to the calculus of variations
- 编译 Busybox 常遇到的问题 SYS_ioprio_set ioprio_get
- 自动换行的css
- Equls 和==的区别
- 冒泡法小程序(1)
- 冒泡法小程序(2)
- note : 有些道理的话