PyQt5中的事件与信号处理

1. 简介

在PyQt5中,事件和信号处理是GUI编程的核心概念。事件是指用户操作或系统消息(如鼠标点击、键盘输入、窗口大小变化等),而信号是指对象之间的通信机制,用于在一个事件发生时通知其他对象执行相应的操作。PyQt5提供了丰富的类和方法来处理事件和信号。

1.1事件(Event)

事件是用户与应用程序交互时发生的动作或状态变化,例如点击鼠标、按键盘等。在PyQt5中,每个窗口小部件(widget)都有一个事件处理器(event handler),它用于处理与该小部件相关的事件。

事件处理器是一个函数,它接收一个事件对象作为参数,并根据事件的类型执行相应的操作。常用的事件处理器包括:

  • mousePressEvent():处理鼠标按下事件。
  • mouseReleaseEvent():处理鼠标释放事件。
  • keyPressEvent():处理键盘按下事件。
  • resizeEvent():处理窗口大小变化事件。

以下是一个简单的示例,演示如何在PyQt5中处理鼠标点击事件:

from PyQt5.QtWidgets import QApplication, QLabel

class MyLabel(QLabel):
    def mousePressEvent(self, event):
        print("鼠标点击了标签!")

app = QApplication([])
label = MyLabel("点击我!")
label.show()
app.exec_()
  • 首先,导入了QApplicationQLabel类,它们分别是QtWidgets模块中用于创建应用程序和标签的类。
  • 接着,定义了一个继承自QLabel的子类MyLabel。在MyLabel类中,重写了mousePressEvent()方法,该方法在鼠标点击事件发生时被调用。当鼠标点击标签时,会在控制台输出消息“鼠标点击了标签!”。
  • 创建了一个QApplication对象,作为整个应用程序的实例。这是每个PyQt5应用程序的必需步骤。
  • 创建了一个MyLabel对象,并传入了一个字符串参数作为标签的文本内容。
  • 调用show()方法显示标签窗口,使其可见。
  • 调用exec_()方法启动应用程序的事件循环,使应用程序保持运行状态,直到用户退出。在此期间,应用程序将等待用户交互事件,例如鼠标点击、键盘输入等。

运行如下图
在这里插入图片描述

鼠标点击窗口里的三个字,就会触发事件,在控制台输出

在这里插入图片描述

1.2 信号(Signal)与槽(Slot)

信号和槽是PyQt5中用于对象之间通信的机制。当一个对象发出信号时,其他对象可以连接到该信号,并在信号触发时执行相应的槽函数。

  • 信号是对象发出的事件,例如按钮被点击、文本框内容变化等。
  • 槽是响应信号的函数,可以是对象的任意方法。

PyQt5中的大多数小部件都有一些内置的信号,例如按钮的clicked信号、文本框的textChanged信号等。可以使用connect()方法将信号连接到槽函数上。

以下是一个示例,演示如何连接按钮的clicked信号到槽函数:

from PyQt5.QtWidgets import QApplication, QPushButton

def on_button_clicked():
    print("按钮被点击了!")

app = QApplication([])
button = QPushButton("点击我!")
button.clicked.connect(on_button_clicked)
button.show()
app.exec_()
  • 这里,我们定义了一个名为on_button_clicked()的函数。这个函数没有参数,当按钮被点击时会被调用,它的作用是在控制台输出消息“按钮被点击了!”。
  • 创建了一个QPushButton对象,并传入了一个字符串参数作为按钮的文本内容。
  • 调用了按钮的clicked信号,并使用connect()方法将其连接到on_button_clicked()函数上。这意味着当按钮被点击时,on_button_clicked()函数会被调用。

运行如下图

在这里插入图片描述

鼠标点击窗口里的三个字,就会触发事件,在控制台输出

在这里插入图片描述

1.3 自定义信号

除了使用内置信号外,还可以自定义信号来实现对象之间的通信。可以使用pyqtSignal()方法创建自定义信号,然后使用emit()方法发出信号。

以下是一个示例,演示如何创建并使用自定义信号:

from PyQt5.QtCore import QObject, pyqtSignal

class MyObject(QObject):
    my_signal = pyqtSignal(str)

    def do_something(self):
        # 发出信号
        self.my_signal.emit("Hello from custom signal!")

def on_custom_signal(value):
    print("接收到自定义信号:", value)

obj = MyObject()
obj.my_signal.connect(on_custom_signal)
obj.do_something()
  • 首先,导入了QObjectpyqtSignal类,它们分别是QtCore模块中用于创建对象和自定义信号的类。
  • 接着,定义了一个名为MyObject的类,它继承自QObject。在MyObject类中,创建了一个名为my_signal的类属性,它是一个pyqtSignal对象,用于定义一个带有一个字符串参数的自定义信号。
  • MyObject类中定义了一个名为do_something()的方法。在这个方法中,调用了self.my_signal.emit()方法发出自定义信号,并传入了字符串参数"Hello from custom signal!"。
  • 接下来,定义了一个名为on_custom_signal()的函数,它有一个参数value,用于接收信号发出的参数。在这个函数中,打印了接收到的自定义信号和传入的参数。
  • 创建了一个MyObject对象,用于发出自定义信号。
  • 调用了对象的my_signal信号,并使用connect()方法将其连接到on_custom_signal()函数上。这样,当MyObject对象发出自定义信号时,on_custom_signal()函数会被调用。
  • 调用了do_something()方法,触发了自定义信号的发出。一旦自定义信号发出,与之连接的槽函数on_custom_signal()就会被调用,打印出"Hello from custom signal!"。

运行上面程序,我们有如下结果

在这里插入图片描述

2. 一个信号与槽的简单示例1

下面是一个简单的示例,展示了如何使用PyQt5创建一个拨动(QDial)和一个LCD数字显示屏(QLCDNumber),当拨动改变时,LCD数字显示屏会实时更新显示拨动的值。

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QDial, QLCDNumber

