[Python自學] PyQT5-子線程更新UI數據、信號槽自動綁定、lambda傳參、partial傳參、覆蓋槽函數


一、子線程中更新UI數據

當我們要持續的更新主線程UI中控件的數據時,可能會導致主窗口阻塞(未響應),這是就需要用子線程將數據傳遞給主線程,並調用槽函數來更新控件顯示數據。

import sys
import time

# 導入QT,其中包含一些常量,例如顏色等
from PyQt5.QtCore import Qt, QThread, pyqtSignal, QDateTime
# 導入常用組件
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtWidgets import QLineEdit
# 使用調色板等
from PyQt5.QtGui import QIcon


# 創建一個子線程
class UpdateThread(QThread):
    # 創建一個信號,觸發時傳遞當前時間給槽函數
    update_data = pyqtSignal(str)

    def run(self):
        # 無限循環,每秒鍾傳遞一次時間給UI
        while True:
            data = QDateTime.currentDateTime()
            currentTime = data.toString("yyyy-MM-dd hh:mm:ss")
            self.update_data.emit(str(currentTime))
            time.sleep(1)


class DemoWin(QMainWindow):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.resize(400, 100)
        self.lineEdit = QLineEdit(self)
        self.lineEdit.resize(400, 100)

        # 創建子線程
        self.subThread = UpdateThread()
        # 將子線程中的信號與timeUpdate槽函數綁定
        self.subThread.update_data.connect(self.timeUpdate)
        # 啟動子線程(開始更新時間)
        self.subThread.start()

        # 添加窗口標題
        self.setWindowTitle("SubThreadDemo")

    # 被子線程的信號觸發,更新一次時間
    def timeUpdate(self, data):
        self.lineEdit.setText(data)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setWindowIcon(QIcon("images/icon.ico"))
    # 創建一個主窗口
    mainWin = DemoWin()
    # 顯示
    mainWin.show()
    # 主循環
    sys.exit(app.exec_())

在上述代碼中,我們啟動了一個子線程來循環發送信號,觸發信號綁定的槽函數(位於主線程),每次觸發都將需要顯示的時間數據傳遞到主線程,並更新到lineEdit控件中。

實現效果:

二、信號和槽函數的自動綁定

我們使用槽函數裝飾器,以及控件對象名可以實現信號和槽函數的自動綁定。

import sys

# 導入QT,其中包含一些常量,例如顏色等
from PyQt5.QtCore import Qt
from PyQt5 import QtCore
# 導入常用組件
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QPushButton, QLabel

# 使用調色板等
from PyQt5.QtGui import QIcon


class DemoWin(QWidget):
    def __init__(self):
        super(DemoWin, self).__init__()
        self.initUI()

    def initUI(self):
        self.resize(400, 200)
        self.okButton = QPushButton("OK", self)
        # 給okButton指定一個ObjectName
        self.okButton.setObjectName("okButton")
        self.cancelButton = QPushButton("Cancel", self)
        # 給cancelButton指定一個ObjectName
        self.cancelButton.setObjectName("cancelButton")

        self.displayLabel = QLabel()
        layout = QVBoxLayout()
        layout.addWidget(self.okButton)
        layout.addWidget(self.cancelButton)
        layout.addWidget(self.displayLabel)
        self.setLayout(layout)
        # 設置自動信號槽綁定(通過控件對象名稱來查找對應的槽函數)
        QtCore.QMetaObject.connectSlotsByName(self)

        # 添加窗口標題
        self.setWindowTitle("AutoDemo")

    @QtCore.pyqtSlot()  # 將這個函數指定為槽函數
    def on_okButton_clicked(self):
        print("okButton被點擊")
        self.displayLabel.setText("okButton被點擊")

    @QtCore.pyqtSlot()  # 將這個函數指定為槽函數
    def on_cancelButton_clicked(self):
        print("cancelButton被點擊")
        self.displayLabel.setText("cancelButton被點擊")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setWindowIcon(QIcon("images/icon.ico"))
    # 創建一個主窗口
    mainWin = DemoWin()
    # 顯示
    mainWin.show()
    # 主循環
    sys.exit(app.exec_())

注意,我們這里使用的是根據ObjectName來查找槽函數,並自動綁定。這種方式的槽函數名必須滿足以下規律:

on_ObjectName_SignalName

