Destroy the signal/slot connection when the slot receives the signal
-
Is there a way to destroy the signal/slot connection once the signal has been triggered so this connection only gets triggered once? I came across the following code:
std::unique_ptr<QObject> context{ new QObject };
QObject* pcontext = context.get();
auto connection =
connect(this,
&SenderClass::send,
pcontext,
this, context = std::move(context) mutable
{
context.reset();
// do something when received the signal
});When context is reset, the connection is destroyed. Is this a bad practice? It seems to be working alright but some developer thinks it is dangerous because pcontext owns that lambda function which destroys itself.
or is there another way to achieve the same behaviour?
-
@johnyang Please format your code properly.
You can simply call disconnect() (https://doc.qt.io/qt-6/qobject.html#disconnect). -
@johnyang I don't really see the reason why you would want a single shot connection?
Why don't you simply call the function directly?
That said, @jsulm is correct, calling disconnect is probably the correct approach. However it can be tricky and I would suggest using
QMetaObject::Connection
explicitlyQMetaObject::Connection conn; conn = QObject::connect(sender, &Sender::signal, [=]() mutable { // Slot code to be executed // ... // Disconnect the connection QObject::disconnect(conn); });
at least you don't end up with unnecessary object creations/allocations
-
@J-Hilk I'm trying to implement a single shot connection waiting for an asynchronous response from http server. I'm curious if the connection in your code will be cleared when it gets out of scope?
Should I do something like this instead:QMetaObject::Connection * const connection = new QMetaObject::Connection; *connection = connect(sender, &Sender::signal, [this, connection](){ QObject::disconnect(*connection); delete connection; });
-
@johnyang thats not needed, the framework manages the lambda internally. In this case the connection and lambda will be valid as long as the
sender
instance exists or disconnect was called.And stuff captured by lambdas does not go out of scope, as long as you don't explicitly capture by reference. As it makes its own copy.
-
There is also a single shot connection type...
-
@johnyang Is
Sender
a long lived object that sendssignal
multiple times?
If not it might not be necessary to disconnect from it, similar to how we usually connect a lambda toQNetworkReply::finished
without disconnecting after.
And like Christian just said there'sQt::SingleShotConnection
as parameter ofQObject::connect
since Qt 6.0 -
@GrecKo said in Destroy the signal/slot connection when the slot receives the signal:
Qt::SingleShotConnection as parameter of QObject::connect since Qt 6.0
Look at that!
-
@J-Hilk Unfortunately I'm still on Qt5 at the moment. I would love to get onto Qt6 soon. I have another question about using the disconnect approach. I have the following scenario:
QMetaObject::Connection * const connection = new QMetaObject::Connection; *connection = connect(sender, &Sender::signal, [this, connection](){ QObject::disconnect(*connection); delete connection; // Some function that also sends out Sender::signal Function(); });
Function() also sends out Sender::signal at the end. The above code seems to be working fine that the lambda function does not get triggered. However if I do the following, the lambda function does get triggered one more time (which is not what I want):
QMetaObject::Connection conn; conn = QObject::connect(sender, &Sender::signal, [=]() { // Disconnect the connection QObject::disconnect(conn); // Some function that also sends out Sender::signal Function(); });
Is it alright to do what I did in the code above?
-
@GrecKo Sender is a long lived object but I only want it to take place once for a logic reason. I am still on Qt5 at the moment so I can't use Qt::SingleShotConnection. However in my example code with unique pointer, is it a bad practice to reset context in the lambda function because it is owned by context?
QObject* pcontext = context.get(); connect(this, &SenderClass::send, pcontext, [this, context = std::move(context)] mutable { context.reset(); // do something when received the signal Function(); });
-
At the moment, the suggested code in https://www.kdab.com/single-shot-connections/ seems to work perfectly so far:
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)));