PyQt5:PyQt5 信号与槽(PyQt5的事件处理机制)

事件

PyQt5有一个独一无二的信号和槽机制来处理事件。信号和槽用于对象之间的通信。当指定事件发生,一个事件信号会被发射。槽可以被任何Python脚本调用。当和槽连接的信号被发射时,槽会被调用。调用示意图如图1所示:
image

信号和槽(槽函数)

在Qt中,所有继承Qwidget中的子对象(也就是控件)和QObject都支持信号与槽机制。
当信号发射时,连接的槽函数将自动执行。
早PyQt5中信号与槽通过Object.signal.connect()方法连接。

PyQt窗口有很多内置信号,开发者也可以自定义信号。
信号与槽具有如下特点:

  • 一个信号可以连接多个槽
  • 一个信号可以连接另一个信号
  • 一个槽可以监听多个信号
  • 信号与槽的连接方式可以是同步连接,也可以是异步连接
  • 信号参数类型可以是python任何类型(image
  • 信号与槽的连接可能会跨线程
  • 信号可能会断开

在Gui编程中,在编写一个类时,首先定义该类的信号与槽,在类中信号与槽进行连接,实现对象之间的数据传输;
当事件或者状态发生改变时,就会发出信号。同时,信号会触发所有与这个事件(信号)相关的函数(槽);
信号与槽可以是多对多的关系,一个信号可以连接多个槽,一个槽也可以监听多个信号。

高级自定义信号与槽

按照自己想要的方式自定义信号与槽函数,并传递参数,自定义信号流程一般如下:

  1. 定义信号
  2. 定义槽函数
  3. 连接信号与槽函数
  4. 发射信号

定义信号

通过类成员变量定义信号对象。使用PyQtSinnal()方法

定义槽函数

定义一个槽函数,有多个不同的输入参数。槽函数就是普通类中的函数或方法

连接信号与槽函数

通过connect方法连接信号与槽函数或者可调用对象

发射信号

通过emit()方法发射信号

实例:

from PyQt5.QtCore import QObject , pyqtSignal

class CustSignal(QObject):

    #声明无参数的信号
    signal1 = pyqtSignal()

    #声明带一个int类型参数的信号
    signal2 = pyqtSignal(int)

    #声明带int和str类型参数的信号
    signal3 = pyqtSignal(int,str)

    #声明带一个列表类型参数的信号
    signal4 = pyqtSignal(list)

    #声明带一个字典类型参数的信号
    signal5 = pyqtSignal(dict)

    #声明一个多重载版本的信号,包括带int和str类型参数的信号和带str类型参数的信号
    signal6 = pyqtSignal([int,str], [str])

    def __init__(self,parent=None):
        super(CustSignal,self).__init__(parent)

        #将信号连接到指定槽函数
        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[str].connect(self.signalCall6OverLoad)

        #发射信号
        self.signal1.emit()
        self.signal2.emit(1)
        self.signal3.emit(1,"text")
        self.signal4.emit([1,2,3,4])
        self.signal5.emit({"name":"wangwu","age":"25"})
        self.signal6[int,str].emit(1,"text")
        self.signal6[str].emit("text")

    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__':
    custSignal = CustSignal()

运行结果如下:

signal1 emit
signal2 emit,value: 1
signal3 emit,value: 1 text
signal4 emit,value: [1, 2, 3, 4]
signal5 emit,value: {'name': 'wangwu', 'age': '25'}
signal6 emit,value: 1 text
signal6 overload emit,value: text

使用自定义参数

在PyQt编程过程中,经常会遇到给槽函数传递自定义参数的情况,比如有一个信号与槽函数的连接是:
button1.clicked.connect(show_page)

对于clicked信号来说,它是没有参数的;对于show_page函数来说,希望可以接收参数。
希望的show_page函数如下这样:

def show_page(self, name):
    print(name,"  点击啦")

问题是

信号发出的参数个数为0,槽函数接收的参数个数为1,由于0<1,这样运行起来一定会报错(原因是信号发出的参数个数一定要大于槽函数接收的参数个数)。解决这个问题就是:自定义参数的传递。

  有两种解决方法,其中一种解决方法是使用lambda表达式。其完整代码如下:

from PyQt5.QtWidgets import QMainWindow, QPushButton , QWidget , QMessageBox, QApplication, QHBoxLayout
import sys

class WinForm(QMainWindow):
    def __init__(self, parent=None):
        super(WinForm, self).__init__(parent)
        button1 = QPushButton('Button 1')
        button2 = QPushButton('Button 2')

        button1.clicked.connect(lambda: self.onButtonClick(1))
        button2.clicked.connect(lambda: self.onButtonClick(2))

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

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

    def onButtonClick(self, n):
        print('Button {0} 被按下了'.format(n))
        QMessageBox.information(self, "信息提示框", 'Button {0} clicked'.format(n))

if __name__ == "__main__":
    app = QApplication(sys.argv)
    form = WinForm()
    form.setGeometry(300,300,600,400)
    form.show()
    sys.exit(app.exec_())

运行效果如下:

image

解释OnButtonClick()函数怎样处理两个按钮传递来的信号的:

使用lambda表达式传递按钮数字给槽函数,当然也可以传递其他任何东西,甚至是按钮控件本身(假设槽函数打算把传递信号的按钮修改为不可用的话)。

另一种解决方法就是使用functools中的partial函数。
不过,更推荐lambda函数。

装饰器信号与槽

参考博客

posted @ 2021-06-02 11:30  poowicat  阅读(890)  评论(0编辑  收藏  举报