繁体   English   中英

如何在双向上使用QThreads在PyQt中设置信号和插槽?

[英]How do I setup Signals and Slots in PyQt with QThreads in both directions?

这是根据ekhumoro 在这里这里的回答的后续问题。


我想了解的是,当使用pyqtSlot正确定义pyqtSlot并将其分配给QThread (例如,使用moveToThread() )时,它将在此QThread中执行,而不是在调用该moveToThread()执行。 此外,还需要使用Qt.QueuedConnectionQt.AutoConnection建立连接。

我编写了代码对此进行了测试。 我的目标是实现类似以下的简单操作: 在此处输入图片说明
带有一个按钮的Gui,它开始一些费时的工作,然后返回结果,并显示回GUI中。

from PyQt5.Qt import *

class MainWindow(QMainWindow):
    change_text = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.button = QPushButton('Push me!', self)
        self.setCentralWidget(self.button)

        print('main running in:', QThread.currentThread())
        thread = Thread(change_text, self)
        thread.start()
        self.button.clicked.connect( thread.do_something_slow, Qt.QueuedConnection)
        self.change_text.connect(self.display_changes, Qt.QueuedConnection)

    @pyqtSlot(str)
    def display_changes( self, text ):
        self.button.setText(text)

class Thread(QThread):
    def __init__(self, signal_to_emit, parent):
        super().__init__(parent)
        self.signal_to_emit = signal_to_emit
        #self.moveToThread(self) #doesn't help

    @pyqtSlot()
    def do_something_slow( self ):
        print('Slot doing stuff in:', QThread.currentThread())
        import time
        time.sleep(5)
        self.signal_to_emit.emit('I did something')

if __name__ == '__main__':
    app = QApplication([])
    main = MainWindow()
    main.show()
    app.exec()

但是.. gui正在阻塞,并且在主线程中调用了插槽。
我想念什么? 一定要小(我希望)。

问题是您混淆了QThreadQt线程 ,即Qt创建的新线程,但是没有, QThread处理本机线程的类,只有run()方法在另一个线程上运行,其他方法生活在QThread所在的线程中,该线程是QObject

QObject驻留在哪个线程中?

QObject所在的线程是父对象的线程,如果没有父对象,它将是创建该对象的线程。 另一方面,一个QObject可以使用moveToThread()移到另一个线程,并且其所有子对象也将移动。 如果QObject没有父对象,则只能使用moveToThread() ,否则它将失败。


使用QThread的方法是创建一个从QThread继承并覆盖run方法并调用start()以便它开始运行run() ,在run()方法中将完成繁重的任务,但是在您的情况下不能使用此形式,因为任务不会连续执行。 比繁重的任务更好的选择是QObject一部分,该QObject将其移至另一个线程。

class MainWindow(QMainWindow):
    change_text = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.button = QPushButton('Push me!', self)
        self.setCentralWidget(self.button)
        print('main running in:', QThread.currentThread())
        # A Worker without a parent is created 
        # so that it can be moved to another thread.
        self.worker = Worker(self.change_text)
        thread = QThread(self) 
        self.worker.moveToThread(thread)
        thread.start()
        # All methods of self.worker are now executed in another thread.
        self.button.clicked.connect(self.worker.do_something_slow)
        self.change_text.connect(self.display_changes)

    @pyqtSlot(str)
    def display_changes( self, text ):
        self.button.setText(text)


class Worker(QObject):
    def __init__(self, signal_to_emit, parent=None):
        super().__init__(parent)
        self.signal_to_emit = signal_to_emit

    @pyqtSlot()
    def do_something_slow( self ):
        print('Slot doing stuff in:', QThread.currentThread())
        import time
        time.sleep(5)
        self.signal_to_emit.emit('I did something')

另一方面,没有必要指明连接类型,因为默认情况下为Qt::AutoConnection ,如果您使用Qt::DirectConnection ,则该类型的连接将在运行时决定,如果接收器与信号所在的导线位于同一根导线中发出,否则,使用Qt::QueuedConnection 如您所知,Worker没有父级,因此对于它来说,其生命周期等于类,它必须是它的属性,因为否则它将是被消除的局部变量。 另一方面,请注意, QThread比MainWindow接收父级,因此QThreadQThread在GUI线程中,但它处理辅助线程。


使用Worker概念,最好使change_text信号不再属于GUI,而是使Worker更好地解耦对象。

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.button = QPushButton('Push me!', self)
        self.setCentralWidget(self.button)

        print('main running in:', QThread.currentThread())
        self.worker = Worker()
        thread = QThread(self)
        self.worker.moveToThread(thread)
        thread.start()
        self.button.clicked.connect(self.worker.do_something_slow)
        self.worker.change_text.connect(self.display_changes)

    @pyqtSlot(str)
    def display_changes( self, text ):
        self.button.setText(text)

class Worker(QObject):
    change_text = pyqtSignal(str)

    @pyqtSlot()
    def do_something_slow( self ):
        print('Slot doing stuff in:', QThread.currentThread())
        import time
        time.sleep(5)
        self.change_text.emit('I did something')

暂无
暂无

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

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