PyQt 从非 QThread 唤醒主线程
PyQt wake main thread from non-QThread
我有一个 PyQt 应用程序,它通过回调从外部源接收信息,这些回调是从不受我控制且不 QThread
的线程调用的。在没有轮询的情况下将此类信息传递给主线程的正确方法是什么?特别是,我想发出一个 Qt 信号,以便我可以在新数据到达时唤醒主线程(或另一个 QThread
)。
如果您只是轮询外部设备或库,我会以同样的方式处理它。创建一个单独的工作线程来处理回调,并向主 GUI 线程发出信号。
class Worker(QObject):
data_ready = pyqtSignal(object, object)
def callback(self, *args, **kwargs):
self.data_ready.emit(args, kwargs)
class Window(...)
def __init__(self):
...
self.worker = Worker(self)
# Connect worker callback function to your external library
library.register_callback(self.worker.callback) #??
...
# Mover worker to thread and connect signal
self.thread = QThread(self)
self.worker.data_ready.connect(self.handle_data)
self.worker.moveToThread(self.thread)
self.thread.start()
@pyqtSlot(object, object)
def handle_data(self, args, kwargs):
# do something with data
信号的默认连接类型是 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.
因此,在发出信号之前,Qt 会简单地比较发送方和接收方的 current 线程亲和性,然后再决定是否将其排队。 底层线程最初是如何启动的并不重要。
这是一个使用 python 工作线程的简单演示:
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_())
典型输出:
$ 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]
我有一个 PyQt 应用程序,它通过回调从外部源接收信息,这些回调是从不受我控制且不 QThread
的线程调用的。在没有轮询的情况下将此类信息传递给主线程的正确方法是什么?特别是,我想发出一个 Qt 信号,以便我可以在新数据到达时唤醒主线程(或另一个 QThread
)。
如果您只是轮询外部设备或库,我会以同样的方式处理它。创建一个单独的工作线程来处理回调,并向主 GUI 线程发出信号。
class Worker(QObject):
data_ready = pyqtSignal(object, object)
def callback(self, *args, **kwargs):
self.data_ready.emit(args, kwargs)
class Window(...)
def __init__(self):
...
self.worker = Worker(self)
# Connect worker callback function to your external library
library.register_callback(self.worker.callback) #??
...
# Mover worker to thread and connect signal
self.thread = QThread(self)
self.worker.data_ready.connect(self.handle_data)
self.worker.moveToThread(self.thread)
self.thread.start()
@pyqtSlot(object, object)
def handle_data(self, args, kwargs):
# do something with data
信号的默认连接类型是 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.
因此,在发出信号之前,Qt 会简单地比较发送方和接收方的 current 线程亲和性,然后再决定是否将其排队。 底层线程最初是如何启动的并不重要。
这是一个使用 python 工作线程的简单演示:
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_())
典型输出:
$ 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]