QT QObject::connect函数的学习
- 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;int type;} 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函数的学习的更多相关文章
- 很多人以为 connect 和 disconnect 应该像 new 和 delete 一样成对出现 这是错误的(只要 sender 或 receiver 其中之一不存在了,connect 会自动失效。QObject::connect 函数是线程安全的)
其实我写文章也是边查资料边编辑的 有时候是怕自己的阐述不严谨,有时候是怕自己重复造轮子 就像有些人不停的教大家QLabel QDialog QWidget 个人是不屑的 命令模式 用 Qt's Und ...
- QT运行出错:QObject::connect: Parentheses expected以及QObject::connect: No such slot ***
我在QGraphicsScene子类中添加了item的弹出菜单,并连接Action到槽函数,结果槽函数不起作用,输出:QObject::connect: No such slot *** C++ C ...
- Qt error ------ no matching function for call to QObject::connect(QSpinBox*&, <unresolved overloaded function type>, QSlider*&, void (QAbstractSlider::*)(int))
connect(ui->spinBox_luminosity,&QSpinBox::valueChanged, ui->horizontalSlider_luminosity, & ...
- 【QT】跨线程的信号槽(connect函数)
线程的信号槽机制需要开启线程的事件循环机制,即调用QThread::exec()函数开启线程的事件循环. Qt信号-槽连接函数原型如下: bool QObject::connect ( const Q ...
- Qt - 错误总结 - QObject::connect: Cannot queue arguments of type 'PVCI_CAN_OBJ' (Make sure 'PVCI_CAN_OBJ' is registered using qRegisterMetaType().)
背景:一个线程通过signal-slot发送PVCI_CAN_OBJ类型的值到主线程中, 错误提示: QObject::connect: Cannot queue arguments of type ...
- Qt解决:Qobject::connect queue arguments of type ‘xxxx’,Make sure ‘xxxx’ is registered using qRegister
解决方法:在调用connect之前,通过 qRegisterMetaType() 注册你connect函数里对象的类型代码如下: typedef QString CustomString;//你自己定 ...
- Qt 信号和槽异常: QObject::connect: No Such slot baseClassName::subClassfunction() in ......
2019-08-14起笔 小熊的情况描述: 父类A继承自QWidget,所以父类A自动添加了Q_OBJECT. 子类B继承自父类A,子类B没有添加Q_OBJECT.在子类B中给动态创建的控件添加事件 ...
- Qt和c/c++connect函数冲突解决方法
在使用c/c++的connect函数时在前面写::connect()这样就可以解决了
- Qt: The State Machine Framework 学习
State Machine,即为状态机,是Qt中一项非常好的框架.State Machine包括State以及State间的Transition,构成状态和状态转移.通过状态机,我们可以很方便地实现很 ...
随机推荐
- linux系统下,递归删除.svn文件
linux系统下,递归删除.svn文件 SVNLinux 进入要删除的目录,执行下面的命令就可以啦. find . -name "*.svn" | xargs rm -rf
- fineuploader 上传jquery 控件
fineuploader 昨天用的一个jquery插件. 可参考这篇文章以前写的 file-uploader 跟 这个跟里面介绍的2个jquery 插件相比.觉得更强大写..版本号都3.9 了….. ...
- [BZOJ 1006] [HNOI2008] 神奇的国度 【弦图最小染色】
题目链接: BZOJ - 1006 题目分析 这道题是一个弦图最小染色数的裸的模型. 弦图的最小染色求法,先求出弦图的完美消除序列(MCS算法),再按照完美消除序列,从后向前倒着,给每个点染能染的最小 ...
- CentOS 6.0图解网络安装全过程
转自CentOS 6.0图解网络安装全过程 国内镜像站点(东北大学.网易) 网易镜像站点:http://mirrors.163.com/centos/6.0/isos/ 中科大镜像站点:http:// ...
- To enable integrated Windows authentication in Windows Vista/IIS 7
https://msdn.microsoft.com/en-us/library/x8a5axew.aspx Log on to the Web server by using an administ ...
- bzoj1041
基于圆的对称性,我们只需要考虑第一象限的整点即可满足条件的x,y都是整数数学上这类问题我们通常用一个量表示另一个量y^2=(r-x)(r+x) (r-x)(r+x)要是完全平方数令d=gcd(r-x ...
- C# 客服端上传文件与服务器器端接收 (简单代码)
简单代码: /*服务器端接收写入 可以实现断点续传*/ public string ConnectUpload(string newfilename,string filepath,byte[] fi ...
- 有关Ant编译
今天给一个项目做jar包,从之前的项目拷贝了build文件,改了改,运行,结果有问题.编译时不打出任何信息,也不报错,从目标文件看,似乎拷贝等任务都执行了,但是编译没有执行.网上Google结果都说是 ...
- 【转】Unity 相关经典博客资源总结(持续更新)
原文:http://blog.csdn.net/prothi/article/details/20123319 就作为一个记录吧,把平时看过的Unity相关的一些好的Blog记录并分享. 好的论坛: ...
- HDOJ1181变形课 深搜回溯
变形课 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others) Total Submissi ...