Sign up for the KDAB Newsletter
Stay on top of the latest news, publications, events and more.
Go to Sign-up
Sometimes it's useful to establish a connection between a signal and a slot that should be activated only once. This is not how signal/slot connections normally behave. Remember that, when a connection is established, it will stay active until:
So, if we need such a single-shot connection, how do we go about it? A possible solution is to connect our signal to a small helper function (for instance, a lambda) that disconnects the connection, then calls the actual slot. This is it, in pseudocode:
// Trying to make
// connect(sender, &Sender::signal, receiver, &Receiver::slot)
// behave as single-shot connection
auto singleShot = [receiver, connection](parameters) {
QObject::disconnect(connection); // WHOPS, we don't have this yet!
receiver->slot(parameters);
};
connection = connect(sender, &Sender::signal, receiver, std::move(singleShot));
There's a semantic problem with this code: the connection
object is created after the lambda. But we need its value in the lambda, to be able to disconnect it! Fixing this requires us to introduce a little indirection, but it's doable:
auto connection = std::make_unique<QMetaObject::Connection>();
auto connectionPtr = connection.get();
auto singleShot = [receiver, connection = std::move(connection)](parameters) {
QObject::disconnect(*connection);
receiver->slot(parameters);
};
*connectionPtr = connect(sender, &Sender::signal, receiver, std::move(singleShot)));
This is...quite annoying to write. I certainly don't want to type all of that every time I need a single-shot connection, and trying to make it generic is super tricky for new users of Qt.
We can do better!
In Qt 6, I have added the convenience Qt::SingleShotConnection
connection flag that you can pass as the fifth argument of QObject::connect (optionally combining it with the other connection types):
connect(sender, &Sender::signal,
receiver, &Receiver::slot,
static_cast<Qt::ConnectionType>(Qt::SingleShotConnection));
The static_cast
isn't technically necessary, in this case. But it becomes necessary, should we want to also pass some other arguments (for instance, if we want the connection to be queued as well as single-shot). This closed a long-standing and very voted feature request. Sometimes, by removing the pebble in your shoe, you make many other people happy.
I can't add the same feature to Qt 5, as Qt 5 development is closed for new features.
However, I've also reimplemented the solution shown above in KDToolBox and packaged it with a convenient API:
KDToolBox::connectSingleShot(sender, &Sender::signal, receiver, &Receiver::slot);
sender->causeSignalEmission(); // calls the slot, and breaks the connection
sender->causeSignalEmission(); // does NOT call the slot
About KDAB
The KDAB Group is a globally recognized provider for software consulting, development and training, specializing in embedded devices and complex cross-platform desktop applications. In addition to being leading experts in Qt, C++ and 3D technologies for over two decades, KDAB provides deep expertise across the stack, including Linux, Rust and modern UI frameworks. With 100+ employees from 20 countries and offices in Sweden, Germany, USA, France and UK, we serve clients around the world.
Stay on top of the latest news, publications, events and more.
Go to Sign-up
Upgrade your applications from Qt 5 to Qt 6 with KDAB’s migration services. Get a free migration assessment and join a hands-on workshop to prepare your team for a successful transition!
Learn more
3 Comments
16 - Apr - 2021
Kelteseth
So this is like QTimer::singleShot()?
16 - Apr - 2021
Giuseppe D'Angelo
Hello,
I'm not sure what you mean. Could you elaborate, how would you use singleShot to achieve the same result?
2 - Dec - 2021
Y Malaika
I don't think so.
QTimer::singleshot() invokes the slot without establishing a connection, either immediately or with a specified delay.
What is presented here are ways to establish a latent, single use only connection without invoking it. When and if it does get invoked, at some currently unspecified time by some currently unspecified signal, it will automatically disconnect itself from that signal. Neither the sender nor the receiver need be aware of this or have their code burdened by it, and that means you are free to place the entirety of the glue logic somewhere else should you desire to do so. At least that's my understanding.