概述

在 Qt 中,事件(Event) 是对象之间进行内部通信的底层机制,用于响应各种“发生的事”,例如窗口显示、鼠标点击、键盘输入、定时器超时等。理解事件系统是开发复杂交互式应用程序的基础。

什么是事件?

  • 事件是 QEvent 或其子类的实例,封装了“发生的事情”的相关信息(如鼠标坐标、按键代码等)。

  • 任何 QObject 子类都能接收和处理事件。

  • 事件通常由操作系统产生(比如鼠标、键盘),也可以由应用程序自己生成(比如定时器事件、自定义事件)。

信号槽可以看作对特定事件的二次封装。例如 QPushButton 的 clicked()

流程

每一个Qt应用程序都对应一个唯一的QApp1ication应用程序对象,调用这个对象的exec()函数之后,Qt框架内部的事件检测就开始了(程序将进入事件循环来监听应用程序的事件)

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

Qt 的事件处理流程确实遵循事件派发 → 事件过滤 → 事件分发 → 事件处理 这几个主要阶段


[操作系统/应用程序产生事件]
          ↓
1️⃣ 事件派发(QApplication::notify)
   - 全局入口,找到目标 QObject
          ↓
2️⃣ 事件过滤(事件过滤器)
   - 目标对象上安装的所有 eventFilter 依次执行
   - 任一过滤器返回 true,则事件被“截断”,不再继续
          ↓
3️⃣ 事件分发(QObject::event())
   - 根据事件类型调用对应的具体事件处理函数
   - 例如 QKeyEvent → keyPressEvent()
          ↓
4️⃣ 事件处理(具体 *Event 函数)
   - 最终的业务处理,可重写实现自定义行为

1.事件派发

1当事件产生之后,Qt使用用应用程序对象调用notify()函数将事件发送到指定的窗口

[override virtual] bool QApplication::notify(Qobject *receiver, QEvent *e);

2.事件过滤

对应机制:installEventFilter() + eventFilter() 重写,需要先给窗口安装过滤器,该事件才会触发,在目标对象的处理函数(event() 函数)之前执行,只要某个过滤器返回 true,事件就被过滤/拦截,不再传递给后续过滤器以及目标对象本身,默认为不对任何事情进行过滤

[virtual] bool QApplication::eventFilter(QObject *watched, QEvent *e);

3.事件分发

当事件发送到指定窗口之后,窗口的事件分发器会对收到的事件进行分类,
事件分发器会将分类之后的事件,分发给对应的事件处理器函数进行处理。

它会根据事件的类型(例如 QEvent::KeyPress),调用对应的具体处理函数(如 keyPressEvent())。

[override virtual protected] bool Qwidget::event(QEvent *event);

4.事件处理

相应的处理函数,如 keyPressEvent(QKeyEvent*)、mousePressEvent(QMouseEvent*) 等。是虚函数,可以重写,用于实现控件自己的行为逻辑。

[virtual protected] void QWidget::keyPressEvent(QKeyEvent *event);
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);

用户操作

用户如果想要某个事件,产生某个效果,一般对事件过滤事件处理进行操作

1.事件处理示例

思路


这里创建一个类,继承于QTextEdit,名为mytextdit,是一个自定义控件,这里将ui的textEdit控件提升为mytextdit,
在这里插入图片描述

这里,通过调用基类构造函数传入 parent,使当前控件成为父 widget 的子对象,从而纳入 UI 体系,具体在.cpp文件,同时.cpp文件重写事件处理函数,实现用户在触发这个事件而想要实现的功能(其中注释也是思路补充)。


mytextdit::mytextdit(QWidget *parent):QTextEdit(parent){

}

mytextdit.hh

#ifndef MYTEXTDIT_H
#define MYTEXTDIT_H

#include <QTextEdit>

class mytextdit : public QTextEdit
{
public:
    mytextdit(QWidget* parent);
    int ctr_key_pressed=0;//定义一个变量,用途可见.cpp注释

protected:
    void wheelEvent(QWheelEvent *e) override;
    void keyPressEvent(QKeyEvent *e) override;
    void keyReleaseEvent(QKeyEvent *e) override;
};

#endif // MYTEXTDIT_H

#include "mytextdit.h"

#include <QWheelEvent>
#include<QDebug>
#include<QWidget>

mytextdit::mytextdit(QWidget *parent):QTextEdit(parent)//通过调用基类构造函数传入 parent,使当前控件成为父 widget 的子对象,从而纳入 UI 体系。
{

}

void mytextdit::wheelEvent(QWheelEvent *e)
{
    qDebug()<<e->angleDelta().y();
    if(ctr_key_pressed==1)//定义一个变量,判断按键是否按下,从实现crtl+鼠标滚轮,放大/缩小字体的功能
    {
        if(e->angleDelta().y()>0)//滚轮向前滚
        {
            zoomIn();//QTextEdit的函数,作用:放大字体
        }else if(e->angleDelta().y()<0)
        {
            zoomOut();//减小字体
        }
        e->accept();//这里表示,该事件已经被当前控件处理完毕,否则会向上层父控件继续传播。
    }
    else
    QTextEdit::wheelEvent(e);//针对事件没有被处理的情况,调用基类QTextEdit对这个事件的处理
}

void mytextdit::keyPressEvent(QKeyEvent *e)
{
    if(e->key()==Qt::Key_Control)//ctrl键被按下
    {
        ctr_key_pressed=1;
    }
    QTextEdit::keyPressEvent(e);
}

void mytextdit::keyReleaseEvent(QKeyEvent *e)
{
    if(e->key()==Qt::Key_Control)//ctrl键松开了
    {
        ctr_key_pressed=0;
    }
    QTextEdit::keyReleaseEvent(e);
}

2.事件过滤示例

1.文档参考

在这里插入图片描述在这里插入图片描述

class MainWindow : public QMainWindow
 {
 public:
     MainWindow();

     bool eventFilter(QObject *obj, QEvent *ev) override;

 private:
     QTextEdit *textEdit;
 };

 MainWindow::MainWindow()
 {
     textEdit = new QTextEdit;
     setCentralWidget(textEdit);

     textEdit->installEventFilter(this);
 }

 bool MainWindow::eventFilter(QObject *obj, QEvent *event)
 {
     if (obj == textEdit) {
         if (event->type() == QEvent::KeyPress) {
             QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
             qDebug() << "Ate key press" << keyEvent->key();
             return true;
         } else {
             return false;
         }
     } else {
         // pass the event on to the parent class
         return QMainWindow::eventFilter(obj, event);
     }
 }
2.示例

关键点installEventFilter() + eventFilter() 重写

在这里插入图片描述

 bool eventFilter(QObject *obj, QEvent *ev) override;

在这里插入图片描述

ui->textEdit->installEventFilter(this);//为控件,installEventFilter()

在这里插入图片描述

bool Widget::eventFilter(QObject *obj, QEvent *ev)
{
    if (ev->type() == QEvent::Wheel) {
        if (QGuiApplication::keyboardModifiers() == Qt::ControlModifier) { //事件过滤可以判断滚轮和按键事件,但是没办法联系起来
                                                                           // 所以用了另外一种方式判断按键是否按下
            QWheelEvent *wheelEvent = static_cast<QWheelEvent*>(ev);//类型转换
            if (wheelEvent->angleDelta().y() > 0) {
                increase_font_size();   // 放大字体
            } else if (wheelEvent->angleDelta().y() < 0) {
                decrease_font_size();   // 缩小字体
            }
            return true;   // 事件已处理,阻止继续传递
        }
        else
        return false;
    }
       return QObject::eventFilter(obj, ev);
}

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