PyQt5的事件机制

Posted 哦...

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PyQt5的事件机制相关的知识,希望对你有一定的参考价值。

基于widget的应用程序都是由事件event驱动的,像鼠标单击、按下某个按键、重回组件、最小化窗口等都会产生相应的事件。

应用程序的事件循环

app = QApplication(sys.argv)
form = QWidget()
form.show()
sys.exit(app.exec_())

最后执行的app.exec_()开启了应用程序的事件处理循环。

应用程序会对事件队列中排队的事件进行处理,还可以对相同事件进行合并处理。

事件类型与默认的事件处理函数

PyQt5中,事件是一种对象,事件的基类是抽象类QEvent。

QEvent有众多子类表示具体的事件,例如QKeyEvent表示按键事件,QMouseEvent表示鼠标事件,QPaintEvent表示窗体绘制事件。

当一个事件发生时,PyQt5会根据事件的具体类型用QEvent相应的子类创建一个事件对象,然后传递给产生事件的对象的event()函数进行处理

event(self,e)

event函数的参数e就是PyQt5调用event函数时传入的事件对象。

QEvent类定义了三个接口函数:

accept() 表示事件接收者接受此事件。被接受的事件不会再继续向上传播给上层容器组件。

ignore() 表示事件接收者忽略此事件。被忽略的事件会继续向上传播给上层容器组件。

type() 返回事件的类型。事件类型是枚举,每一个枚举值都对应一个PyQt5的具体事件类型。例如QMouseEvent的type枚举值为5。不同事件类型对应的枚举值可以参考官方文档

事件会优先发送给触发事件对象的event函数,但是event函数默认是不做额外的具体处理,而是将事件转派给触发事件对象的各种事件的默认处理函数。例如:event函数会将type为QMouseEvent事件会转派给触发事件对象的mouseMoveEvent()函数,会将type为QMouseButtonDblClick事件会转派给触发事件对象的mouseDoubleClickEvent()函数。

每一个QWidget都定义了很多这样的默认事件处理函数,都会接受一个具体的event事件对象。每一个具体的event对象出了实现基础QEvent对象的接口之外,还会提供很多其它的与事件相关的函数。例如QMouseEvent对象还提供了返回鼠标位置的pos()/localPos()等函数。

用户在继承QWidget或者其子类的自定义类型中可以重新实现这些默认的事件处理函数,从而实现一些需要的功能。例如,某些组件没有clicked信号,那么就不能通过信号与槽的方式实现对鼠标单击的处理,但是可以重新实现mousePressEvent()或mouseReleaseEvent()函数对鼠标事件进行处理。

事件与信号的关系

事件与信号是有区别的,但是也有关联。Qt为某个界面组件定义的信号通常是对某个事件的封装。例如QPushButton有clicked信号,就可以看做是对QPushButton的QMouseReleaseEvent事件的封装。通过编写与信号关联的槽函数可以实现当信号发射时做的事情。一个信号可以关联多个槽函数,一个槽函数也可以关联多个信号。

通过创建PyQt5.QtCore.pyqtSignal对象可以实现自定义信号。调用自定义信号的emit函数就可以将信号发射出去,触发与信号关联的槽函数。

class QmyLabel(QLabel):
    doubleClicked = pyqtSignal()
    def mouseDoubleClickEvent(self,event):
        self.doubleClicked.emit()

class QmyWidget(Qwidget):
    def __init__(self,parent=None):
        super().__init__(parent)
        self.resize(300,300)
        self.setWindowTitle('自定义信号')
        
        myLab = QmyLabel(self)
        myLab.setText('I am Label')
        font = myLab.font()
        font.setPointSize(14)
        font.setBold(True)
        myLab.setFont(font)
        size = myLab.sizeHint()
        myLab.setGeometry(70,60,size.width(),size.height())
        myLab.doubleClicked.connect(self.do_doubleClicked)

    
    def do_doubleClicked(self):
        print('Label is DoubleClicked!')
    
    def mouseDoubleClickEvent(self,event):
        print('Window is DoubleClicked')

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

QmyLabel自定义了一个doubleClicked信号。当QmyLabel上发生了鼠标双击事件时,会将鼠标双击事件委派给QmyLabel的mouseDobleClickEvent函数处理,处理的方式就是发射自定义信号doubleClicked。

在QmyWidget中,doubleClicked函数的槽函数是QmyWidget的do_doubleClicked函数。该函数一旦检测到doubleClicked信号,槽函数就会执行,在控制台输出Label is DoubleClicked。

事件过滤

通过使用PyQt5的事件过滤器(eventfilter),可以将一个对象上发生的事件委托给另一个对象来检测并处理。 

实现事件过滤功能需要完成以下两项操作:

1. 被监测对象使用installEvenFilter()函数将自己注册给监测对象。

2.监测对象实现eventFilter()函数,对监测对象的事件进行处理。

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


class QmyWidget(QWidget):
    def __init__(self, parent=None) -> None:
        super().__init__(parent)
        self.resize(400,400)
        self.setWindowTitle('事件委托')
        self.laba = QLabel(self)
        self.laba.setText('I am Label A')
        font = self.laba.font()
        font.setPointSize(10)
        font.setBold(True)
        self.laba.setFont(font)
        self.laba.setGeometry(20,20,300,60)
        self.laba.installEventFilter(self)
        self.labb = QLabel(self)
        self.labb.setText('I am Label B')
        font = self.labb.font()
        font.setPointSize(10)
        font.setBold(True)
        self.labb.setFont(font)
        self.labb.setGeometry(20,100,300,60)
        self.labb.installEventFilter(self)
    def eventFilter(self, w, e) -> bool:
        if w == self.laba:
            if e.type()==QEvent.Enter:
                
                self.laba.setText('鼠标来啦')
                self.laba.setStyleSheet('background-color:rgb(170,255,255);')
            if e.type()==QEvent.Leave:
                self.laba.setText('I am Label A')
                self.laba.setStyleSheet('')  
        if w == self.labb:
            if e.type()==QEvent.Enter:
                self.labb.setText('点我!')
                self.labb.setStyleSheet('background-color:rgb(85,255,127);')
            if e.type()==QEvent.MouseButtonPress:
                self.labb.setText('还真点啊!')
                self.labb.setStyleSheet('background-color:rgb(85,255,127);')
            if e.type()==QEvent.MouseButtonRelease:
                self.labb.setText('点我!')
                self.labb.setStyleSheet('background-color:rgb(85,255,127);')
            if e.type()==QEvent.Leave:
                self.labb.setText('I am Label B')
                self.labb.setStyleSheet('')  
        return super().eventFilter(w, e)

app = QApplication(sys.argv)
form = QmyWidget()
form.show()
sys.exit(app.exec_())
        

QmyWidget充当窗口,里面有两个QLabel,laba和labb,它们将事件处理全部委托给了QmyWidget。这样,一旦有发生在laba和labb上的事件,QmyWidget的eventFilter函数就会被触发。当然QmyWidget也并不会处理laba和labb的所有事件,所以会通过条件语句判定事件源和事件类型。最后,也是最重要的,因为并不是对laba和labb的事件都做处理,所以一定要使用QWidget的eventFilter函数做善后处理

以上是关于PyQt5的事件机制的主要内容,如果未能解决你的问题,请参考以下文章

python -- PyQt5(designer)中文详细教程事件和信号

PyQt5快速入门PyQt5信号槽机制

模拟鼠标拖动事件 PyQt5

Pyqt5 鼠标事件不适用于我的自定义标签栏

了解 contextMenuEvent(或任何一般事件)在 PyQt5 中的工作原理

PyQt5对话框窗口打开时没有布局