简体   繁体   English

PyQt 从非 QThread 唤醒主线程

[英]PyQt wake main thread from non-QThread

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 .我有一个 PyQt 应用程序,它通过从不受我控制且不是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.特别是,我想发出一个 Qt 信号,这样我就可以在新数据到达时唤醒主线程(或另一个QThread )。

The default connection type for signals is Qt.AutoConnection , which the docs describe thus:信号的默认连接类型是Qt.AutoConnection ,文档是这样描述的:

If the signal is emitted from a different thread than the receiving object, the signal is queued, behaving as Qt::QueuedConnection.如果信号是从与接收对象不同的线程发出的,则信号将排队,表现为 Qt::QueuedConnection。 Otherwise, the slot is invoked directly, behaving as Qt::DirectConnection.否则,将直接调用插槽,表现为 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.因此,在发出信号之前,Qt 会在决定是否将其排队之前简单地比较发送方和接收方的当前线程关联。 It does not matter how the underlying threads were originally started.底层线程最初是如何启动的并不重要。

Here is a simple demo using a python worker thread:这是一个使用 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_())

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]

I would handle it the same way I would if you were just polling an external device or library.如果您只是轮询外部设备或库,我会像处理它一样处理它。 Create a separate worker thread that handles the callback, and emits an signal to the main GUI thread.创建一个单独的工作线程来处理回调,并向主 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM