PySide6 多線程信號傳遞


本人是一位PySide的初學者,最近剛有點整明白Pyside6的多線程間的信號傳遞,於是想記錄一下。
之前在網上找資料時,感覺很多教程的代碼都比較復雜,於是想到寫一個簡單版本的,也希望能給其他人一些參考。
由於本人也是初學者,因此可能有很多錯誤,也請大家不吝賜教。

演示

Demo

首先寫一個主窗口:

from PySide6.QtCore import Signal,Slot,Qt,QThread
from PySide6.QtWidgets import QWidget,QVBoxLayout,QPushButton,QLabel,QApplication
import sys,time

class mainWindow(QWidget):
    def __init__(self) -> None:
        super().__init__()

        self.label = QLabel("Hello!")
        self.label.setAlignment(Qt.AlignCenter)
        self.but = QPushButton("Click!")
        
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.label)
        self.layout.addWidget(self.but)
        self.setLayout(self.layout)
        self.setWindowTitle('Signal Example')
        self.resize(300,300)
        self.show()

        
if __name__ == '__main__':
    app = QApplication([])
    widgets = mainWindow()
    sys.exit(app.exec())

這一步應該不用多講,如果看不懂建議從PySide基礎開始學起。
主窗口

寫一個線程

該線程主要就是倒數。我們要讓主程序的窗口上現實剩余時間,就是Hello!的那個位置。

class Th(QThread):
    timer = Signal(int)
    finish = Signal(bool)
    def __init__(self) -> None:
        super().__init__()
    
    def run(self):
        # ptvsd.debug_this_thread()
        print('Start Timer')
        self.finish.emit(False)
        for x in range(5):
            self.timer.emit(5-x)
            time.sleep(1)
        self.finish.emit(True)

在這里,我用Signal初始化兩個信號,用於傳遞剩余時間和是否完成倒計時。
當倒計時開始時,設置未完成,將False給finish信號:
self.finish.emit(False)
隨后每秒,都會把剩余時間給timer信號:
self.timer.emit(5-x)
當倒計結束時,將True給finish信號:
self.finish.emit(True)

為主窗口添加槽並綁定

首先,我們點擊Click!按鈕應該開始倒計時,即執行我們上一步寫的線程。

    @Slot()
    def fun(self):
        self.th = Th()
        self.th.timer.connect(self.flushlabel)
        self.th.finish.connect(self.isFinish)
        self.th.start()

初始化線程,注意這里要用類的成員變量,不要用局部變量:
self.th = Th()
之后是開始這個線程:
self.th.start()

信號

self.th.timer.connect(self.flushlabel)
self.th.finish.connect(self.isFinish)

這兩個是完成信號與槽的綁定。
timer和finish都是上一步我們在倒計時的類中設定的信號。
我們希望,在Th類(倒計時的類)中,每次信號的emit,都會運行一次綁定的槽(self.flushlabel與self.isFinish)

槽函數

上一步綁定的槽,在這里我們來實現它。實現的代碼是在主窗口類中的。

class mainWindow(QWidget):
    def __init__(self) -> None:
		
		...
		...
		...
		
    @Slot()
    def fun(self):
		
		...
		...
		...

    @Slot(int)
    def flushlabel(self,nu):
        self.label.setText(str(nu))
    @Slot(bool)
    def isFinish(self,bo):
        if bo is True:
            self.but.setEnabled(True)
        else:
            self.but.setEnabled(False)

@Slot(int)@Slot(bool)為標識槽函數的參數類型,不寫似乎也沒有問題。

    def flushlabel(self,nu):
        self.label.setText(str(nu))

這里。該函數用來獲得timer每次emit的倒計時,將這個數字現實到主窗口的label中。

    def isFinish(self,bo):
        if bo is True:
            self.but.setEnabled(True)
        else:
            self.but.setEnabled(False)

這個函數來獲得每次finish每次emit的狀態,判斷倒計時是否完成,用來控制主窗口的按鈕是否可以點擊。

至此,我們的槽函數與信號的綁定完成了。這樣每次在子線程(倒計時)的信號被emit時,我們的主線程(主窗口)都能拿到其emit的內容,並且使用其內容對主窗口進行相應更改。

完整代碼

import sys,time
from PySide6.QtCore import Signal,Slot,Qt,QThread
from PySide6.QtWidgets import QWidget,QVBoxLayout,QPushButton,QLabel,QApplication

class mainWindow(QWidget):
    def __init__(self) -> None:
        super().__init__()

        self.label = QLabel("Hello!")
        self.label.setAlignment(Qt.AlignCenter)
        self.but = QPushButton("Click!")
        self.but.clicked.connect(self.fun)
        
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.label)
        self.layout.addWidget(self.but)
        self.setLayout(self.layout)
        self.setWindowTitle('Signal Example')
        self.resize(300,300)
        self.show()
    @Slot()
    def fun(self):
        self.th = Th()
        self.th.timer.connect(self.flushlabel)
        self.th.finish.connect(self.isFinish)
        self.th.start()

    @Slot(int)
    def flushlabel(self,nu):
        self.label.setText(str(nu))
    @Slot(bool)
    def isFinish(self,bo):
        if bo is True:
            self.but.setEnabled(True)
        else:
            self.but.setEnabled(False)

class Th(QThread):
    timer = Signal(int)
    finish = Signal(bool)
    def __init__(self) -> None:
        super().__init__()
    
    def run(self):
        print('Start Timer')
        self.finish.emit(False)
        for x in range(5):
            self.timer.emit(5-x)
            time.sleep(1)
        self.finish.emit(True)
        
if __name__ == '__main__':
    app = QApplication([])
    widgets = mainWindow()
    sys.exit(app.exec())


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM