I'm using Qt 5.9.2 with Visual Studio 2015 and QtDesigner for programming a Windows GUI application. I tried connecting one of my actions via the following call:
connect(ui.myAction, &QAction::triggered, memberPtrToObjX_, &ClassX::Run);
However ClassX::Run is not always triggered after clicking on myAction in the menubar. Investigating into this problem, I figured, that the same signal-slot connection using lambda syntax works:
connect(ui.myAction, &QAction::triggered, [this](bool run) { memberPtrToObjX_->Run(run); });
I'm pretty sure, that both calls are syntactically correct. Besides both calls return a valid QMetaObject::Connection, if I save the return value and check with operator bool().
Obviously I could just stick with the working lambda-version, but I'm confused and would prefer knowing the reason behind my "solution". Is there any functional difference between these two calls, that explains the different behaviour?
The two calls of QObject::connect() (exposed by the OP) behave differently in the case that this->memberPtrToObjX_ is modified after the call of connect().
The first
connect(ui.myAction, &QAction::triggered, memberPtrToObjX_, &ClassX::Run);
calls
QMetaObject::Connection QObject::connect( const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type = Qt::AutoConnection).
Creates a connection of the given type from the signal in the sender object to the method in the receiver object. Returns a handle to the connection that can be used to disconnect it later.
Hence, the current pointer in this->memberPtrToObjX_ is connected as signal receiver. If this->memberPtrToObjX_ is modified after connect() this doesn't have any effect to the signal connection.
The second
connect(ui.myAction, &QAction::triggered, [this](bool run) { memberPtrToObjX_->Run(run); });
calls
QMetaObject::Connection QObject::connect( const QObject *sender, PointerToMemberFunction signal, Functor functor).
Creates a connection from signal in sender object to functor, and returns a handle to the connection.
Hence, the (functor behind the) lambda is connected as receiver. The lambda resolves the pointer in this->memberPtrToObjX_ at the time it is executed i.e. when the signal is triggered.
The second difference (which originally was uncovered in the comment of G.M.) is the connection type:
The first version uses the default value Qt::AutoConnection (as it is not defined explicitly). The version with the lambda uses always Qt::DirectConnection instead.
The difference appears if the pointee in this->memberPtrToObjX_ does not "live" in the same thread. In this case, the Qt::AutoConnection is resolved to Qt::QueuedConnection instead of Qt::DirectConnection.
I assumed that the pointee in this->memberPtrToObjX_ would "live" in the same QThread. If not, the second version (with the lambda) becomes very questionable as it calls the a member function of the object "living" in a different thread (where it is hard to tell what that thread is doing at this time). This only seems to work better but is very possibly a "time bomb".
Related
I have the following code that performs a background operation (scan_value) while updating a progress bar in the ui (progress). scan_value iterates over some value in obj, emitting a signal (value_changed) each time that the value is changed. For reasons which are not relevant here, I have to wrap this in an object (Scanner) in another thread. The Scanner is called when the a button scan is clicked. And here comes my question ... the following code works fine (i.e. the progress bar gets updated on time).
# I am copying only the relevant code here.
def update_progress_bar(new, old):
fraction = (new - start) / (stop - start)
progress.setValue(fraction * 100)
obj.value_changed.connect(update_progress_bar)
class Scanner(QObject):
def scan(self):
scan_value(start, stop, step)
progress.setValue(100)
thread = QThread()
scanner = Scanner()
scanner.moveToThread(thread)
thread.start()
scan.clicked.connect(scanner.scan)
But if I change the last part to this:
thread = QThread()
scanner = Scanner()
scan.clicked.connect(scanner.scan) # This was at the end!
scanner.moveToThread(thread)
thread.start()
The progress bar gets updated only at the end (my guess is that everything is running on the same thread). Should it be irrelevant if I connect the signal to a slot before of after moving the object receiving object to the Thread.
It shouldn't matter whether the connection is made before or after moving the worker object to the other thread. To quote from the Qt docs:
Qt::AutoConnection - If the signal is emitted from a different
thread than the receiving object, the signal is queued, behaving as
Qt::QueuedConnection. Otherwise, the slot is invoked directly,
behaving as Qt::DirectConnection. The type of connection is
determined when the signal is emitted. [emphasis added]
So, as long as the type argument of connect is set to QtCore.Qt.AutoConnection (which is the default), Qt should ensure that signals are emitted in the appropriate way.
The problem with the example code is more likely to be with the slot than the signal. The python method that the signal is connected to probably needs to be marked as a Qt slot, using the pyqtSlot decorator:
from QtCore import pyqtSlot
class Scanner(QObject):
#pyqtSlot()
def scan(self):
scan_value(start, stop, step)
progress.setValue(100)
EDIT:
It should be clarified that it's only in fairly recent versions of Qt that the type of connection is determined when the signal is emitted. This behaviour was introduced (along with several other changes in Qt's multithreading support) with version 4.4.
Also, it might be worth expanding further on the PyQt-specific issue. In PyQt, a signal can be connected to a Qt slot, another signal, or any python callable (including lambda functions). For the latter case, a proxy object is created internally that wraps the python callable and provides the slot that is required by the Qt signal/slot mechanism.
It is this proxy object that is the cause of the problem. Once the proxy is created, PyQt will simply do this:
if (rx_qobj)
proxy->moveToThread(rx_qobj->thread());
which is fine if the connection is made after the receiving object (i.e. rx_qobj) has been moved to its thread; but if it's made before, the proxy will stay in the main thread.
Using the #pyqtSlot decorator avoids this issue altogether, because it creates a Qt slot more directly and does not use a proxy object at all.
Finally, it should also be noted that this issue does not currently affect PySide.
My problem was solved by movinf the connection to the spot where the worker thread is initialized, in my case because I am accessing an object which only exists after instantiation of my Worker Object class which is in another thread.
Simply connect the signal after the self.createWorkerThread()
Regards
This has to do with the connection types of Qt.
http://pyqt.sourceforge.net/Docs/PyQt5/signals_slots.html#connect
http://qt-project.org/doc/qt-4.8/qt.html#ConnectionType-enum
In case both objects live in the same thread, a standard connection type is made, which results in a plain function call. In this case, the time consuming operation takes place in the GUI thread, and the interface blocks.
In case the connection type is a message passing style connection, the signal is emitted using a message which is handled in the other thread. The GUI thread is now free to update the user interface.
When you do not specify the connection type in the connect function, the type is automatically detected.
I repeatedly see people having problems with slots not being called. I would like to collect some of the most common reasons. So maybe I can help people and avoid a lot of redundant questions.
What are reasons for signal / slot connections not working? How can such problems be avoided?
There are some rules that make life with signals and slots easier and cover the most common reason for defective connections. If I forgot something please tell me.
1) Check the debug console output:
When execution errors occur, the debug output can show you the reason.
2) Use the full signature of signal and slot:
Instead of
connect(that, SIGNAL(mySignal), this, SLOT(mySlot));
write
connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
and check your spelling and capitalization.
3) Use existing overloads:
Carefully check if you are using the desired overloads of signal and slot and if the overloads you used actually exist.
4) Your signal and slot must be compatible:
This especially means the parameters must be of the same type (references are tolerated) and have the same order.
Compile-time syntax also needs the same number of parameters. Old runtime syntax allows connecting signals to slots with less parameters.
5) Always check return value of connect method (programmers should never ignore return values):
Instead of
connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
always use something like
bool success = connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
Q_ASSERT(success);
Or if you like throw an exception or implement full error handling. You may also use a macro like that:
#ifndef QT_NO_DEBUG
#define CHECK_TRUE(instruction) Q_ASSERT(instruction)
#else
#define CHECK_TRUE(instruction) (instruction)
#endif
CHECK_TRUE(connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int))));
6) You need an event loop for queued connections:
I.e. when ever you connect signals/slots of two objects owned by different threads (so called queued connections) you need to call exec(); in the slot's thread!
The event loop also needs to be actually served. Whenever the slot's thread is stuck in some kind of busy loop, queued connections are NOT executed!
7) You need register custom types for queued connections:
So when using custom types in queued connections you must register them for this purpose.
First declare the type using the following macro:
Q_DECLARE_METATYPE(MyType)
Then use one of the following calls:
qRegisterMetaType<MyTypedefType>("MyTypedefType"); // For typedef defined types
qRegisterMetaType<MyType>(); // For other types
8) Prefer new compile time syntax over old run-time checked syntax:
Instead of
connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
use this syntax
connect(that, &ThatObject::mySignal, this, &ThisObject::mySlot));
which checks signal and slot during compile time and even does not need the destination being an actual slot.
If your signal is overloaded use the following syntax:
connect(that, static_cast<void (ThatObject::*)(int)> &ThatObject::mySignal), this, &ThisObject::mySlot); // <Qt5.7
connect(that, qOverload<int>::of(&ThatObject::mySignal), this, &ThisObject::mySlot); // >=Qt5.7 & C++11
connect(that, qOverload<int>(&ThatObject::mySignal), this, &ThisObject::mySlot); // >=Qt5.7 & C++14
Starting with Qt5.14, overloaded signals are deprecated. Disable deprecated Qt features to get rid of the above shenanigans.
Also do not mix const/non-const signals/slots for that syntax (normally signals and slots will be non-const).
9) Your classes need a Q_OBJECT macro:
In classes where you are using "signals" and "slots" specifications you need to add a Q_OBJECT macro like this:
class SomeClass
{
Q_OBJECT
signals:
void MySignal(int x);
};
class SomeMoreClass
{
Q_OBJECT
public slots:
void MySlot(int x);
};
This macro adds necessary meta information to the class.
10) Your objects must be alive:
As soon as either the sender object or the receiver object is destroyed, Qt automatically discards the connection.
If the signal isn't emitted: Does the sender object still exist?
If the slot isn't called: Does the receiver object still exist?
To check the lifetime of both objects use a debugger break point or some qDebug() output in the constructors/destructors.
11) It still does not work:
To do a very quick and dirty check of your connection emit the signal by your self using some dummy arguments and see if it is called:
connect(that, SIGNAL(mySignal(int)), this, SLOT(mySlot(int)));
emit that->mySignal(0); // Ugly, don't forget to remove it immediately
Finally of course it is possible that the signal simply is not emitted. If you followed the above rules, probably something is wrong in your program's logic. Read the documentation. Use the debugger. And if there is now other way, ask at stackoverflow.
In my practice, I have encountered cases of incorrectly overriding eventFilter in the object receiving the signal. Some novice programmers forget to return "false" at the end of function. And thus do not allow the MetaCall event to pass to the receiving object. In this case, the signal is not processed at the receiving object.
Short answer
You (almost) don't have to worry about that anymore. Always use the QMetaMethod/Pointer to member prototype of connect, as it will fail at compile time if the signal and slot are not compatible.
connect(sourceObject, &SourceClass::signal, destObject, &DestClass::slot);
This prototype will only fail at runtime if the sourceObject or destObject is null (which is to be expected). But argument incompatibility will show up during compilation
Only rare situations require the older SIGNAL/SLOT literal-based syntax, so this should be your last resort.
Compatibility
The signatures are compatible if the following conditions are met:
You are connecting a signal to a slot or a signal
The destination signal/slot has the same number or less arguments than the source signal
Arguments of the source signal can be implicitly converted to the corresponding argument (matched in order) in the destination signal/slot, if used
Examples
OK - signalA(int, std::string) => signalC(int, std::string)
Note that we are connecting to a signal
OK - signalA(int, std::string) => slotB(int, std::string)
OK - signalA(int, std::string) => slotB(int)
String parameter ignored
OK - signalA(int, std::string) => slotB()
All parameters ignored
OK - signalA(int, const char*) => slotB(int, QString)
Implicitely converted with QString(const char*)
Fails - signalA(int, std::string) => slotB(std::string)
int not implicitely convertible to std::string
Fails - signalA(int, std::string) => slotB(std::string, int)
Incorrect order
Fails - signalA(int, std::string) => slotB(int, std::string, int)
Too many arguments on the right side
I am currently trying to understand the new QT5 signal/slot syntax
connect(sender, &Sender::valueChanged, [=](const QString &newValue) {
receiver->updateValue("senderValue", newValue);
});
Now my question is where is the address of the receiver SLOT in the above expression ? I wanted to know this because what happens if a signal is in threadA and the slot is in thread B and I wanted it to be a queued connection ?
A slot is a piece of code, it doesn't "live" in a thread - a thread might run it or not, but the code itself doesn't belong to any thread. (If the slot is a member function, then the Qt object defined as the receiver belongs to a Qt thread - that's a property of the object, not the function.)
In the code you have above, the compiler generates an object that:
captures receiver by value ([=])
has a function-call operator that can be called with a reference to a const QString.
That object is passed to connect along with the other two arguments. It's not a QObject, so it doesn't have an owning thread in the Qt sense. What you need to make sure of is that:
what receiver points to stays alive for as long as that signal is connected
receiver->updateValue(...) is thread-safe - it will be called in sender's context/thread.
If receiver->updateValue needs to be called in receiver's thread/context, then do not use that syntax for the connect call, use the one where you specify both sender and receiver, and the connection type.
I have a QT class instance, called C, (C inherits QOBJECT) that sends a signal S.
In my program, other QT classes instances X are created and destroyed when the program runs. These other classes connect and disconnect S, i.e. they run:
connect(C,SIGNAL(S()), this, SLOT(my_func())); // <this> is an instance of X
or
disconnect(C,SIGNAL(S()), this, SLOT(my_func()));
In class C, the calculation of whether S should be emitted (and the data associated to it - not shown here) is rather complicated, so I would like the instance of class C (which emits the signal) to be notified when one(or more) object are connected (listening) to S or when all are disconnected.
I have read about the connectNotify and disconnectNotify functions, but their usage is discouraged. Besides the documentation does not state very clearly if there is a one to one relationship between the number of (dis)connectNotify calls and the number of "listener" to the signal (or can one single connectNotify be called for more than one listener?).
Can I just count positively (count++) the number of connectNotify and negatively (count--) the number of disconnectNotify and just react to non-zero value?
Any better way to do this?
First, I think you've got it right that connectNotify and disconnectNotify can be used for this purpose - each connect event will be counted properly, even if it is a duplicate from the same object.
You can also double check this with QObject::receivers
int QObject::receivers ( const char * signal ) const [protected]
Returns the number of receivers connected to the signal. Since both
slots and signals can be used as receivers for signals, and the same
connections can be made many times, the number of receivers is the
same as the number of connections made from this signal. When calling
this function, you can use the SIGNAL() macro to pass a specific
signal: if (receivers(SIGNAL(valueChanged(QByteArray))) > 0) {
QByteArray data;
get_the_value(&data); // expensive operation
emit valueChanged(data); } As the code snippet above illustrates, you can use this function to avoid emitting a signal that
nobody listens to. Warning: This function violates the object-oriented
principle of modularity. However, it might be useful when you need to
perform expensive initialization only if something is connected to a
signal.
My suggestion would be to write a simple test program. Override connectNotify and disconnectNotify to increment/decrement a counter, but also use receivers to verify that the counter is correct. Try connecting multiple times, disconnecting multiple times, disconnecting even if there is no connection, etc.
Something to be careful of: connect and disconnect are thread-safe; I'm not sure if the matching Notify functions are safe also.
Since Qt 5.0, you can do this more easily with the QObject::isSignalConnected function. Example from the documentation:
static const QMetaMethod valueChangedSignal = QMetaMethod::fromSignal(&MyObject::valueChanged);
if (isSignalConnected(valueChangedSignal)) {
QByteArray data;
data = get_the_value(); // expensive operation
emit valueChanged(data);
}
Say I have a Qt application where I have something like this:
connect(A, SIGNAL(a()), B, SLOT(b1()));
connect(A, SIGNAL(a()), B, SLOT(b2()));
...
void B::b1() {
A->disconnect();
}
If a() is emitted, will the slot B::b2() still be called after disconnecting all slots from A in B::b1(), since they both respond to the same signal? Assume that both objects live in the same thread, so we have a direct connection.
I know that the disconnect() disconnects all signal connections from A, but I am not sure if the emit just schedules both the b1 and b2 slots to be called and then calls them, so that a change to the connections has no effect until the two slots (and therefore the emit) return. So it could be implemented like:
emit:
make temprorary copy of signal's slot table
foreach element in temporary slot table: call
disconnect:
clear signal's slot table
Otherwise you could run into datastructure integrity problems.
A quick experiment shows that the second slot is not called.
However, I'm not sure if this is something that Qt promises.
Qt does document that certain checks are made during the iteration of connected receivers, but it's but that's pretty loose and non-specific (and I haven't come across anything better in my short search); from http://doc.qt.io/qt-5/signalsandslots.html:
This is the overhead required to locate the connection object, to safely iterate over all connections (i.e. checking that subsequent receivers have not been destroyed during the emission)
So they loosely document that certain checks are made while a signal is emitted, such as whether a receiver has been destroyed. I think checking whether the connection has been disconnected seems like a similar kind of situation, but it would be nice if that were explicitly documented.
Since it sounds like this will be a problem for you (you want all the signals to get through, right?), you may be able to work around this problem by ensuring that the signal to slot b1() is connected last. Qt does promise that slots will be called by a signal in connection order, so if you arrange for the slot that does the disconnect to be the last one, all the other slots will be seen.
I don't know how easy this might be to arrange, but it also seems kind of strange that an object would disconnect everything from inside another object's slot function (so there's probably some other refactoring that can solve this problem as well).
If none of that is acceptable, it wouldn't be too hard to come up with a proxy object for A's signals that has the behavior you need. There would be a single:
connect(A, SIGNAL(a()), proxy_obj, SLOT(proxy_slot()));
to connect A's signal to the proxy, then B can connect to the proxy's signal (which the proxy would be emit when the proxy's slot got called):
connect(proxy_obj, SIGNAL(a()), B, SLOT(b1()));
connect(proxy_obj, SIGNAL(a()), B, SLOT(b2()));
Since A's disconnect will not affect the connections that the proxy has to B, the proxy will ensure that all of B's slots get called when signal A->a() is emitted.
class A_proxy : public QObject
{
Q_OBJECT
public slots:
void proxy_slot() {
emit a();
}
signals:
void a();
};
disconnect reference page answers your question.
It depends on how you call it, and the way you call it (without any parameter), it will disconnect all signals on object A, therefore the slot B:b2 will not be called after disconnect.