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

I have the following code that performs a background action ( scan_value ) when updating the execution line in ui ( progress ). scan_value over a value in obj , emitting a signal ( value_changed ) every time the value changes. For reasons that are not relevant here, I have to wrap this in an object ( Scanner ) in another thread. The scanner is called when the scan button is clicked . And here is my question ... the following code works fine (i.e. the progress bar is 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 is only updated at the end (my guess is that everything works in one thread). If this does not matter, if I connect the signal to the slot earlier than after moving the object receiving the object to Thread.

+9
python multithreading signals-slots pyqt qthread
Dec 23 '13 at 22:30
source share
3 answers

It does not matter if the connection was made before or after moving the work object to another thread. To quote Qt docs :

Qt :: AutoConnection - if a signal is emitted from a different thread than the receiving object, the signal is queued, behaves like Qt :: QueuedConnection. Otherwise, the slot is called directly, like Qt :: DirectConnection. Connection type: determined by the emission of a signal . [highlighted by me]

So, as long as the type connect argument is set to QtCore.Qt.AutoConnection (which is the default), Qt must ensure that the signals are emitted appropriately.

The problem with the sample code is more likely to be with a slot than a signal. The python method to which the signal is connected should probably 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 only in fairly recent versions of Qt, the type of connection is determined by the emission of a signal. This behavior was introduced (along with several other changes to Qt's multithreading support) with version 4.4.

Also, it might be worthwhile to further expand the PyQt problem. In PyQt, a signal can be connected to a Qt slot, another signal, or any python called. In the latter case, an internal proxy object is created that wraps the called python and provides the slot that is required by the Qt signal / slot mechanism.

It is this proxy object that causes the problem. After creating the proxy, 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 transferred to its stream; but if this is done earlier, the proxy will remain in the main thread.

Using the @pyqtSlot decorator avoids this problem in general, since it creates a better Qt slot and does not use a proxy object at all.

Finally, it should also be noted that this issue does not currently affect PySide.

+11
Dec 28 '13 at 19:21
source share

My problem was solved by moving the connection to the workflow initialization location, in my case, because I am accessing an object that only exists after creating an instance of my Worker Object class, which is in another thread.

Just plug in the signal after self.createWorkerThread()

respectfully

0
Apr 02 '19 at 16:08
source share

This is due to Qt connection types.

http://pyqt.sourceforge.net/Docs/PyQt5/signals_slots.html#connect

http://qt-project.org/doc/qt-4.8/qt.html#ConnectionType-enum

If both objects live in the same thread, a standard connection type is created, which leads to a simple function call. In this case, the time operation takes place in the GUI thread, and the interface blocks.

If the connection type is a message style connection, the signal is emitted using a message that is being processed in another thread. The GUI thread can now update the user interface.

If you do not specify the connection type in the connection function, the type is automatically determined.

-one
Dec 28 '13 at 12:02
source share



All Articles