I have a PyQt application that receives information from an external source via callbacks that are called from threads that are not under my control and which are not QThread
. What is the correct way to pass such information to the main thread, without polling? Particularly, I want to emit a Qt signal such that I can wake the main thread (or another QThread
) upon arrival of new data.
The default connection type for signals is Qt.AutoConnection, which the docs describe thus:
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.
So before emitting a signal, Qt simply compares the current thread affinity of the sender and receiver before deciding whether to queue it or not. It does not matter how the underlying threads were originally started.
Here is a simple demo using a python worker thread:
import sys, time, threading
from PyQt4 import QtCore, QtGui
class Worker(object):
def __init__(self, callback):
self._callback = callback
self._thread = None
def active(self):
return self._thread is not None and self._thread.is_alive()
def start(self):
self._thread = threading.Thread(target=self.work, name='Worker')
self._thread.start()
def work(self):
print('work: [%s]' % threading.current_thread().name)
for x in range(5):
time.sleep(1)
self._callback(str(x))
class Window(QtGui.QPushButton):
dataReceived = QtCore.pyqtSignal(str)
def __init__(self):
super(Window, self).__init__('Start')
self.clicked.connect(self.start)
self.dataReceived.connect(self.receive)
self.worker = Worker(self.callback)
def receive(self, data):
print('received: %s [%s]' % (data, threading.current_thread().name))
def callback(self, data):
print('callback: %s [%s]' % (data, threading.current_thread().name))
self.dataReceived.emit(data)
def start(self):
if self.worker.active():
print('still active...')
else:
print('start: [%s]' % threading.current_thread().name)
self.worker.start()
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
window = Window()
window.show()
print('show: [%s]' % threading.current_thread().name)
sys.exit(app.exec_())
Typical output:
$ python test.py
show: [MainThread]
start: [MainThread]
work: [Worker]
callback: 0 [Worker]
received: 0 [MainThread]
still active...
callback: 1 [Worker]
received: 1 [MainThread]
still active...
callback: 2 [Worker]
received: 2 [MainThread]
still active...
callback: 3 [Worker]
received: 3 [MainThread]
callback: 4 [Worker]
received: 4 [MainThread]