PyQt5信号槽机制(二)
继PyQt5信号槽机制1
信号槽应用进阶
自定义信号槽
通常通过类变量自定义信号对象,在__init__
函数前自定义信号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| class MyQtObject(QObject): noParameterSignal = pyqtSignal() oneParameterSignal = pyqtSignal(int) oneParameterOverloadSignal = pyqtSignal([int], [str]) twoParameterOverloadSignal = pyqtSignal([int, str], [int, int]) oneParameterSignalList = pyqtSignal(list) oneParameterSignalDict = pyqtSignal(dict) def onNoParameterSlot(self): print("no parameter signal") def onOneParameterSlot(self, nIndex): print("one parameter signal: ", nIndex) def onTwoParameterSlot(self, nIndex, status): print("two parameter signal:", nIndex, status) def update(self): self.noParameterSignal.emit() self.oneParameterSignal.emit() self.twoParameterOverloadSignal.emit() if __name__ == '__main__':
MyQtObj = MyQtObject() MyQtObj.noParameterSignal.connect(onNoParameterSlot) MyQtObj.oneParameterSignal.connect(onOneParameterSlot) MyQtObj.twoParameterOverloadSignal(onTwoParameterSlot) MyQtObj.update()
|
信号槽传递自定义参数
Qt中信号个数必须大于等于槽函数参数个数(槽函数接受来自于Qt信号作为参数),PyQt使用自定义参数传递可以解决槽函数参数比信号参数多的问题。使用Lambda表达式或functools的partial
函数可以传递自定义参数给槽函数,自定义参数类型可以是Python的任意类型.
- 个人理解:原本通过
pyqtSignal
定义的信号中参数数量是固定的,他和槽函数一一对应,通过connect
函数传递。但是有一些信号,如button.clicked
信号就没有参数了,但是有些时候槽函数需要这个信号的相关信息,比如是哪个按钮,这就需要信号传递参数给槽函数。
- Lambda表达式或
partial
函数可以作为无参数或参数比默认的少的函数,可以骗过无参数的信号。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import sys from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QHBoxLayout from functools import partial
class MainWindow(QWidget): def __init__(self, parent=None): super().__init__(parent) button1 = QPushButton("Button1", self) button2 = QPushButton("Button2", self)
layout = QHBoxLayout() layout.addWidget(button1) layout.addWidget(button2) self.setLayout(layout)
self.setWindowTitle("MainWindow Demo") self.resize(800, 600)
button1.clicked.connect(lambda: self.onButtonClick(1)) button2.clicked.connect(lambda: self.onButtonClick(2))
def onButtonClick(self, n): print("Button {0} is Clicked".format(n))
if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show()
sys.exit(app.exec_())
|
信号槽与装饰器
函数装饰器
可以用函数装饰器定义信号槽和槽函数,使用方法如下
1 2 3
| @PyQt5.QtCore.pyqtSlot(bool) def on_发送者对象名称_发射信号名称(self, parameter): pass
|
使用函数装饰器时,必须用connectSlotsByName(self, QObject)
连接槽函数和信号
QtCore.QMetaObject.connectSlotsByName(self, QObject)
connectSlotsByName用于将QObject子孙对象的某些信号根据名称连接到某些QObject对象的相应槽函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| import sys from PyQt5 import QtCore from PyQt5.QtWidgets import QWidget, QApplication, QPushButton, QHBoxLayout
class MainWindow(QWidget): def __init__(self, parent = None): super().__init__(parent) button1 = QPushButton("Button1", self) button1.setObjectName("Button1") button2 = QPushButton("Button2", self) button2.setObjectName("Button2")
layout = QHBoxLayout()
layout.addWidget(button1) layout.addWidget(button2) self.setLayout(layout)
self.setWindowTitle("MainWindow Demo") self.resize(800, 600)
QtCore.QMetaObject.connectSlotsByName(self)
@QtCore.pyqtSlot() def on_Button1_clicked(self): print("Button1 is clicked")
@QtCore.pyqtSlot() def on_Button2_clicked(self): print("Button2 is clicked")
if __name__ == '__main__':
app = QApplication(sys.argv) window = MainWindow() window.show()
sys.exit(app.exec_())
|
事件处理机制
事件机制与信号槽机制的区别
PyQt为事件处理提供了高级别的信号槽机制和低级别的事件处理机制,信号槽机制是事件处理机制的高级封装。使用控件时,不用考虑事件处理机制,只需要关心信号槽即可;对于自定义派生控件,必须考虑事件处理机制,根据控件的行为需求重新实现相应的事件处理函数
事件处理的方法
共5种事件处理的方法,分别为:
- 重新实现事件处理函数
常用的事件处理函数如paintEvent
,mouseMoveEvent
、mousePressEvent
、mouseReleaseEvent
等
- 重新实现QObject.event事件分发函数
在增加新的事件时,需要重新实现QObject.event方法,并增加新事件的分发路由
- 安装事件过滤器
如果对QObject对象调用installEventFilter方法,则为QObject对象安装事件过滤器。QObject对象的所有事件都会先传递到事件过滤器eventFilter函数,在事件过滤器eventFilter函数中可以丢弃或修改某些事件,对感兴趣的事件使用自定义的事件处理机制,对其它事件使用默认事件处理机制。事件过滤机制会对QObject的所有事件进行过滤,因此如果要过滤的事件比较多则会影响程序性能。
- 在QApplication安装事件过滤器
在QApplication对象安装事件过滤器将会对所有QObject对象的所有事件进行过滤,并且会首先获得事件,即将事件发送给其它任何一个事件过滤器前,都会首先发送给QApplication的事件过滤器。
- 重新QApplication的notify方法
PyQt使用QApplication对象的notify方法进行分发事件,要想在任何事件过滤器前捕获事件唯一的方法就是重新实现QApplication的notify方法。
事件处理实例
QDialog对话框在ESC按键按下时会自动退出,使用事件处理和过滤对按下ESC按键进行处理。
- 重新实现事件处理函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import sys from PyQt5.QtWidgets import QDialog, QApplication from PyQt5.QtCore import Qt
class Dialog(QDialog): def __init__(self, parent=None): super().__init__(parent)
def keyPressEvent(self, event): if event.key() == Qt.Key_Escape: QDialog.keyPressEvent(self, event) print(event.key())
if __name__ == "__main__": app = QApplication(sys.argv)
dialog = Dialog() dialog.show()
sys.exit(app.exec_())
|
- 重新实现event函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import sys from PyQt5.QtWidgets import QDialog, QApplication from PyQt5.QtCore import Qt from PyQt5.QtGui import QKeyEvent
class Dialog(QDialog): def __init__(self, parent=None): super().__init__(parent)
def event(self, event): if event.type() == QKeyEvent.KeyPress and event.key() == Qt.Key_Escape: return QDialog.event(self, event) else: return True
if __name__ == "__main__": app = QApplication(sys.argv) dialog = Dialog() dialog.exec_()
sys.exit(app.exec_())
|
- QObject安装事件过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| import sys from PyQt5.QtWidgets import QDialog, QApplication from PyQt5.QtCore import Qt from PyQt5.QtGui import QKeyEvent
class Dialog(QDialog): def __init__(self, parent=None): super().__init__(parent) self.installEventFilter(self)
def eventFilter(self, watched, event): if event.type() == QKeyEvent.KeyPress and event.key() == Qt.Key_Escape: return QDialog.eventFilter(self, watched, event)
else: return True
if __name__ == "__main__": app = QApplication(sys.argv) dialog = Dialog() dialog.exec_()
sys.exit(app.exec_())
|
- QApplication安装事件过滤器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import sys from PyQt5.QtWidgets import QDialog, QApplication from PyQt5.QtCore import Qt from PyQt5.QtGui import QKeyEvent
class Dialog(QDialog): def __init__(self, parent=None): super().__init__(parent)
def eventFilter(self, watched, event): if event.type() == QKeyEvent.KeyPress and event.key() == Qt.Key_Escape: return QDialog.eventFilter(self, watched, event) else: return True
if __name__ == "__main__": app = QApplication(sys.argv) dialog = Dialog() app.installEventFilter(dialog) dialog.exec_()
sys.exit(app.exec_())
|
参考自:
PyQt5快速入门(二)PyQt5信号槽机制