PyQt: Connecting a signal to a slot to start a background operation PyQt: Connecting a signal to a slot to start a background operation multithreading multithreading

PyQt: Connecting a signal to a slot to start a background operation


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