簡體   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