Skip to content

Single-Shot Signal/Slot Connections

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:

  • the connection is explicitly disconnected; or
  • the sender object is destroyed; or
  • the receiver object is destroyed.

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!

Enter Qt 6.0!

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.

And for Qt 5?

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

KDToolBox is KDAB’s collection of miscellaneous useful C++ classes and stuff. You can download it from our GitHub repository here.

Have fun!

About KDAB

If you like this article and want to read similar material, consider subscribing via our RSS feed.

Subscribe to KDAB TV for similar informative short video content.

KDAB provides market leading software consulting and development services and training in Qt, C++ and 3D/OpenGL. Contact us.

3 thoughts on “Single-Shot Signal/Slot Connections”

    1. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *