4.QT4中的connect的实现
信号槽通过connect进行连接,connect的源码在qobject.cpp中
QT4中的connect的声明如下
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);
使用时,一般是这样的
connect(sender, SIGNAL(destroyed()), this, SLOT(objectDestroyed()));
其中的SIGNAL和SLOT定义如下,在qobjectdefs.h中
# define SLOT(a) "1"#a
# define SIGNAL(a) "2"#a
#a 得到参数的字符串形式,对于槽函数的宏,前面加 "1",对于信号的宏,前面加 "2",则上面的connect代码就变成如下形式
connect(sender, "2destroyed()", this, "1objectDestroyed()");
connect具体实现如下
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,const QObject *receiver, const char *method,Qt::ConnectionType type)
{if (sender == nullptr || receiver == nullptr || signal == nullptr || method == nullptr) {qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",sender ? sender->metaObject()->className() : "(nullptr)",(signal && *signal) ? signal+1 : "(nullptr)",receiver ? receiver->metaObject()->className() : "(nullptr)",(method && *method) ? method+1 : "(nullptr)");return QMetaObject::Connection(0);}//检查sender、receiver、signal、method是否为空,为空,返回个空连接QByteArray tmp_signal_name;if (!check_signal_macro(sender, signal, "connect", "bind"))//检查signal字符串中是否有2,如果没有,空连接返回return QMetaObject::Connection(0);const QMetaObject *smeta = sender->metaObject();const char *signal_arg = signal;++signal; //跳过2,指向具体的字符串QArgumentTypeArray signalTypes;Q_ASSERT(QMetaObjectPrivate::get(smeta)->revision >= 7);QByteArray signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signalName, signalTypes.size(), signalTypes.constData());//计算信号的相对序号if (signal_index < 0) {// check for normalized signaturestmp_signal_name = QMetaObject::normalizedSignature(signal - 1);signal = tmp_signal_name.constData() + 1;signalTypes.clear();signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);smeta = sender->metaObject();signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signalName, signalTypes.size(), signalTypes.constData());}//如果相对序号不能存在,normalized后,再计算一次信号函数的相对序号if (signal_index < 0) {err_method_notfound(sender, signal_arg, "connect");err_info_about_objects("connect", sender, receiver);return QMetaObject::Connection(0);}//如果normalized后,信号函数还不存在,返回空连接signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);signal_index += QMetaObjectPrivate::signalOffset(smeta);//将signal_index加上偏移,变为绝对序号QByteArray tmp_method_name;int membcode = extract_code(method);if (!check_method_code(membcode, receiver, method, "connect"))return QMetaObject::Connection(0);//检查槽函数是否有字符1const char *method_arg = method;++method; //处理方法同信号QArgumentTypeArray methodTypes;QByteArray methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);const QMetaObject *rmeta = receiver->metaObject();int method_index_relative = -1;Q_ASSERT(QMetaObjectPrivate::get(rmeta)->revision >= 7);switch (membcode) {//根据membcode是1还是2计算槽函数的相对序号,因为信号可以连接信号,所以在考虑槽函数的时候要处理槽函数是信号的情况case QSLOT_CODE:method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, methodName, methodTypes.size(), methodTypes.constData());break;case QSIGNAL_CODE:method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, methodName, methodTypes.size(), methodTypes.constData());break;}if (method_index_relative < 0) {//如果得到的槽函数的相对序号不合法,normalized并再计算槽函数的相对序号// check for normalized methodstmp_method_name = QMetaObject::normalizedSignature(method);method = tmp_method_name.constData();methodTypes.clear();methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);// rmeta may have been modified abovermeta = receiver->metaObject();switch (membcode) {case QSLOT_CODE:method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, methodName, methodTypes.size(), methodTypes.constData());break;case QSIGNAL_CODE:method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, methodName, methodTypes.size(), methodTypes.constData());break;}}if (method_index_relative < 0) {//再次判断,如果槽函数的序号还是不合法的,返回空连接err_method_notfound(receiver, method_arg, "connect");err_info_about_objects("connect", sender, receiver);return QMetaObject::Connection(0);}if (!QMetaObjectPrivate::checkConnectArgs(signalTypes.size(), signalTypes.constData(),methodTypes.size(), methodTypes.constData())) {qWarning("QObject::connect: Incompatible sender/receiver arguments""\n %s::%s --> %s::%s",sender->metaObject()->className(), signal,receiver->metaObject()->className(), method);return QMetaObject::Connection(0);}//检查信号函数和槽函数的参数是否兼容,不兼容,返回空连接int *types = 0;if ((type == Qt::QueuedConnection)&& !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size()))) {return QMetaObject::Connection(0);}//如果此时信号槽的连接方式是QueuedConnection,但是queuedConnectionTypes的返回值为空(信号的类型数据没有获取到),那么也返回一个空连接#ifndef QT_NO_DEBUGQMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());check_and_warn_compat(smeta, smethod, rmeta, rmethod);
#endifQMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));//前面的check都没问题,进行连接,调用QMetaObjectPrivate::connectreturn handle;
}
QMetaObjectPrivate::connect也在qobject.cpp中
QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,int signal_index, const QMetaObject *smeta,const QObject *receiver, int method_index,const QMetaObject *rmeta, int type, int *types)
//sender 是发送信号的对象;signal_index 信号绝对序号;smeta 是发送信号的对象的元对象;
//receiver 是信号的接收对象;method_index 是槽函数的相对序号;rmeta 是信号的接收对象的元对象;
//type 是连接类型;types 是指向的是入队时,信号的类型数据。
{QObject *s = const_cast<QObject *>(sender);QObject *r = const_cast<QObject *>(receiver);//去底层constint method_offset = rmeta ? rmeta->methodOffset() : 0;//加上偏移量之后,得到槽函数的绝对序号Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 6);QObjectPrivate::StaticMetaCallFunction callFunction = rmeta ? rmeta->d.static_metacall : nullptr;//函数指针指向接收端元对象的qt_static_metacall(在MOC文件中实现)QOrderedMutexLocker locker(signalSlotLock(sender),signalSlotLock(receiver));//互斥锁,为了多线程顺序访问QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.loadRelaxed();//获取sender的ConnectionDataif (type & Qt::UniqueConnection && scd) {//如果连接类型有UniqueConnection并且sender的ConnectionData有数据if (scd->signalVectorCount() > signal_index) {//判断信号的绝对索引小于信号的数量const QObjectPrivate::Connection *c2 = scd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();int method_index_absolute = method_index + method_offset;while (c2) {if (!c2->isSlotObject && c2->receiver.loadRelaxed() == receiver && c2->method() == method_index_absolute)return nullptr;c2 = c2->nextConnectionList.loadRelaxed();}//遍历ConnectionList,如果发现ConnectionList中的receiver和传入的receiver相等并且该connection中的方法索引和绝对索引相等,那么就认为不满足UniqueConnection,直接退出}type &= Qt::UniqueConnection - 1;}std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection};//创建一个QObjectPrivate::Connection并对其中的数据进行赋值c->sender = s;c->signal_index = signal_index;c->receiver.storeRelaxed(r);QThreadData *td = r->d_func()->threadData;td->ref();c->receiverThreadData.storeRelaxed(td);c->method_relative = method_index;c->method_offset = method_offset;c->connectionType = type;c->isSlotObject = false;c->argumentTypes.storeRelaxed(types);c->callFunction = callFunction;QObjectPrivate::get(s)->addConnection(signal_index, c.get());//根据信号的绝对序号,添加QObjectPrivate::Connection对象locker.unlock();//解锁QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);if (smethod.isValid())s->connectNotify(smethod);return c.release();
}
上述代码中的loadrelaxed调用的其实就是C++11中的atomic类中的load方法,返回的是其中类或结构中包含的数据,详细见http://www.cplusplus.com/reference/atomic/atomic/load/
addConnection实现如下
void QObjectPrivate::addConnection(int signal, Connection *c)
{Q_ASSERT(c->sender == q_ptr);ensureConnectionData();ConnectionData *cd = connections.loadRelaxed();cd->resizeSignalVector(signal + 1);//为新信号的Connectionlist分配内存ConnectionList &connectionList = cd->connectionsForSignal(signal);//找到信号的connectionListif (connectionList.last.loadRelaxed()) {//添加新的信号的connectionlistQ_ASSERT(connectionList.last.loadRelaxed()->receiver.loadRelaxed());connectionList.last.loadRelaxed()->nextConnectionList.storeRelaxed(c);} else {connectionList.first.storeRelaxed(c);}c->id = ++cd->currentConnectionId;//设置新的connection的idc->prevConnectionList = connectionList.last.loadRelaxed();//设置新的connection的prevConnectionListconnectionList.last.storeRelaxed(c);QObjectPrivate *rd = QObjectPrivate::get(c->receiver.loadRelaxed());rd->ensureConnectionData();c->prev = &(rd->connections.loadRelaxed()->senders);c->next = *c->prev;//将新的connection添加到对应的信号的connectionlist中*c->prev = c;if (c->next)c->next->prev = &c->next;
}
connection与connectionlist的整体的结构图如下
参考
Qt5.14源码
https://qtguide.ustclug.org/
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出
4.QT4中的connect的实现相关推荐
- 5.QT5中的connect的实现
在QT4中,解析信号槽是通过将信号槽的名字转化为字符串,然后通过connect解析该字符串,得到信号函数的相对序号和,然后创建信号connectionlist,但是,所有的检查都在运行时,通过解析字符 ...
- ORA-01436: 用户数据中的CONNECT BY 循环
起始地 目的地 距离(公里) A B 1000 A C 1100 A ...
- 在Qt4中使用QPersistentModelIndex传递QModelIndex
在Qt4的model/view架构中,用的非常多的是QModelIndex,但这个类是动态的,可以说不可以传递的,因为随着view的变化,相同结点的QModelIndex也可能不同,非常容易失效,甚至 ...
- ITK:在二进制映像中标记Connect组件
ITK:在二进制映像中标记Connect组件 内容提要 C++实现代码 内容提要 在二进制映像中标记连接的组件. C++实现代码 #include "itkLiThresholdImageF ...
- QT3与QT4中uic的使用差异---李家凯老师
1. QT3 在QT3中,用designer设计好界面后,使用uic来根据界面生成代码文件,QT3的uic可以自动生成.h和.cpp文件,假设你在制作一个名叫DemoDlg的对话框,生成文件名为De ...
- 数据库异常---ORA-01436: 用户数据中的 CONNECT BY loop in user data 循环
数据库 ORA-01436: 用户数据中的 CONNECT BY loop in user data 循环 技术qq交流群:JavaDream:251572072 教程下载,在线交流:创梦IT社 ...
- QT下信号与槽不在同一个线程中如何connect
QT下信号与槽不在同一个线程中如何connect
- react-redux中的connect
将redux中的state与react中组件中的属性或者方法作绑定. 1.createConnect 作为高阶函数,创建Connect. function createConnect({connect ...
- UDP socket编程中使用connect
转自:http://hi.baidu.com/rwen2012/item/545a39ba741307d085dd7957 标准的udp客户端开了套接口后,一般使用sendto和recvfrom函数来 ...
最新文章
- c语言编程 构建围墙,c语言程序设计朝盛 综合程序练习题.ppt
- 首例基因编辑干细胞治疗艾滋病:北大邓宏魁参与,达到最佳治疗效果
- Redis笔记之常用命令
- 这是一位川大零基础转行 Python 的人生勇士
- 【ArcGIS风暴】ArcGIS中制作GPS点位轨迹线及多边形
- 前端学习(2971):静态打包资源
- 挣值管理:PV,AC和EV
- 用R语言实现密度聚类dbscan
- 如何在工作中快速成长?阿里资深架构师给工程师的10个简单技巧
- Inceptor上存储过程相关
- Mike and Cellphone
- 企业微信集成自建应用——踩坑记录
- 命令行里打 cd 简直是浪费生命
- 视频教程-微信公众平台深度开发v2.0第3季——二维码、模板消息-微信开发
- WPf 带滚动条WrapPanel 自动换行 和控件右键菜单
- java制作坦克大战
- Nanopi NEO Core测试
- php 模拟登录qq空间,PHP模拟登录QQ空间的例子
- 计算机网络与协议分析,计算机网络-使用网络协议分析器捕捉和分析协议数据包...
- 一、slowfast 代码复现