I have such problem. In my threaded application I need to disconnect slot and reconnect signal to another slot. Unfortunately if new signals send before disconnecting it will be handled in the old slot. Such behaviour is not described in Qt documentation and looks like a bug. So here example code how to reproduce this.
void Tester::start()
{
connect(this, &Tester::signal, this, &Tester::slot, Qt::ConnectionType::QueuedConnection);
QTimer::singleShot(0, this, [=]() {
emit signal();
emit signal();
});
QTimer::singleShot(1000, this, [=]() {
emit signal();
});
QTimer::singleShot(2000, qApp, &QCoreApplication::quit);
}
void Tester::slot()
{
qDebug() << "Invoke slot";
disconnect(this, &Tester::signal, this, &Tester::slot);
connect(this, &Tester::signal, this, &Tester::slot2);
}
void Tester::slot2()
{
qDebug() << "Invoke slot2";
}
Output:
Invoke slot
Invoke slot
Invoke slot2
Invoke slot2
I expect
Invoke slot
Invoke slot2
Invoke slot2
So there is a way to fix this in global sense? Or I can only to handle this in each particular case?
CodePudding user response:
This is not a bug. Once the signal is emitted, the corresponding event requesting calling the connected slot is put to the queue in the receiver thread's even loop. When you disconnect the signal, there is no way the event loop queue to be cleaned from the already enqueued events. So when the event loop gets to processing of this event, it calls the original slot. For more details how this works read https://woboq.com/blog/how-qt-signals-slots-work-part3-queuedconnection.html
I do not understand why you decided for this strange and fragile design. I do not know your specific usecase. But in general I would probably use two different signals sent one after the other, each connected to a different slot.
CodePudding user response:
emphasized textBy setting the connection as a queued connection, you’ve deferred the invitation of the slot until later. By the time slot
runs for the first time, you’ve already called the second emit
, queueing a second call to slot
. If you want your first timer callback to wait until the slot invocation is complete before returning from the emit
(thereby doing it on the subsequent state of the signal), don’t use a queued connection.