您当前的位置: 首页 >  Python

不脱发的程序猿

暂无认证

  • 2浏览

    0关注

    492博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Python Qt GUI设计:5种事件处理机制(提升篇—3)

不脱发的程序猿 发布时间:2021-11-22 21:12:06 ,浏览量:2

目录

1、常见事件类型

2、事件处理方法

2.1、重新实现事件函数

2.2、重新实现QObject.event()

2.3、安装事件过滤器

2.4、在QApplication中安装事件过滤器

2.5、重新实现QApplication的notify()方法

之前在Python Qt GUI设计:QTimer计时器类、QThread多线程类和事件处理类(基础篇—8)中,我们已经简单讲到,PyQt为事件处理提供了两种机制:高级的信号与槽机制以及低级的事件处理程序,本篇博文将系统讲解Qt的事件处理机类和制。

事件处理机制本身很复杂,是PyQt底层的知识点,当采用信号与槽机制处理不了时,才会考虑使用事件处理机制。

信号与槽可以说是对事件处理机制的高级封装,如果说事件是用来创建窗口控件的,那么信号与槽就是用来对这个窗口控件进行使用的。比如一个按钮,当我们使用这个按钮时,只关心clicked信号,至于这个按钮如何接收并处理鼠标点击事件,然后再发射这信号,则不用关心。但是如果要重载一个按钮,这时就要关心这个问题了。比如可以改变它的行为:在鼠标按键按下时触发clicked信号,而不是在释放时。

1、常见事件类型

Qt事件的类型有很多,常见的Qt事件如下所示:

  • 键盘事件:按键按下和松开。
  • 鼠标事件:鼠标指针移动、鼠标按键按下和松开。
  • 拖放事件:用鼠标进行拖放。
  • 滚轮事件:鼠标滚轮滚动。
  • 绘屏事件:重绘屏幕的某些部分。
  • 定时事件:定时器到时。
  • 焦点事件:键盘焦点移动。
  • 进入和离开事件:鼠标指针移入Widget内,或者移出。
  • 移动事件::Widget的位置改变。
  • 大小改变事件:Widget的大小改变。
  • 显示和隐藏事件:Widget显示和隐藏。
  • 窗口事件:窗口是否为当前窗口。

还有一些常见的Qt事件,比如Socket事件、剪贴板事件、字体改变事件、布局改变事件等。

具体事件类型和说明可参见说明文档:

2、事件处理方法

PyQt提供了如下5种事件处理和过滤方法(由弱到强),其中只有前两种方法使用最频繁。

2.1、重新实现事件函数

比如mousePressEvent()、keyPressEvent()、paintEvent()。这是最常规的事件处理方法。

通过示例了解重新实现事件函数的使用方法,效果如下所示:

这个示例中包含了多种事件类型,所以比较复杂。

首先是类的建立,建立text和message两个变量,使用paintEvent函数把它们输出到窗口中。

update函数的作用是更新窗口,由于在窗口更新过程中会触发一次 paintEvent函数(paintEvent是窗口基类QWidget的内部函数),因此在本例中update函数的作用等同于paintEvent函数。

然后是重新实现窗口关闭事件与上下文菜单事件,对于上下文菜单事件,主要影响message变量的结果,paintEvent负责把这个变量在窗口底部输出。

绘制事件是代码的核心事件,它的主要作用是时刻跟踪text与message这两个变量的信息,并把 text的内容绘制到窗口的中部,把message的内容绘制到窗口的底部(保持5秒后就会被清空)。

以及最后一些鼠标、键盘的点击操作等。

实现代码如下所示:

import sys
from PyQt5.QtCore import (QEvent, QTimer, Qt)
from PyQt5.QtWidgets import (QApplication, QMenu, QWidget)
from PyQt5.QtGui import QPainter


class Widget(QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        self.justDoubleClicked = False
        self.key = ""
        self.text = ""
        self.message = ""
        self.resize(400, 300)
        self.move(100, 100)
        self.setWindowTitle("Events")
        QTimer.singleShot(0, self.giveHelp)  # 避免窗口大小重绘事件的影响,可以把参数0改变成3000(3秒),然后在运行,就可以明白这行代码的意思。

    def giveHelp(self):
        self.text = "请点击这里触发追踪鼠标功能"
        self.update() # 重绘事件,也就是触发paintEvent函数。

    '''重新实现关闭事件'''
    def closeEvent(self, event):
        print("Closed")

    '''重新实现上下文菜单事件'''
    def contextMenuEvent(self, event):
        menu = QMenu(self)
        oneAction = menu.addAction("&One")
        twoAction = menu.addAction("&Two")
        oneAction.triggered.connect(self.one)
        twoAction.triggered.connect(self.two)
        if not self.message:
            menu.addSeparator()
            threeAction = menu.addAction("Thre&e")
            threeAction.triggered.connect(self.three)
        menu.exec_(event.globalPos())

    '''上下文菜单槽函数'''
    def one(self):
        self.message = "Menu option One"
        self.update()

    def two(self):
        self.message = "Menu option Two"
        self.update()

    def three(self):
        self.message = "Menu option Three"
        self.update()

    '''重新实现绘制事件'''
    def paintEvent(self, event):
        text = self.text
        i = text.find("\n\n")
        if i >= 0:
            text = text[0:i]
        if self.key: # 若触发了键盘按钮,则在文本信息中记录这个按钮信息。
            text += "\n\n你按下了: {0}".format(self.key)
        painter = QPainter(self)
        painter.setRenderHint(QPainter.TextAntialiasing)
        painter.drawText(self.rect(), Qt.AlignCenter, text) # 绘制信息文本的内容
        if self.message: # 若消息文本存在则在底部居中绘制消息,5秒钟后清空消息文本并重绘。
            painter.drawText(self.rect(), Qt.AlignBottom | Qt.AlignHCenter,
                             self.message)
            QTimer.singleShot(5000, self.clearMessage)
            QTimer.singleShot(5000, self.update)

    '''清空消息文本的槽函数'''
    def clearMessage(self):
        self.message = ""

    '''重新实现调整窗口大小事件'''
    def resizeEvent(self, event):
        self.text = "调整窗口大小为: QSize({0}, {1})".format(
            event.size().width(), event.size().height())
        self.update()

    '''重新实现鼠标释放事件'''
    def mouseReleaseEvent(self, event):
        # 若鼠标释放为双击释放,则不跟踪鼠标移动
        # 若鼠标释放为单击释放,则需要改变跟踪功能的状态,如果开启跟踪功能的话就跟踪,不开启跟踪功能就不跟踪
        if self.justDoubleClicked:
            self.justDoubleClicked = False
        else:
            self.setMouseTracking(not self.hasMouseTracking()) # 单击鼠标
            if self.hasMouseTracking():
                self.text = "开启鼠标跟踪功能.\n" + \
                            "请移动一下鼠标!\n" + \
                            "单击鼠标可以关闭这个功能"
            else:
                self.text = "关闭鼠标跟踪功能.\n" + \
                            "单击鼠标可以开启这个功能"
            self.update()

    '''重新实现鼠标移动事件'''
    def mouseMoveEvent(self, event):
        if not self.justDoubleClicked:
            globalPos = self.mapToGlobal(event.pos()) # 窗口坐标转换为屏幕坐标
            self.text = """鼠标位置:
            窗口坐标为:QPoint({0}, {1}) 
            屏幕坐标为:QPoint({2}, {3}) """.format(event.pos().x(), event.pos().y(), globalPos.x(), globalPos.y())
            self.update()

    '''重新实现鼠标双击事件'''
    def mouseDoubleClickEvent(self, event):
        self.justDoubleClicked = True
        self.text = "你双击了鼠标"
        self.update()

    '''重新实现键盘按下事件'''
    def keyPressEvent(self, event):
        self.key = ""
        if event.key() == Qt.Key_Home:
            self.key = "Home"
        elif event.key() == Qt.Key_End:
            self.key = "End"
        elif event.key() == Qt.Key_PageUp:
            if event.modifiers() & Qt.ControlModifier:
                self.key = "Ctrl+PageUp"
            else:
                self.key = "PageUp"
        elif event.key() == Qt.Key_PageDown:
            if event.modifiers() & Qt.ControlModifier:
                self.key = "Ctrl+PageDown"
            else:
                self.key = "PageDown"
        elif Qt.Key_A             
关注
打赏
1664101891
查看更多评论
0.0414s