自学PyQt5(三)| 处理事件的信号槽机制

本文介绍了PyQt5中的事件模型和信号槽Signal&Slots机制,详细阐述了如何通过信号槽处理用户交互,例如使用QLCDNumber显示QSlider的值,以及如何在状态栏中显示用户按钮点击信息。通过示例代码,读者可以掌握在PyQt5中创建GUI程序的基本思路和方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


往期文章:


一. 事件模型

事件模型由以下三个参与者:

  • 事件源
  • 事件对象(即事件)
  • 事件接收者

其中,事件源是状态发生变化的对象,它将事件源中状态的变动封装,封装后的状态称为事件。事件源将事件发送给事件接收者,而后者负责处理事件。

在GUI程序中,常见的事件包括由用户触发的鼠标点击、文本输入等,也包括由其它程序触发的例如网络连接、window manger、定时器等。我们对QApplication实例化后对其使用exec_方法即可使程序进入主循环,并获取、分发事件。


二. 信号槽Signal&slots机制

PyQt5通过Signal&slots机制来实现事件模型。在这个机制中,事件源sender发送出事件signal,而对应的接收者receiver使用槽函数slots处理信号。

我们可以通过下面的例子清楚的看到这一机制的工作过程与框架:

import sys
from PyQt5.QtWidgets import QApplication,\
                            QWidget,\
                            QVBoxLayout,\
                            QLCDNumber,\
                            QSlider
from PyQt5.QtCore import Qt

class MyWindow(QWidget):

    def __init__(self):
        super().__init__()

        self.setLCDandSlider()
        self.setMyWindow()

    def setLCDandSlider(self):
        
        sender = QSlider(Qt.Horizontal,self)

        signal = sender.valueChanged

        receiver = QLCDNumber(self)

        slits = receiver.display

        signal.connect(slits)

        vbox = QVBoxLayout()
        vbox.addWidget(receiver)
        vbox.addWidget(sender)
        self.setLayout(vbox)

    def setMyWindow(self):
        
        self.resize(200,200)
        self.move(0,0)
        self.setWindowTitle('用滑动轴控制的数字显示窗')
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MyWindow()
    sys.exit(app.exec_())

其效果如图:
在这里插入图片描述
我们可以通过拖动滑条来改变显示的数字:
在这里插入图片描述


下面我们来具体分析这段代码。首先,我们调入一个QLCDNumber函数,这个函数的作用是将数字以液晶显示器的格式显示在窗口,例如:

    def setMyLCDNumber(self):
        
        lcd = QLCDNumber(self)
        lcd.display(3.14)
        lcd.resize(200,200)

这段代码制定了一个LCD显示窗口,并通过槽函数display指定了显示的数字,效果如下:
在这里插入图片描述
而QSlider函数则是在窗口中添加一个滑动轴。

在这段代码中,我们的时间源是滑动轴,而滑动轴发送的事件就是滑动轴的值sender.valueChanged;事件接收者是LCD显示窗,而其提供的处理事件的槽函数就是receiver.display。最后,我们通过signal.connect(slits)将事件与槽函数联系在一起即可。

我们使用了纵向布局,所以申请了QVBoxLayout,而滑动轴默认是纵向的,为了美观我们向QSlider函数中传入Qt.Horizontal,将其调整为横向。若不指定该参数,最后的窗口效果如下:
在这里插入图片描述

三. 示例:使用signal&slots机制获取用户按钮动作信息

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

class MyWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.setMyButton()
        self.setMyWindow()

    def setMyButton(self):
        
        button_a = QPushButton('按钮a',self)
        button_b = QPushButton('按钮b',self)
        self.setButtonLayout(button_a,button_b)

        myStatusBar = self.statusBar()

        button_a.clicked.connect(self.MySlot)
        button_b.clicked.connect(self.MySlot)

    def setButtonLayout(self,button_a,button_b):
       
        widget = QWidget()

        hbox = QHBoxLayout()
        hbox.addStretch(1)
        hbox.addWidget(button_a)
        hbox.addStretch(1)
        hbox.addWidget(button_b)
        hbox.addStretch(1)

        vbox = QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(hbox)
        vbox.addStretch(1)

        widget.setLayout(vbox)
        self.setCentralWidget(widget)
        #将QWidget设置为QMainWindow的主窗口

    def MySlot(self):

        sender = self.sender()

        self.statusBar().showMessage('刚刚触发的按钮是' + sender.text())

    def setMyWindow(self):
        
        self.resize(600,600)
        self.move(0,0)
        self.setWindowTitle('显示按钮信息')
        self.show()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MyWindow()
    sys.exit(app.exec_())

