十五、PyQt5 之 信号与槽

一、信号与槽基本用法

#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
信号(Signal)与槽(Slot)
"""

from PyQt5.QtWidgets import *
import sys


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

    def onClick(self):
        self.btn.setText("信号已经发出")
        self.btn.setStyleSheet("QPushButton(max-width:200px;min-width:200px")

    def initUI(self):
        self.setGeometry(300, 300, 500, 500)
        self.setWindowTitle("信号(Signal)与槽(Slot)")
        self.btn = QPushButton("我的按钮", self)
        self.btn.clicked.connect(self.onClick)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    gui = SigalSlotDemo()
    gui.show()
    sys.exit(app.exec_())

二、自定义信号 (pyqtSignal)

#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
自定义信号 pyqtSignal()
"""

from PyQt5.QtCore import *


class MyTypeSignal(QObject):
    # 定义一个信号, object 一定要有,不知道为什么
    sendMsg = pyqtSignal(object)

    #  发送三个参数的信号
    sendMsg1 = pyqtSignal(str, int, int)

    def run(self):
        # 触犯槽函数
        self.sendMsg.emit("Hello PyQt5")

    def run1(self):
        # 触犯槽函数
        self.sendMsg1.emit("Hello WY", 3, 4)


# 定义一个槽类
class MySlot(QObject):
    def get(self, msg):
        print("信息: " + msg)

    def get1(self, msg, a, b):
        print(msg)
        print(a + b)


if __name__ == '__main__':
    send = MyTypeSignal()
    slot = MySlot()
    # 将 slot.get的方法与信号绑定,一旦调用了 send.run() 方法,就会将 emit 发送的数据
    # 传递给 slot.get() 的msg
    send.sendMsg.connect(slot.get)
    send.run()  # 信息: Hello PyQt5

    send.sendMsg1.connect(slot.get1)
    send.run1()

    # 调用断开信号之后,再调用 run 就会不起作用了
    send.sendMsg.disconnect()
    send.run()

三、为类添加多个信号(信号的重载)

#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
为了添加多个信号 (信号的重载)
"""
from PyQt5.QtCore import *


class MultiSignal(QObject):
    signal1 = pyqtSignal()
    signal2 = pyqtSignal(int)
    signal3 = pyqtSignal(int, str)
    signal4 = pyqtSignal(list)
    signal5 = pyqtSignal(dict)
    # 声明一个重载版本的信号, 也就是槽函数参数可以是 int和str类型,也可以只有一个 str类型的参数
    signal6 = pyqtSignal([int, str], [str])

    def __init__(self):
        super(MultiSignal, self).__init__()
        self.signal1.connect(self.signalCall1)
        self.signal2.connect(self.signalCall2)
        self.signal3.connect(self.signalCall3)
        self.signal4.connect(self.signalCall4)
        self.signal5.connect(self.signalCall5)
        # 默认重载第一个参数
        self.signal6[int, str].connect(self.signalCall6)
        # 上面和下面这一行是一样的
        # self.signal6.connect(self.signalCall6)
        # 想要只重载 [str] 这一个参数,使用下面的方法
        self.signal6[str].connect(self.signalCall6Overload)

        self.signal1.emit()
        self.signal2.emit(10)
        self.signal3.emit(10, "Hello World")
        self.signal4.emit([1, 2, 3, 4, 5])
        self.signal5.emit({"name": "wangyong"})
        self.signal6[int, str].emit(20, "test")
        self.signal6[str].emit("TEST")

    def signalCall1(self):
        print("signal1 emit")

    def signalCall2(self, val):
        print("signal2 emit, value", val)

    def signalCall3(self, val, text):
        print("signal3 emit, value", val, text)

    def signalCall4(self, val):
        print("signal4 emit, value", val)

    def signalCall5(self, val):
        print("signal5 emit, value", val)

    def signalCall6(self, val, text):
        print("signal6 emit, value", val, text)

    def signalCall6Overload(self, val):
        print("signal6 overload emit, value", val)


if __name__ == '__main__':
    multiSignal = MultiSignal()

四、信号与槽的 N 对 N 连接与断开连接

#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
信号与槽的 N 对 N 连接与断开连接
"""

from PyQt5.QtCore import *


class NNSignal(QObject):
    signal1 = pyqtSignal()
    signal2 = pyqtSignal(int)
    signal3 = pyqtSignal()

    def __init__(self):
        super(NNSignal, self).__init__()
        # 同时绑定两个槽函数
        self.signal1.connect(self.call1)
        self.signal1.connect(self.call11)
        self.signal1.emit()

        # 可以绑定到信号上
        self.signal2.connect(self.signal1)
        self.signal2.emit(2)  # 本质上还是触发 self.signal1, 所以这个 2 没有任何作用,只是作为定义时候需要的参数

        # 解除关联
        self.signal1.disconnect(self.call1)
        self.signal1.disconnect(self.call11)
        self.signal2.disconnect(self.signal1)
        # 直接这样应该也是可以的
        # self.signal1.disconnect()
        # self.signal2.disconnect()

        # 重新关联
        self.signal1.connect(self.call1)
        self.signal2.connect(self.call2)
        self.signal1.emit()
        self.signal2.emit(2)

        self.signal3.connect(self.signal1)
        self.signal1.emit()  # call1 emit
        self.signal3.emit()  # call1 emit 这个和上一步绑定最后都是同一个槽函数

    def call1(self):
        print("call1 emit")

    def call11(self):
        print("call11 emit")

    def call2(self, val):
        print("call2 emit:", val)


if __name__ == '__main__':
    nnSignal = NNSignal()

五、为窗口添加信号

#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
为窗口类添加信号
"""

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys


class WinSignal(QWidget):
    button_clicked_signal = pyqtSignal()

    def __init__(self):
        super(WinSignal, self).__init__()
        self.setWindowTitle("为窗口类添加信号")
        self.resize(300, 100)

        self.btn = QPushButton("关闭窗口", self)
        self.btn.clicked.connect(self.btn_clicked)
        self.button_clicked_signal.connect(self.btn_close)

    def btn_clicked(self):
        self.button_clicked_signal.emit()

    def btn_close(self):
        self.close()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    example = WinSignal()
    example.show()
    sys.exit(app.exec_())

六、多线程更新UI数据

#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
多线程更新UI数据(在两个线程中传递数据)
"""

from PyQt5.QtCore import QThread, pyqtSignal, QDateTime
from PyQt5.QtWidgets import QApplication, QDialog, QLineEdit
import time
import sys


class BackendThread(QThread):
    update_date = pyqtSignal(str)

    def run(self):
        while True:
            data = QDateTime.currentDateTime()
            currentTime = data.toString("yyyy-MM-dd hh:mm:ss")
            self.update_date.emit(str(currentTime))
            time.sleep(1)


class ThreadUpdateUI(QDialog):

    def __init__(self):
        super(QDialog, self).__init__()
        self.setWindowTitle("多线程更新UI数据")
        self.resize(400, 100)
        self.input = QLineEdit(self)
        self.input.resize(400, 100)
        self.initUI()

    def initUI(self):
        self.backend = BackendThread()
        self.backend.update_date.connect(self.handleDisplay)
        self.backend.start()

    def handleDisplay(self, data):
        self.input.setText(data)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    example = ThreadUpdateUI()
    example.show()
    sys.exit(app.exec_())

七、信号与槽自动连接(on_objectname_signalname)

#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
信号与槽自动连接

setObjectName(signalname)
on_objectname_signalname --- on_okButton_clicked
槽函数名称命名规则: on_发送者对象名称_发射信号名称(self, 参数)

