文章目录
- 1. 简介
- 1.1事件(Event)
- 1.2 信号(Signal)与槽(Slot)
- 1.3 自定义信号
- 2. 一个信号与槽的简单示例1
- 3. 一个信号与槽的简单示例2
- 4. 事件发送者
- 5. 创建自定义信号
- 6. 一个简单计算器
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_()
- 首先,导入了
QApplication
和QLabel
类,它们分别是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()
- 首先,导入了
QObject
和pyqtSignal
类,它们分别是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_Up
、Qt.Key_Down
、Qt.Key_Left
、Qt.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_())
运行上面程序,我们可以得到