回调函数与Qt信号槽
Posted 从前,有个傻子........
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了回调函数与Qt信号槽相关的知识,希望对你有一定的参考价值。
回调函数的本质是基于“想让别人的代码执行自己的代码,而别人的代码你不能随意改变”这种思维产生的,回调函数是函数指针的一种用法,如果多个类都关注某个类的状态变化,此时需要维护一个列表,以存放多个回调函数的地址,对于每一个被关注的类,都需要做类似的工作,效率低且不灵活。
Qt为了消除回调函数的弊端,开发了一种新的消息传递机制,即信号和槽。
这种机制能够自动有效的组织和管理继承自QObject的Qt对象,被称为对象树。这种机制在UI编程上具备优势,能够帮助程序员环节内存泄漏问题,当应用程序创建了一个具有父窗口部件的对象时,该对象被加入父窗口的child列表,当父窗口被销毁时,child列表中的对象被一一删除,子类释放的顺序与构造顺序相反。
当一个对象的内部状态发生改变时,如果其他对象对它的状态改变需要作出反应,只需让这个类发出状态改变的信号。
信号的注意点:
1.所有信号的声明都是公有的,所以不能在signals前面加public,private,proteed。
2.信号没有返回值。
3.信号不需要定义。
4.必须直接或间接继承自QObject类,并且开头私有声明包含Q_OBJECT
5.在同一线程中,一个信号被emit,会立即执行slot函数,slot函数执行结束后才执行emit后面的代码,若信号连接多个槽,等所有槽函数执行结束后继续执行后面代码,槽函数执行顺序按照连接顺序,不同线程即跨线程时,槽函数的执行是随机的。
6.可将信号与槽的连接方式进行设置,发出信号不需等待槽函数执行完,而是直接执行后面代码,通过设置connect第5个参数。
7.信号与槽要求参数一致,即参数类型一致,若不一致,允许信号的参数比槽函数多,但槽函数的参数顺序必须与信号的前几个一致。槽函数参数可以比信号少,但不能使用信号中不存在的参数。
槽函数是一个普通的C++函数,可以是虚函数,static函数,也可以被重载,可以公有,私有,也可以被其他函数调用,唯一特点就是可以和信号连接。
Connect函数,有两个原型
static QMtaObject::Connect connect
const QObject *Sender,//信号发送对象指针
const char *signal,//信号函数字符串,使用SIGNAL()
const QObject *receiver,//槽函数对象指针
const char *member,//槽函数字符串,使用SLOT()
Qt::ConnectionType = Qt::AutoConnection);
connect(pushButton,SIGNAL(clicked()),dialog,SLOT(close()));
static QMtaObject::Connect connect
const QObject *Sender,//信号发送对象指针
const char &signal,//信号函数字符串,使用SIGNAL()
const QObject *receiver,//槽函数对象指针
const char &member,//槽函数字符串,使用SLOT()
Qt::ConnectionType = Qt::AutoConnection);
connect(pushButton,&QPushButton::clicked,dialog,&QDialog::close);
此方式在编译期间可以进行拼写检查,参数检查,类型检查,并且支持参数的兼容性转换。
Qt源码阅读 信号槽的连接与调用
信号槽连接
信号槽的连接,其实内部本质还是一个回调函数,主要是维护了信号发送Object的元对象里一个连接的列表。调用connect
函数时,将槽的一系列信息,封装成一个Connection
,在发送信号时,通过这个列表,去回调槽函数。
1. 信号的连接
下面列举一种信号的连接方式,来大致讲解一下信号的连接过程。
//Connect a signal to a pointer to qobject member function
// QtPrivate::FunctionPointer<Func1>::Object返回发送信号的对象类型
template <typename Func1, typename Func2>
static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot,
Qt::ConnectionType type = Qt::AutoConnection)
typedef QtPrivate::FunctionPointer<Func1> SignalType;
typedef QtPrivate::FunctionPointer<Func2> SlotType;
Q_STATIC_ASSERT_X(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::Value,
"No Q_OBJECT in the class with the signal");
//compilation error if the arguments does not match.
// 检查信号和槽参数是否一致
Q_STATIC_ASSERT_X(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
"The slot requires more arguments than the signal provides.");
// 检查信号和槽参数是否兼容
Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments, typename SlotType::Arguments>::value),
"Signal and slot arguments are not compatible.");
// 检查信号和槽的返回值是否兼容
Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
"Return type of the slot is not compatible with the return type of the signal.");
const int *types = nullptr;
// SignalType -> QtPrivate::FunctionPointer<Func1>
// QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types() 返回信号参数的值对应的元类型id列表
if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
return connectImpl(sender, reinterpret_cast<void **>(&signal),
receiver, reinterpret_cast<void **>(&slot),
new QtPrivate::QSlotObject<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
typename SignalType::ReturnType>(slot),
type, types, &SignalType::Object::staticMetaObject);
上面主要都是一些基本的信号连接的判断,主要是:
- 信号和槽的参数数量
- 信号和槽的参数是否兼容
- 信号和槽的返回值是否兼容
然后获取信号参数所对应的元类型Id,再就到了一个信号连接的具体内部实现中
QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signal,
const QObject *receiver, void **slot,
QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
const int *types, const QMetaObject *senderMetaObject)
if (!signal)
qWarning("QObject::connect: invalid nullptr parameter");
if (slotObj)
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
int signal_index = -1;
void *args[] = &signal_index, signal ;
// 根据调用来判断是否存在信号,如果当前类没有就去父类中寻找
// 直到找到信号或者是最基层的类
// 找到信号的index和信号的对象
for (; senderMetaObject && signal_index < 0; senderMetaObject = senderMetaObject->superClass())
senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args);
if (signal_index >= 0 && signal_index < QMetaObjectPrivate::get(senderMetaObject)->signalCount)
break;
if (!senderMetaObject)
qWarning("QObject::connect: signal not found in %s", sender->metaObject()->className());
slotObj->destroyIfLastRef();
return QMetaObject::Connection(nullptr);
// 信号下标
signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject);
return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj, type, types, senderMetaObject);
同样,我们对这个函数进行分析,第一个片段是对信号发送者是否为空指针的一个判断
if (!signal)
qWarning("QObject::connect: invalid nullptr parameter");
if (slotObj)
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
第二个片段是去找到信号发送者(sender)的元对象类型(Meta Object)以及信号在对象信号中的位置。如果当前对象没有该信号,就去其父类对象去找。直到找到为止。
for (; senderMetaObject && signal_index < 0; senderMetaObject = senderMetaObject->superClass())
senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args);
if (signal_index >= 0 && signal_index < QMetaObjectPrivate::get(senderMetaObject)->signalCount)
break;
然后就是进一步调用其内部实现:
QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int signal_index,
const QObject *receiver, void **slot,
QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
const int *types, const QMetaObject *senderMetaObject)
// 发送对象、接收对象、槽函数对象、信号发送的元对象都不为空 2023-3-11
if (!sender || !receiver || !slotObj || !senderMetaObject)
// 任意一个为空,报错且清理空间,并返回
const char *senderString = sender ? sender->metaObject()->className()
: senderMetaObject ? senderMetaObject->className()
: "Unknown";
const char *receiverString = receiver ? receiver->metaObject()->className()
: "Unknown";
qWarning("QObject::connect(%s, %s): invalid nullptr parameter", senderString, receiverString);
if (slotObj)
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
// 去掉const的发送和接受对象
QObject *s = const_cast<QObject *>(sender);
QObject *r = const_cast<QObject *>(receiver);
// 顺序锁,按照顺序依次去对mutex去上锁
// 这里依次对发送和接收者的信号去上锁
QOrderedMutexLocker locker(signalSlotLock(sender),
signalSlotLock(receiver));
if (type & Qt::UniqueConnection && slot && QObjectPrivate::get(s)->connections.loadRelaxed())
// ObjectPrivate::get(s) 获取s对应的d指针
// connections 维护了所有的信号槽连接
QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.loadRelaxed();
if (connections->signalVectorCount() > signal_index)
// 获取信号的连接
const QObjectPrivate::Connection *c2 = connections->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
// 循环遍历
while (c2)
// 如果已经存在信号和槽的连接,且为uniqueConnection,则返回
if (c2->receiver.loadRelaxed() == receiver && c2->isSlotObject && c2->slotObj->compare(slot))
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
c2 = c2->nextConnectionList.loadRelaxed();
// 将type与UniqueConnection进行异或,去掉UniqueConnection
type = static_cast<Qt::ConnectionType>(type ^ Qt::UniqueConnection);
// 创建一个新的连接
std::unique_ptr<QObjectPrivate::Connection> cnew QObjectPrivate::Connection;
c->sender = s;
c->signal_index = signal_index;
QThreadData *td = r->d_func()->threadData;
td->ref();
c->receiverThreadData.storeRelaxed(td);
c->receiver.storeRelaxed(r);
c->slotObj = slotObj;
c->connectionType = type;
c->isSlotObject = true;
if (types)
c->argumentTypes.storeRelaxed(types);
c->ownArgumentTypes = false;
// 将新创建的连接加到连接列表中
QObjectPrivate::get(s)->addConnection(signal_index, c.get());
QMetaObject::Connection ret(c.release());
locker.unlock();
QMetaMethod method = QMetaObjectPrivate::signal(senderMetaObject, signal_index);
Q_ASSERT(method.isValid());
s->connectNotify(method);
return ret;
同样第一个部分也是对一些个空值的判断
// 发送对象、接收对象、槽函数对象、信号发送的元对象都不为空 2023-3-11
if (!sender || !receiver || !slotObj || !senderMetaObject)
// 任意一个为空,报错且清理空间,并返回
const char *senderString = sender ? sender->metaObject()->className()
: senderMetaObject ? senderMetaObject->className()
: "Unknown";
const char *receiverString = receiver ? receiver->metaObject()->className()
: "Unknown";
qWarning("QObject::connect(%s, %s): invalid nullptr parameter", senderString, receiverString);
if (slotObj)
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
然后就是一个if判断,主要是对Qt::UniqueConnection
连接的一些处理,获取当前对象的信号连接列表,并判断当前要连接的信号和槽,之前有没有被连接过,如果有过连接,就直接返回。
if (type & Qt::UniqueConnection && slot && QObjectPrivate::get(s)->connections.loadRelaxed())
// ObjectPrivate::get(s) 获取s对应的d指针
// connections 维护了所有的信号槽连接
QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.loadRelaxed();
if (connections->signalVectorCount() > signal_index)
// 获取信号的连接
const QObjectPrivate::Connection *c2 = connections->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
// 循环遍历
while (c2)
// 如果已经存在信号和槽的连接,且为uniqueConnection,则返回
if (c2->receiver.loadRelaxed() == receiver && c2->isSlotObject && c2->slotObj->compare(slot))
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
c2 = c2->nextConnectionList.loadRelaxed();
// 将type与UniqueConnection进行异或,去掉UniqueConnection
type = static_cast<Qt::ConnectionType>(type ^ Qt::UniqueConnection);
最后才是创建一个Connection
并将连接的信息以及信号的参数设置进去,然后保存到对象的信号连接容器里。
// 创建一个新的连接
std::unique_ptr<QObjectPrivate::Connection> cnew QObjectPrivate::Connection;
c->sender = s;
c->signal_index = signal_index;
QThreadData *td = r->d_func()->threadData;
td->ref();
c->receiverThreadData.storeRelaxed(td);
c->receiver.storeRelaxed(r);
c->slotObj = slotObj;
c->connectionType = type;
c->isSlotObject = true;
if (types)
c->argumentTypes.storeRelaxed(types);
c->ownArgumentTypes = false;
// 将新创建的连接加到连接列表中
QObjectPrivate::get(s)->addConnection(signal_index, c.get());
QMetaObject::Connection ret(c.release());
locker.unlock();
QMetaMethod method = QMetaObjectPrivate::signal(senderMetaObject, signal_index);
Q_ASSERT(method.isValid());
s->connectNotify(method);
return ret;
2 槽的调用
定义一个信号,使用moc
生成moc文件之后,我们可以看到信号函数的定义如下:
// SIGNAL 0
void MainWindow::sgnTestFor()
QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
我们发射一个信号的时候,我们会这样写:
emit sgnTestFor();
我们可以看关于emit
的定义:
其实emit
关键字什么都没有做,只是标识了一下当前发射了信号。所以本质上,发射一个信号实际上就是直接调用了这个信号的函数,也就是调用了QMetaObject
中的activate
函数。
函数如下:
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
void **argv)
int signal_index = local_signal_index + QMetaObjectPrivate::signalOffset(m);
if (Q_UNLIKELY(qt_signal_spy_callback_set.loadRelaxed()))
doActivate<true>(sender, signal_index, argv);
else
doActivate<false>(sender, signal_index, argv);
上面的qt_signal_spy_callback_set
暂时不清楚是什么玩意,所以我们不管,直接看具体的doActive
函数
template <bool callbacks_enabled>
void doActivate(QObject *sender, int signal_index, void **argv)
// 首先获取QObject的private对象
QObjectPrivate *sp = QObjectPrivate::get(sender);
// 判断信号是否阻塞
if (sp->blockSig)
return;
Q_TRACE_SCOPE(QMetaObject_activate, sender, signal_index);
if (sp->isDeclarativeSignalConnected(signal_index)
&& QAbstractDeclarativeData::signalEmitted)
Q_TRACE_SCOPE(QMetaObject_activate_declarative_signal, sender, signal_index);
QAbstractDeclarativeData::signalEmitted(sp->declarativeData, sender,
signal_index, argv);
const QSignalSpyCallbackSet *signal_spy_set = callbacks_enabled ? qt_signal_spy_callback_set.loadAcquire() : nullptr;
void *empty_argv[] = nullptr ;
if (!argv)
argv = empty_argv;
if (!sp->maybeSignalConnected(signal_index))
// The possible declarative connection is done, and nothing else is connected
if (callbacks_enabled && signal_spy_set->signal_begin_callback != nullptr)
signal_spy_set->signal_begin_callback(sender, signal_index, argv);
if (callbacks_enabled && signal_spy_set->signal_end_callback != nullptr)
signal_spy_set->signal_end_callback(sender, signal_index);
return;
if (callbacks_enabled && signal_spy_set->signal_begin_callback != nullptr)
signal_spy_set->signal_begin_callback(sender, signal_index, argv);
bool senderDeleted = false;
Q_ASSERT(sp->connections.loadAcquire());
QObjectPrivate::ConnectionDataPointer connections(sp->connections.loadRelaxed());
QObjectPrivate::SignalVector *signalVector = connections->signalVector.loadRelaxed();
// 信号连接列表,因为一个信号可能连接了多个槽
const QObjectPrivate::ConnectionList *list;
if (signal_index < signalVector->count())
list = &signalVector->at(signal_index);
else
list = &signalVector->at(-1);
// 判断当前线程是不是信号发送者的线程
Qt::HANDLE currentThreadId = QThread::currentThreadId();
bool inSenderThread = currentThreadId == QObjectPrivate::get(sender)->threadData.loadRelaxed()->threadId.loadRelaxed();
//
// We need to check against the highest connection id to ensure that signals added
// during the signal emission are not emitted in this emission.
uint highestConnectionId = connections->currentConnectionId.loadRelaxed();
// 此处也就代表着,一个信号连接的多个槽函数,或者多个连接,会以连接的顺序被触发
do
QObjectPrivate::Connection *c = list->first.loadRelaxed();
if (!c)
continue;
do
QObject * const receiver = c->receiver.loadRelaxed();
if (!receiver)
continue;
QThreadData *td = c->receiverThreadData.loadRelaxed();
if (!td)
continue;
bool receiverInSameThread;
// 判断发送和接受是不是同一个线程
if (inSenderThread)
receiverInSameThread = currentThreadId == td->threadId.loadRelaxed();
else
// need to lock before reading the threadId, because moveToThread() could interfere
QMutexLocker lock(signalSlotLock(receiver));
receiverInSameThread = currentThreadId == td->threadId.loadRelaxed();
// 判断连接方式是否是队列连接,是队列连接就要丢入事件循环队列中处理
// determine if this connection should be sent immediately or
// put into the event queue
if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
|| (c->connectionType == Qt::QueuedConnection))
queued_activate(sender, signal_index, c, argv);
continue;
#if QT_CONFIG(thread)
else if (c->connectionType == Qt::BlockingQueuedConnection)
// 如果发送对象和接受对象在一个线程,使用BlockingQueuedConnection会导致死锁
if (receiverInSameThread)
qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: "
"Sender is %s(%p), receiver is %s(%p)",
sender->metaObject()->className(), sender,
receiver->metaObject()->className(), receiver);
QSemaphore semaphore;
QBasicMutexLocker locker(signalSlotLock(sender));
if (!c->receiver.loadAcquire())
continue;
QMetaCallEvent *ev = c->isSlotObject ?
new QMetaCallEvent(c->slotObj, sender, signal_index, argv, &semaphore) :
new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction,
sender, signal_index, argv, &semaphore);
QCoreApplication::postEvent(receiver, ev);
// 阻塞直至函数执行完成
semaphore.acquire();
continue;
#endif
// 下面是普通连接,
// 如果不在一个线程,并且使用直连,那么接收者就为空
QObjectPrivate::Sender senderData(receiverInSameThread ? receiver : nullptr, sender, signal_index);
// 如果是槽函数对象
if (c->isSlotObject)
c->slotObj->ref();
struct Deleter
void operator()(QtPrivate::QSlotObjectBase *slot) const
if (slot) slot->destroyIfLastRef();
;
const std::unique_ptr<QtPrivate::QSlotObjectBase, Deleter> objc->slotObj;
Q_TRACE_SCOPE(QMetaObject_activate_slot_functor, obj.get());
obj->call(receiver, argv);
else if (c->callFunction && c->method_offset <= receiver->metaObject()->methodOffset())
//we compare the vtable to make sure we are not in the destructor of the object.
const int method_relative = c->method_relative;
const auto callFunction = c->callFunction;
const int methodIndex = (Q_HAS_TRACEPOINTS || callbacks_enabled) ? c->method() : 0;
if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr)
signal_spy_set->slot_begin_callback(receiver, methodIndex, argv);
Q_TRACE_SCOPE(QMetaObject_activate_slot, receiver, methodIndex);
callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv);
if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr)
signal_spy_set->slot_end_callback(receiver, methodIndex);
else
const int method = c->method_relative + c->method_offset;
if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr)
signal_spy_set->slot_begin_callback(receiver, method, argv);
Q_TRACE_SCOPE(QMetaObject_activate_slot, receiver, method);
QMetaObject::metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv);
if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr)
signal_spy_set->slot_end_callback(receiver, method);
// 此处while是循环遍历信号所连接的槽/信号
while ((c = c->nextConnectionList.loadRelaxed()) != nullptr && c->id <= highestConnectionId);
// 循环两次
while (list != &signalVector->at(-1) &&
//start over for all signals;
((list = &signalVector->at(-1)), true));
if (connections->currentConnectionId.loadRelaxed() == 0)
senderDeleted = true;
if (!senderDeleted)
sp->connections.loadRelaxed()->cleanOrphanedConnections(sender);
if (callbacks_enabled && signal_spy_set->signal_end_callback != nullptr)
signal_spy_set->signal_end_callback(sender, signal_index);
前面的一些基本的判断,我们就忽略,直接找到重要的地方,循环遍历信号所连接的部分。
-
当信号槽为队列连接,我们需要将信号丢到事件循环里,待事件循环将该信号发送出去。
if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread) || (c->connectionType == Qt::QueuedConnection)) queued_activate(sender, signal_index, c, argv); continue; #if QT_CONFIG(thread)
-
当信号槽为阻塞队列连接(
BlockingQueuedConnection
)时,首先,我们需要判断发送和接收者是不是在一个线程,因为如果连接类型为BlockingQueuedConnection
,发送者和接收者在一个线程,会导致死锁。else if (c->connectionType == Qt::BlockingQueuedConnection) // 如果发送对象和接受对象在一个线程,使用BlockingQueuedConnection会导致死锁 if (receiverInSameThread) qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: " "Sender is %s(%p), receiver is %s(%p)", sender->metaObject()->className(), sender, receiver->metaObject()->className(), receiver); QSemaphore semaphore; QBasicMutexLocker locker(signalSlotLock(sender)); if (!c->receiver.loadAcquire()) continue; QMetaCallEvent *ev = c->isSlotObject ? new QMetaCallEvent(c->slotObj, sender, signal_index, argv, &semaphore) : new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal_index, argv, &semaphore); QCoreApplication::postEvent(receiver, ev); // 阻塞直至函数执行完成 semaphore.acquire(); continue; #endif
其他类型的连接如下:
-
信号的连接是一个槽函数对象
QSlotObject
,就直接调用call
函数if (c->isSlotObject) c->slotObj->ref(); struct Deleter void operator()(QtPrivate::QSlotObjectBase *slot) const if (slot) slot->destroyIfLastRef(); ; const std::unique_ptr<QtPrivate::QSlotObjectBase, Deleter> objc->slotObj; Q_TRACE_SCOPE(QMetaObject_activate_slot_functor, obj.get()); obj->call(receiver, argv);
-
如果是其他类型,就通过
QMetaObject::InvokeMetaMethod
来调用else if (c->callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) //we compare the vtable to make sure we are not in the destructor of the object. const int method_relative = c->method_relative; const auto callFunction = c->callFunction; const int methodIndex = (Q_HAS_TRACEPOINTS || callbacks_enabled) ? c->method() : 0; if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr) signal_spy_set->slot_begin_callback(receiver, methodIndex, argv); Q_TRACE_SCOPE(QMetaObject_activate_slot, receiver, methodIndex); callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv); if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr) signal_spy_set->slot_end_callback(receiver, methodIndex); else const int method = c->method_relative + c->method_offset; if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr) signal_spy_set->slot_begin_callback(receiver, method, argv); Q_TRACE_SCOPE(QMetaObject_activate_slot, receiver, method); QMetaObject::metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv); if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr) signal_spy_set->slot_end_callback(receiver, method);
并且遍历整个列表,将所有相关的连接都调用一遍。
然后我们看QueuedConnection
的连接函数:
代码里,揭示了一点,就是如果我们使用信号槽连接的方式,而信号的参数不是一个元类型或者没用qRegisterMetaType
来注册类型,那么队列连接是不行的,槽函数是不会触发的。
static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv)
// 存储元类型参数(meta-type argument)
const int *argumentTypes = c->argumentTypes.loadRelaxed();
if (!argumentTypes)
// 获取对应的信号
QMetaMethod m = QMetaObjectPrivate::signal(sender->metaObject(), signal);
// 获取信号的参数,并检查是否所有参数均为元类型(meta-type)
argumentTypes = queuedConnectionTypes(m.parameterTypes());
if (!argumentTypes) // cannot queue arguments
argumentTypes = &DIRECT_CONNECTION_ONLY;
if (!c->argumentTypes.testAndSetOrdered(nullptr, argumentTypes))
if (argumentTypes != &DIRECT_CONNECTION_ONLY)
delete [] argumentTypes;
argumentTypes = c->argumentTypes.loadRelaxed();
// 参数不符合要求,返回
if (argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate
return;
int nargs = 1; // include return type
while (argumentTypes[nargs-1])
++nargs;
QBasicMutexLocker locker(signalSlotLock(c->receiver.loadRelaxed()));
if (!c->receiver.loadRelaxed())
// the connection has been disconnected before we got the lock
return;
if (c->isSlotObject)
c->slotObj->ref();
locker.unlock();
// 然后通过post一个QMetaCallEvent事件到事件循环队列中去
QMetaCallEvent *ev = c->isSlotObject ?
new QMetaCallEvent(c->slotObj, sender, signal, nargs) :
new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal, nargs);
void **args = ev->args();
int *types = ev->types();
types[0] = 0; // return type
args[0] = nullptr; // return value
if (nargs > 1)
for (int n = 1; n < nargs; ++n)
types[n] = argumentTypes[n-1];
for (int n = 1; n < nargs; ++n)
args[n] = QMetaType::create(types[n], argv[n]);
locker.relock();
if (c->isSlotObject)
c->slotObj->destroyIfLastRef();
if (!c->receiver.loadRelaxed())
// the connection has been disconnected while we were unlocked
locker.unlock();
delete ev;
return;
QCoreApplication::postEvent(c->receiver.loadRelaxed(), ev);
代码中我们可以看到,这里是通过post一个QMetaCallEvent
的事件到事件循环中,然后由事件循环去触发槽函数的调用。
好了,对于信号和槽的分析,我们暂时就先分析到这,如果有问题是我上面没有说明的,可以在评论区给我评论,我看到了,看懂了,我就会更新这篇博客的。
谢谢观看
以上是关于回调函数与Qt信号槽的主要内容,如果未能解决你的问题,请参考以下文章