PyQt5 事件传递分析和信号解析

348 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

PyQt5 事件传递分析和信号解析

事件传递使我们程序员经常需要处理的问题,特别是在做用户交互型的软件开发,我们通常都会涉及到事件传递,如Android中的事件传递,当手指触摸屏幕事件一步步传到界面的控件上,今天我们来介绍下PyQt5这个强大的桌面端框架中的事件

1,事件传递图

image.png

2,事件传递简要概述

上面的图是事件传递最基本的情况,首先事件信息,这里包括很多信息,例如点击事件,绘制事件,我们通过继承QApplication来查看这里面的事件信息

class App(QApplication):
    def notify(self, a0: QObject, a1: QEvent):
        print(a0)
        print(a1)
        # if a0.inherits("QPushButton"):
        #     if a1.type() == QEvent.MouseButtonPress:
        #         print(a0)
        #         print(a1)
        super().notify(a0, a1)
        return False
<PyQt5.QtGui.QMoveEvent object at 0x000001AB9850EAF8>
<PyQt5.QtGui.QWindow object at 0x000001AB9850EA68>
<PyQt5.QtCore.QEvent object at 0x000001AB9850EAF8>
<__main__.Window object at 0x000001AB9850E948>
<PyQt5.QtCore.QEvent object at 0x000001AB9850EAF8>
<PyQt5.QtGui.QWindow object at 0x000001AB9850EA68>
<PyQt5.QtCore.QEvent object at 0x000001AB9850EAF8>
<__main__.Window object at 0x000001AB9850E948>
<PyQt5.QtCore.QEvent object at 0x000001AB9850EAF8>
<PyQt5.QtGui.QWindow object at 0x000001AB9850EA68>
<PyQt5.QtCore.QEvent object at 0x000001AB9850EAF8>
<__main__.Window object at 0x000001AB9850E948>
...

这里我们看到有很多事件,读者可以去运行事例,最后我们贴上源码,说道这里,其实所有事件最先是在

QApplication的notify进行处理的,这个方法中的两个参数一个是对象类型,一个是事件类型,我们从上面的log信息中可以看得出来

最后我们调用父方法,返回False表示的是我们不阻拦事件,将事件信息继续向下传递,如果返回True表示消费了事件


接下来我们自定义一个Button继承QPushButton

class Btn(QPushButton):
    def event(self, e: QEvent):
    	#打印QpushButton的事件信息
    	print(e)
        return super().event(e)
class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(600, 300)
        self.setUI()
    def setUI(self):
        btn = Btn(self)
        btn.setText("按钮")
        btn.move(100, 100)
PyQt5.QtCore.QEvent object at 0x000001D8D23FEDC8>
<PyQt5.QtGui.QPaintEvent object at 0x000001D8D23FEAF8>
<PyQt5.QtCore.QEvent object at 0x000001D8D23FEDC8>
<PyQt5.QtGui.QPaintEvent object at 0x000001D8D23FEAF8>
<PyQt5.QtCore.QEvent object at 0x000001D8D23FEDC8>
<PyQt5.QtGui.QPaintEvent object at 0x000001D8D23FEAF8>
<PyQt5.QtCore.QEvent object at 0x000001D8D23FEDC8>
<PyQt5.QtCore.QChildEvent object at 0x000001D8D23FEB88>
<PyQt5.QtGui.QPaintEvent object at 0x000001D8D23FEAF8>
<PyQt5.QtCore.QEvent object at 0x000001D8D23FEB88>
<PyQt5.QtCore.QEvent object at 0x000001D8D23FEB88>
<PyQt5.QtGui.QFocusEvent object at 0x000001D8D23FEB88>
<PyQt5.QtGui.QHideEvent object at 0x000001D8D23FEB88>

这里我们看到,传递到QPushButton上的事件还是非常的多,但是我们可以进行过滤
    def event(self, e: QEvent):
        if e.type() == QEvent.MouseButtonPress:
            print(e)
            # 根据事件的类型进行分发
        return super().event(e)
    # 打印 <PyQt5.QtGui.QMouseEvent object at 0x000002639E73EB88>

到此信息传递到了QPushButton上,接着就是触发具体事件了,如点击事件,双击事件,移动事件

class Btn(QPushButton):
    def event(self, e: QEvent):
        if e.type() == QEvent.MouseButtonPress:
            print(e)
            # 根据事件的类型进行分发
        return super().event(e)
    def mousePressEvent(self, e: QMouseEvent):
        print("鼠标按下了")
        return super().mousePressEvent(e)
   # 当出表按下的时候,控制台打印 鼠标按下了

此时我们再定义一个信号和槽函数

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(600, 300)
        self.setUI()
    def setUI(self):
        btn = Btn(self)
        btn.setText("按钮")
        btn.move(100, 100)
        btn.pressed.connect(self.click)
    def click(self):
        print("按钮点击信号槽函数调用了")

按道理来说,按钮点击后应该是会触发相关事件,调用绑定的click方法,但是我们运行后发现,并没有调用这个槽函数,是怎么回事呢

def mousePressEvent(self, e: QMouseEvent):
	pass

原因是这个函数里面就是发射信号的地方,当我们重写的时候,就没有发射这个pressed信号了,自然槽函数不会调用了,当我们注释这段代码的时候发现,槽函数能够正常的调用,另外一种方法就是,调用父级的方法,父级里面会有发射信号的操作

    def mousePressEvent(self, e: QMouseEvent):
        print("鼠标按下了")
        return super().mousePressEvent(e)

在这里插入图片描述

也就是是我们最后用的这些信号都是在具体的时间方法接收器中进行发射的如以下的方法

    def mousePressEvent(self, e: QMouseEvent):
        print("鼠标按下了")
        return super().mousePressEvent(e)
    def mouseReleaseEvent(self, e: QMouseEvent):
        pass
    def mouseDoubleClickEvent(self, a0: QMouseEvent):
        pass
    def mouseMoveEvent(self, e: QMouseEvent):
        pass

上面这么其实也就是介绍了,在PyQt5中,窗口事件是怎么一步步向下传递的

最后贴上测试源码: (点个赞赛,我大晚上的写博客不容易啊)

import sys

from PyQt5.QtCore import QObject, QEvent
from PyQt5.QtGui import QMouseEvent
from PyQt5.QtWidgets import QWidget, QLabel, QApplication, QPushButton


class App(QApplication):
    def notify(self, a0: QObject, a1: QEvent):
        # if a0.inherits("QPushButton"):
        #     if a1.type() == QEvent.MouseButtonPress:
        #         print(a0)
        #         print(a1)
        super().notify(a0, a1)
        return True

class Btn(QPushButton):
    def event(self, e: QEvent):
        if e.type() == QEvent.MouseButtonPress:
            print(e)
            # 根据事件的类型进行分发
        return super().event(e)
    def mousePressEvent(self, e: QMouseEvent):
        print("鼠标按下了")
        return super().mousePressEvent(e)

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.resize(600, 300)
        self.setUI()
    def setUI(self):
        btn = Btn(self)
        btn.setText("按钮")
        btn.move(100, 100)
        btn.pressed.connect(self.click)
    def click(self):
        print("按钮点击信号槽函数调用了")
if __name__ == '__main__':

    app = App(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())