"""

from PyQt5 import QtCore
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QPushButton
import sys


class AutoSignalSlot(QWidget):
    def __init__(self):
        super(AutoSignalSlot, self).__init__()
        self.okButton = QPushButton("ok", self)
        # 给按钮设置一个名字,名称随意, 但是槽函数需要根据名称来
        self.okButton.setObjectName("okButton")

        self.cancelButton = QPushButton("cancel", self)
        self.cancelButton.setObjectName("cancelButton")

        layout = QHBoxLayout()
        layout.addWidget(self.okButton)
        layout.addWidget(self.cancelButton)
        self.setLayout(layout)
        # 根据名称统一自动调用参函数
        QtCore.QMetaObject.connectSlotsByName(self)

        # 直接绑定槽函数
        # self.okButton.clicked.connect(self.on_okButton_clicked)

    @QtCore.pyqtSlot()  # 采用装饰器的方式,表示该函数只能用槽函数的方式绑定, 不能用以前的click来事件绑定
    def on_okButton_clicked(self):
        print("点击了OK 按钮")

    @QtCore.pyqtSlot()
    def on_cancelButton_clicked(self):
        print("点击了 Cancel 按钮")


if __name__ == '__main__':
    app = QApplication(sys.argv)
    example = AutoSignalSlot()
    example.show()
    sys.exit(app.exec_())

八、使用Lambda表达式为槽函数传递参数

#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
使用Lambda表达式为槽函数传递参数

Lambda表达式:匿名函数,也就是没有名字的函数

fun = lambda :print("hello world")
fun()

fun1 = lambda x,y:print(x,y)
fun1("a","b")
"""

from PyQt5.QtWidgets import *
import sys


class LambdaSlotArg(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("使用Lambda表达式为槽函数传递参数")

        button1 = QPushButton("按钮1")
        button2 = QPushButton("按钮2")
        ok = 100

        button1.clicked.connect(lambda: self.onButtonClick(10, ok))
        button2.clicked.connect(lambda: self.onButtonClick(ok, -20))
        button1.clicked.connect(lambda: QMessageBox.information(self, "结果", "单击了button1"))

        layout = QHBoxLayout()
        layout.addWidget(button1)
        layout.addWidget(button2)

        mainFrame = QWidget()
        mainFrame.setLayout(layout)
        self.setCentralWidget(mainFrame)

    def onButtonClick(self, m, n):
        print("m + n =", m + n)
        QMessageBox.information(self, "结果", str(m + n))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = LambdaSlotArg()
    form.show()
    sys.exit(app.exec_())

九、使用Partial对象为槽函数传递参数

#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
使用Partial对象为槽函数传递参数
"""

from PyQt5.QtWidgets import *
import sys
from functools import partial


class PartialSlotArg(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("使用Partial表达式为槽函数传递参数")

        button1 = QPushButton("按钮1")
        button2 = QPushButton("按钮2")
        x = 20
        y = -123
        button1.clicked.connect(partial(self.onButtonClick, 10, 20))
        button2.clicked.connect(partial(self.onButtonClick, x, y))

        layout = QHBoxLayout()
        layout.addWidget(button1)
        layout.addWidget(button2)
        mainFrame = QWidget()
        mainFrame.setLayout(layout)
        self.setCentralWidget(mainFrame)

    def onButtonClick(self, m, n):
        print("m + n =", m + n)
        QMessageBox.information(self, "结果", str(m + n))


if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = PartialSlotArg()
    form.show()
    sys.exit(app.exec_())

十、键盘事件,Override(覆盖)槽函数

#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
Override(覆盖)槽函数, 也就是函数重写
"""

from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
import sys


class OverrideSlot(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Override(覆盖)槽函数")

    def keyPressEvent(self, e):
        if e.key() == Qt.Key_Escape:  # 按下 esc 按键, 退出
            self.close()
        elif e.key() == Qt.Key_Alt:
            self.setWindowTitle("按下Alt键")  # 按下 Alt 键, 修改标题


if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = OverrideSlot()
    form.show()
    sys.exit(app.exec_())

十一、多窗口交互

11.1 多窗口交互--不是用信号与槽

# DateDialog.py

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


class DateDialog(QDialog):
    def __init__(self, parent=None):  # parent 参数一定要要有,看源码应该是原码有问题
        super(DateDialog, self).__init__(parent)
        self.setWindowTitle("DateDialog")

        layout = QVBoxLayout(self)
        self.datetime = QDateTimeEdit(self)
        self.datetime.setCalendarPopup(True)
        self.datetime.setDateTime(QDateTime.currentDateTime())

        layout.addWidget(self.datetime)

        buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, Qt.Horizontal, self)
        buttons.accepted.connect(self.accept)
        buttons.rejected.connect(self.reject)

        layout.addWidget(buttons)
        # self.setLayout(layout)

    def dateTime(self):
        return self.datetime.dateTime()

    @staticmethod
    def getDateTime(parent=None):
        dialog = DateDialog(parent)
        result = dialog.exec()
        date = dialog.dateTime()
        return (date.date(), date.time(), result == QDialog.Accepted)
#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
多窗口交互(1):不使用信号与槽
"""

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from SignalSlot.DateDialog import DateDialog


class MultiWindow1(QWidget):
    def __init__(self):
        super(MultiWindow1, self).__init__()
        self.setWindowTitle("多窗口交互(1):不使用信号与槽")

        self.lineEdit = QLineEdit(self)
        self.button1 = QPushButton('弹出对话框1')
        self.button1.clicked.connect(self.onButton1Click)

        self.button2 = QPushButton('弹出对话框2')
        self.button2.clicked.connect(self.onButton2Click)

        gridLayout = QGridLayout()
        gridLayout.addWidget(self.lineEdit)
        gridLayout.addWidget(self.button1)
        gridLayout.addWidget(self.button2)

        self.setLayout(gridLayout)

    def onButton1Click(self):
        dialog = DateDialog(self)
        result = dialog.exec()
        date = dialog.dateTime()
        self.lineEdit.setText(date.date().toString())
        dialog.destroy()

    def onButton2Click(self):
        date, time, result = DateDialog.getDateTime()
        self.lineEdit.setText(date.toString())
        if result == QDialog.Accepted:
            print("点击确定按钮")
        else:
            print("单击取消按钮")


if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = MultiWindow1()
    form.show()
    sys.exit(app.exec_())

11.2 多窗口交互--使用信号与槽

#!/usr/bin/python
# -*- coding:utf-8 -*-

"""
多窗口交互(2):使用信号与槽

如果一个窗口A与另一个窗口B交互,那么A尽量不要直接访问B窗口中的控件,
应该访问B窗口中的信号,并指定与信号绑定的槽函数

例:如果A直接访问B窗口的控件,一旦B窗口控件发生改变,那么A和B的代码都需要变化
如果A访问的是B中的信号,那么B中的控件发生了改变,只需要修改B中的代码即可
"""

import sys
from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from SignalSlot.NewDateDialog import NewDateDialog


class MultiWindow2(QWidget):
    def __init__(self, parent=None):
        super(MultiWindow2, self).__init__(parent)
        self.resize(400, 90)
        self.setWindowTitle("多窗口交互(2):使用信号与槽")

        self.open_btn = QPushButton("获取时间")
        self.lineEdit_inner = QLineEdit(self)
        self.lineEdit_emit = QLineEdit(self)
        self.open_btn.clicked.connect(self.openDialog)

        self.lineEdit_inner.setText('接收子窗口内置信号的时间')
        self.lineEdit_emit.setText('接收子窗口自定义信号的时间')

        grid = QGridLayout()
        grid.addWidget(self.lineEdit_inner)
        grid.addWidget(self.lineEdit_emit)
        grid.addWidget(self.open_btn)
        self.setLayout(grid)

    def openDialog(self):
        dialog = NewDateDialog(self)
        # 连接子窗口的内置信号与主窗口的槽函数
        dialog.datetime_inner.dateTimeChanged.connect(self.deal_inner_slot)
        # 连接子窗口的自定义信号与主窗口的槽函数
        dialog.Signal_OneParameter.connect(self.deal_emit_slot)
        dialog.show()

    def deal_inner_slot(self, date):
        self.lineEdit_inner.setText(date.toString())

    def deal_emit_slot(self,dateStr):
        self.lineEdit_emit.setText(dateStr)


if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = MultiWindow2()
    form.show()
    sys.exit(app.exec_())

 

posted on 2022-07-09 11:34  软饭攻城狮  阅读(78)  评论(0编辑  收藏  举报

导航