[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键"。

 

====

posted @ 2020-06-15 17:41  风间悠香  阅读(7434)  评论(0编辑  收藏  举报