class DialLCDExample(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        # 设置窗口标题和大小
        self.setWindowTitle('拨动与LCD示例')
        self.setGeometry(100, 100, 300, 200)

        # 创建垂直布局管理器
        layout = QVBoxLayout()

        # 创建拨动和LCD数字显示屏
        self.dial = QDial()  # 创建QDial对象
        self.lcd = QLCDNumber()  # 创建QLCDNumber对象

        # 设置拨动的范围和步长
        self.dial.setMinimum(0)  # 设置拨动的最小值
        self.dial.setMaximum(100)  # 设置拨动的最大值
        self.dial.setValue(50)  # 设置初始值
        self.dial.setSingleStep(1)  # 设置拨动的步长

        # 将拨动的valueChanged信号连接到LCD数字显示屏的display槽上
        self.dial.valueChanged.connect(self.lcd.display)

        # 将拨动和LCD数字显示屏添加到布局中
        layout.addWidget(self.dial)
        layout.addWidget(self.lcd)

        # 设置窗口的布局为垂直布局管理器
        self.setLayout(layout)

if __name__ == '__main__':
    app = QApplication(sys.argv)  # 创建应用程序实例
    window = DialLCDExample()  # 创建窗口实例
    window.show()  # 显示窗口
    sys.exit(app.exec_())  # 运行应用程序的事件循环

在上面程序中:

  • QApplication:用于创建应用程序实例,每个PyQt5程序都需要一个。
  • QWidget:所有用户界面对象的基类,这里的窗口继承自QWidget
  • QVBoxLayout:垂直布局管理器,用于管理窗口中的控件布局。
  • QDial:拨动控件,可以让用户通过拨动手柄来选择数值。
  • QLCDNumber:用于显示数字的LCD屏幕控件。
  • init_ui():初始化用户界面的方法,设置窗口标题、大小、布局等。
  • setValue():设置拨动控件的初始值。
  • setSingleStep():设置拨动控件的步长。
  • valueChanged:拨动控件的值变化时发出的信号。
  • connect():将信号连接到槽函数上,当拨动控件的值变化时,调用槽函数更新LCD数字显示屏的值。
  • show():显示窗口。
  • exec_():运行应用程序的事件循环。

运行程序,结果如下

在这里插入图片描述

3. 一个信号与槽的简单示例2

下面是一个简单的PyQt5示例,演示了如何重写keyPressEvent()方法来处理按键事件,当按住上、下、左、右方向键时,窗口中的文本内容会依次在对应方位移动。

import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel
from PyQt5.QtCore import Qt

class KeyPressEventExample(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        # 设置窗口标题和大小
        self.setWindowTitle('按键事件处理示例')
        self.setGeometry(100, 100, 300, 200)

        # 创建一个标签,并将其文本居中显示
        self.label = QLabel("按住上、下、左、右方向键试试")
        self.label.setAlignment(Qt.AlignCenter)

        # 创建垂直布局管理器
        layout = QVBoxLayout()
        layout.addWidget(self.label)  # 将标签添加到布局中
        self.setLayout(layout)  # 设置窗口的布局为垂直布局管理器

    def keyPressEvent(self, event):
        # 检测按下的按键
        key = event.key()

        # 获取窗口的当前位置
        current_pos = self.label.pos()

        # 移动文本内容的方位
        if key == Qt.Key_Up:
            self.label.move(current_pos.x(), current_pos.y() - 10)  # 向上移动文本内容
        elif key == Qt.Key_Down:
            self.label.move(current_pos.x(), current_pos.y() + 10)  # 向下移动文本内容
        elif key == Qt.Key_Left:
            self.label.move(current_pos.x() - 10, current_pos.y())  # 向左移动文本内容
        elif key == Qt.Key_Right:
            self.label.move(current_pos.x() + 10, current_pos.y())  # 向右移动文本内容

if __name__ == '__main__':
    app = QApplication(sys.argv)  # 创建应用程序实例
    window = KeyPressEventExample()  # 创建窗口实例
    window.show()  # 显示窗口
    sys.exit(app.exec_())  # 运行应用程序的事件循环

在上面程序中,

  • QApplication:用于创建应用程序实例,每个PyQt5程序都需要一个。
  • QWidget:所有用户界面对象的基类,这里的窗口继承自QWidget
  • QVBoxLayout:垂直布局管理器,用于管理窗口中的控件布局。
  • QLabel:用于显示文本或图像的标签控件。
  • init_ui():初始化用户界面的方法,设置窗口标题、大小、布局等。
  • keyPressEvent():重写了按键事件处理方法,当按下方向键时会调用此方法。
  • event.key():获取按下的按键。
  • Qt.Key_UpQt.Key_DownQt.Key_LeftQt.Key_Right:定义了方向键的键码。
  • pos():获取标签的当前位置。
  • move():移动标签的位置。
  • show():显示窗口。
  • exec_():运行应用程序的事件循环。

运行上面程序,我们会有如下结果

在这里插入图片描述

4. 事件发送者

在PyQt5中,事件发送者(event sender)是指触发事件的对象。当用户与GUI中的控件交互时(例如点击按钮、输入文本等),这些控件会发出相应的事件信号。事件发送者是触发这些信号的对象,可以通过事件对象来获取事件发送者的信息。

以下是一个简单的示例,演示了如何使用事件发送者:

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

class EventSenderExample(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        self.setWindowTitle('事件发送者示例')
        self.setGeometry(100, 100, 300, 200)

        layout = QVBoxLayout()

        self.label = QLabel("等待按钮点击...")
        layout.addWidget(self.label)

        self.button1 = QPushButton("按钮1")
        self.button1.clicked.connect(self.on_button_click)
        layout.addWidget(self.button1)

        self.button2 = QPushButton("按钮2")
        self.button2.clicked.connect(self.on_button_click)
        layout.addWidget(self.button2)

        self.setLayout(layout)

    def on_button_click(self):
        sender = self.sender()  # 获取事件发送者
        self.label.setText(f"按钮 {sender.text()} 被点击了")

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

在这个示例中,有两个按钮,当其中任何一个按钮被点击时,标签文本会显示哪个按钮被点击了。在on_button_click()方法中,self.sender()方法用于获取事件发送者,即触发信号的按钮对象。然后可以通过获取到的按钮对象进行相应的操作。

在这里插入图片描述

5. 创建自定义信号

在PyQt5中,可以通过继承自QObject的子类来创建自定义信号。使用pyqtSignal()方法创建自定义信号,并使用emit()方法发出信号。其他对象可以连接到这个自定义信号,并在信号触发时执行相应的槽函数。

下面是一个简单的示例,演示了如何创建和使用自定义信号,这个示例基于1.3里的例子进一步改进:

import sys
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtWidgets import QApplication, QPushButton, QVBoxLayout, QWidget

class MyObject(QObject):
    # 创建一个自定义信号
    my_signal = pyqtSignal(str)

    def do_something(self):
        # 发出自定义信号
        self.my_signal.emit("Hello from custom signal!")

class CustomSignalExample(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        self.setWindowTitle('自定义信号示例')
        self.setGeometry(100, 100, 300, 200)

        layout = QVBoxLayout()

        self.button = QPushButton("点击发出自定义信号")
        layout.addWidget(self.button)

        self.setLayout(layout)

        self.button.clicked.connect(self.on_button_click)

    def on_button_click(self):
        obj = MyObject()
        obj.my_signal.connect(self.on_custom_signal)
        obj.do_something()

    def on_custom_signal(self, value):
        print("接收到自定义信号:", value)

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

在这个示例中,创建了一个名为MyObject的QObject子类,并在其中定义了一个自定义信号my_signal。在do_something()方法中,通过调用emit()方法来发出自定义信号。

CustomSignalExample窗口中,有一个按钮,当按钮被点击时,会创建MyObject对象,并连接其自定义信号到槽函数on_custom_signal上。然后调用do_something()方法,发出自定义信号。当自定义信号被发出时,槽函数on_custom_signal会被调用,并打印出接收到的自定义信号的值。

运行程序,我们会得到

在这里插入图片描述

点击按钮,会在控制端输出

在这里插入图片描述

6. 一个简单计算器

下面是一个比较复杂的事件与槽的例子,演示了如何使用PyQt5创建一个简单的计算器应用程序。用户可以在文本框中输入两个数字,然后点击按钮执行加法、减法、乘法或除法运算,并将结果显示在另一个文本框中。

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

class CalculatorApp(QWidget):
    def __init__(self):
        super().__init__()
        self.init_ui()

    def init_ui(self):
        self.setWindowTitle('简单计算器')
        self.setGeometry(100, 100, 300, 200)

        # 创建布局
        main_layout = QVBoxLayout()
        input_layout = QHBoxLayout()
        output_layout = QHBoxLayout()

        # 创建输入框和标签
        self.input1 = QLineEdit()
        self.input2 = QLineEdit()
        self.operation_label = QLabel()

        # 创建按钮
        self.add_button = QPushButton('+')
        self.sub_button = QPushButton('-')
        self.mul_button = QPushButton('*')
        self.div_button = QPushButton('/')

        # 创建结果显示标签
        self.result_label = QLabel()

        # 将输入框和按钮添加到输入布局中
        input_layout.addWidget(self.input1)
        input_layout.addWidget(self.input2)
        input_layout.addWidget(self.operation_label)

        # 将按钮添加到输出布局中
        output_layout.addWidget(self.add_button)
        output_layout.addWidget(self.sub_button)
        output_layout.addWidget(self.mul_button)
        output_layout.addWidget(self.div_button)

        # 将布局添加到主布局中
        main_layout.addLayout(input_layout)
        main_layout.addLayout(output_layout)
        main_layout.addWidget(self.result_label)

        self.setLayout(main_layout)

        # 连接按钮的点击事件到槽函数
        self.add_button.clicked.connect(self.add)
        self.sub_button.clicked.connect(self.subtract)
        self.mul_button.clicked.connect(self.multiply)
        self.div_button.clicked.connect(self.divide)

    # 定义加法槽函数
    def add(self):
        num1 = float(self.input1.text())
        num2 = float(self.input2.text())
        result = num1 + num2
        self.operation_label.setText('+')
        self.result_label.setText(f'结果:{result}')

    # 定义减法槽函数
    def subtract(self):
        num1 = float(self.input1.text())
        num2 = float(self.input2.text())
        result = num1 - num2
        self.operation_label.setText('-')
        self.result_label.setText(f'结果:{result}')

    # 定义乘法槽函数
    def multiply(self):
        num1 = float(self.input1.text())
        num2 = float(self.input2.text())
        result = num1 * num2
        self.operation_label.setText('*')
        self.result_label.setText(f'结果:{result}')

    # 定义除法槽函数
    def divide(self):
        num1 = float(self.input1.text())
        num2 = float(self.input2.text())
        if num2 != 0:
            result = num1 / num2
            self.operation_label.setText('/')
            self.result_label.setText(f'结果:{result}')
        else:
            self.result_label.setText('除数不能为零')

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

运行上面程序,我们可以得到

在这里插入图片描述

  • 40
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xy_optics

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值