ObjectName即我們給控件設置的objectname,SignalName就是事件名,例如clicked,triggled等。

實現效果:

三、通過lambda表達式給槽函數傳遞參數

當我們在使用系統默認提供的事件時(例如按鈕的clicked事件),他默認是不帶參數的,也就是說用connect直接綁定,只能綁定不接受參數的槽函數。

但是我們有時候需要綁定帶參數的槽函數,這時就可以使用lambda表達式將槽函數包裝一下。

import sys

# 導入QT,其中包含一些常量,例如顏色等
from PyQt5.QtCore import Qt
# 導入常用組件
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QPushButton, QLabel

# 使用調色板等
from PyQt5.QtGui import QIcon


class DemoWin(QWidget):
    def __init__(self):
        super(DemoWin, self).__init__()
        self.initUI()

    def initUI(self):
        self.resize(400, 100)
        self.okButton = QPushButton("OK", self)
        # 使用lambda表達式進行參數傳遞,即將槽函數封裝為一個匿名的無參數函數給信號綁定
        self.okButton.clicked.connect(lambda: self.on_okButton_clicked(20, 50))

        self.displayLabel = QLabel()
        self.displayLabel.setAlignment(Qt.AlignCenter)
        layout = QVBoxLayout()
        layout.addWidget(self.okButton)
        layout.addWidget(self.displayLabel)
        self.setLayout(layout)
        # 添加窗口標題
        self.setWindowTitle("LambdaDemo")

    def on_okButton_clicked(self, x, y):
        self.displayLabel.setText("x+y = " + str(x + y))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setWindowIcon(QIcon("images/icon.ico"))
    # 創建一個主窗口
    mainWin = DemoWin()
    # 顯示
    mainWin.show()
    # 主循環
    sys.exit(app.exec_())

實現效果:

 

四、通過partial偏函數為槽函數傳參數

import sys

# 導入QT,其中包含一些常量,例如顏色等
from PyQt5.QtCore import Qt
# 導入常用組件
from PyQt5.QtWidgets import QApplication, QWidget
from PyQt5.QtWidgets import QVBoxLayout
from PyQt5.QtWidgets import QPushButton, QLabel

# 使用調色板等
from PyQt5.QtGui import QIcon

from functools import partial class DemoWin(QWidget):
    def __init__(self):
        super(DemoWin, self).__init__()
        self.initUI()

    def initUI(self):
        self.resize(400, 100)
        self.okButton = QPushButton("OK", self)
        # 使用functools提供的partial偏函數來傳遞參數
        self.okButton.clicked.connect(partial(self.on_okButton_clicked, 20, 50))

        self.displayLabel = QLabel()
        self.displayLabel.setAlignment(Qt.AlignCenter)
        layout = QVBoxLayout()
        layout.addWidget(self.okButton)
        layout.addWidget(self.displayLabel)
        self.setLayout(layout)
        # 添加窗口標題
        self.setWindowTitle("LambdaDemo")

    def on_okButton_clicked(self, x, y):
        self.displayLabel.setText("x+y = " + str(x + y))


if __name__ == '__main__':
    app = QApplication(sys.argv)
    app.setWindowIcon(QIcon("images/icon.ico"))
    # 創建一個主窗口
    mainWin = DemoWin()
    # 顯示
    mainWin.show()
    # 主循環
    sys.exit(app.exec_())

用法基本和Lambda差不多,也就起到將槽函數封裝的效果。封裝后,信號所直接綁定的函數是沒有參數的。

五、覆蓋(override)槽函數 

由於QT為我們提供了很多默認的槽函數(默認操作),我們可以通過重寫槽函數將其默認操作進行覆蓋。

Demo:

class DemoWin(QWidget):
    def __init__(self):
        super(DemoWin, self).__init__()
        self.initUI()

    def initUI(self):
        # 添加窗口標題
        self.setWindowTitle("OverrideDemo")

    def keyPressEvent(self, a0: QtGui.QKeyEvent) -> None:
        if a0.key() == Qt.Key_Escape:
            self.close()
        elif a0.key() == Qt.Key_Alt:
            self.setWindowTitle("按下了Alt鍵")

我們通過覆蓋keyPressEvent槽函數修改了按ESC和ALT鍵的行為。當我們按ESC的時候,窗口關閉,當按ALT鍵的時候窗口標題修改為"按下了Alt鍵"。

 

====


免責聲明!

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



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