在这段代码中,我们在窗口上放置了两个按钮,希望能把用户按按钮的信息显示在窗口底部的状态栏中,并且窗口使用了框布局,效果如下:
在这里插入图片描述
当我们点击按钮b时,下方出现提示:
在这里插入图片描述
下面我们来具体分析一下这段代码:

首先我们为了使信息显示在状态栏里,所以调入了QMainWindow类。QMainWindow类是QWidget类的子类,提供了状态栏等控件,我们的MyWindow类继承了QMainWindow。

之后,我们申请了两个按钮和一个状体栏,并使用自己写的方法对按钮的布局进行设置:

        button_a = QPushButton('按钮a',self)
        button_b = QPushButton('按钮b',self)
        self.setButtonLayout(button_a,button_b)

        myStatusBar = self.statusBar()

setButtonLayout方法中,我们使用了框布局,但是QMainWindow并未直接提供框布局方法,所以在setButtonLayout方法中,我们为了方便的使用框布局而增加了几行代码:

		widget = QWidget()

        widget.setLayout(vbox)
        self.setCentralWidget(widget)

这几行代码使我们的主窗口仍然是QWidget类,既可以方便的使用QWidget类提供的布局方法,也能使用QMainWindow类提供的对状态栏的设置。

解决了布局问题之后,我们设置信息&槽;其中信息是按钮的点击,而槽函数是我们另外定义的:

        button_a.clicked.connect(self.MySlot)
        button_b.clicked.connect(self.MySlot)

两个信息或者说事件都指向同一个槽函数:

    def MySlot(self):

        sender = self.sender()

        self.statusBar().showMessage('刚刚触发的按钮是' + sender.text())

这个函数使用了sender函数;这个函数的作用是获得用户点击窗口中某个控件的信息,我们使用text方法获得控件的信息显示文本并通过showMessage方法显示在了状态栏中。

这个示例中我们需要注意的是如何在继承自QMainWindow的类中使用QWidget类的方法;同时在定义槽函数时使用的sender方法也是我们需要关注的。

四. 实例:做选择

import sys
from PyQt5.QtWidgets import QApplication,\
                            QWidget,\
                            QMainWindow,\
                            QPushButton,\
                            QHBoxLayout,\
                            QVBoxLayout,\
                            QLabel

class MyWindow(QMainWindow):

    def __init__(self):
        super().__init__()

        self.setMyWidget()
        self.setMyWindow()

    def setMyWidget(self):
        '对窗口中的按钮控件进行设置'

        label_tip = self.setMyLabel()
        button_a,button_b = self.setMyButton()

        self.setMyLayout(label_tip,button_a,button_b)

        self.statusBar()
        self.setMyAnswer(button_a,button_b)

    def setMyWindow(self):
        '设置窗口的基础内容'

        self.resize(600,600)
        self.move(0,0)
        self.setWindowTitle('请做出选择')
        self.show()

    def setMyLabel(self):

        label_tip = QLabel('如果有机会,你要选择金钱还是选择爱情?')

        return label_tip

    def setMyButton(self):

        button_a = QPushButton('金钱')
        button_b = QPushButton('爱情')

        return button_a,button_b

    def setMyLayout(self,label_tip,button_a,button_b):

        widget = QWidget()

        hbox_a = QHBoxLayout()
        hbox_a.addStretch(1)
        hbox_a.addWidget(label_tip)
        hbox_a.addStretch(1)

        hbox_b = QHBoxLayout()
        hbox_b.addStretch(1)
        hbox_b.addWidget(button_a)
        hbox_b.addStretch(1)
        hbox_b.addWidget(button_b)
        hbox_b.addStretch(1)

        vbox = QVBoxLayout()
        vbox.addStretch(2)
        vbox.addLayout(hbox_a)
        vbox.addStretch(1)
        vbox.addLayout(hbox_b)
        vbox.addStretch(2)

        widget.setLayout(vbox)
        self.setCentralWidget(widget)

    def setMyAnswer(self,button_a,button_b):

        button_a.clicked.connect(self.slot_a)
        button_b.clicked.connect(self.slot_b)

    def slot_a(self):

        self.statusBar().showMessage('你选择了金钱而不是爱情,生活很空虚。')

    def slot_b(self):

        self.statusBar().showMessage('你选择了爱情而不是金钱,生活很艰难。')

if __name__ == '__main__':
    app = QApplication(sys.argv)
    w = MyWindow()
    sys.exit(app.exec_())

这个程序在窗口中给出一个问题和两个答案,当你选择某个答案、按下对应的按钮后,状态栏里会显示出相应的评价,具体效果如下:

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值