0%

PyQt5信号槽机制(二)

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)
# 定义一个参数的重载版本的信号,参数类型可以为int或str
oneParameterOverloadSignal = pyqtSignal([int], [str])
# 定义两个个参数的重载版本的信号,参数类型可以为int,str或int,int
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))
# button1.clicked.connect(partial(self.onButtonClick, 1))
# button2.clicked.connect(partial(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种事件处理的方法,分别为:

  1. 重新实现事件处理函数
    常用的事件处理函数如paintEventmouseMoveEventmousePressEventmouseReleaseEvent
  2. 重新实现QObject.event事件分发函数
    在增加新的事件时,需要重新实现QObject.event方法,并增加新事件的分发路由
  3. 安装事件过滤器
    如果对QObject对象调用installEventFilter方法,则为QObject对象安装事件过滤器。QObject对象的所有事件都会先传递到事件过滤器eventFilter函数,在事件过滤器eventFilter函数中可以丢弃或修改某些事件,对感兴趣的事件使用自定义的事件处理机制,对其它事件使用默认事件处理机制。事件过滤机制会对QObject的所有事件进行过滤,因此如果要过滤的事件比较多则会影响程序性能。
  4. 在QApplication安装事件过滤器
    在QApplication对象安装事件过滤器将会对所有QObject对象的所有事件进行过滤,并且会首先获得事件,即将事件发送给其它任何一个事件过滤器前,都会首先发送给QApplication的事件过滤器。
  5. 重新QApplication的notify方法
    PyQt使用QApplication对象的notify方法进行分发事件,要想在任何事件过滤器前捕获事件唯一的方法就是重新实现QApplication的notify方法。

事件处理实例

QDialog对话框在ESC按键按下时会自动退出,使用事件处理和过滤对按下ESC按键进行处理。

  1. 重新实现事件处理函数
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)

# 重新实现keyPressEvent
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_())
  1. 重新实现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)

# 重新实现keyPressEvent
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_())
  1. 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_())
  1. 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信号槽机制