当前位置: 首页 > article >正文

Qt事件处理:理解处理器、过滤器与事件系统

1. 事件

事件 是一个描述应用程序中、发生的某些事情的对象。

在 Qt 中,所有事件都继承自 QEvent ,并且每个事件都有特定的标识符,如:Qt::MouseButtonPress 代表鼠标按下事件。

每个事件对象包含该事件的所有相关信息,如:鼠标事件包含鼠标的坐标、按下的按钮等信息。

2. 事件处理器

事件处理器 是用于处理特定类型事件的成员函数,通常以 event 结尾,如:mousePressEvent 、enterEvent 等。

事件处理器定义了当某个事件发生时,应执行的操作。

事件处理器可以分为两类:

  • 预定义事件处理器:由 Qt 提供的标准事件处理函数,可以通过重写它们以实现自定义行为。
class SessionFriendItem : public QWidget
{
    Q_OBJECT
public:
    SessionFriendItem(QWidget* owner, const QIcon& avatar, const QString& name, const QString& text)
        : owner(owner), selected(false)
    {
        // 1. 设置基本属性和样式
    	this->setFixedHeight(64);
        this->setStyleSheet("QWidget { background-color: rgb(236, 233, 231); }");
        
        // 2. 创建网格布局
        QGridLayout* layout = new QGridLayout();
        layout->setContentMargins(0, 0, 0, 0);
        layout->setVerticalSpacing(0); // 设置竖直方向间距
        layout->setHorizontalSpacing(0); // 设置水平方向间距
            
        this->setLayout(layout);
            
        // 3. 创建头像
        QPushButton avatar_btn = new QPushButton();
        avatar_btn->setFixedSize(QSize(46, 46));
        avatar_btn->setIconSize(QSize(46, 46));
        avatar_btn->setIcon(avatar);
        avatar_btn->setStyleSheet("QPushButton { border: none; background-color: transparent; }");
        layout->addWidget(avatar_btn, 0, 0, 2, 2);
            
        // 4. 创建昵称
        QLabel* name_label = new QLabel();
        name_label->setText(name);
        name_label->setFixedHeight(30);
        name_label->setStyleSheet("QLabel { font-size: 14px; }");
        layout->addWidget(name_label, 0, 2, 1, 6);
            
        // 5. 添加预览消息
        QLabel* msg_label = new QLabel();
        msg_label->setText(text);
        msg_label->setFixedHeight(25);
        msg_label->setStyleSheet("QLabel { font-size: 12px; }");
        layout->addWidget(msg_label, 1, 2, 1, 6);
    }
    
    void mousePressEvent(QMouseEvent* event) override
    {
        // 1. 恢复兄弟组件的 rgb
        QObjectList* children = this->parent()->children();
        for (QObject* child : children)
        {
            if (child->isWidgetType() == false) continue;
            
            SessionFriendItem* temp = dynamic_cast<SessionFriendItem*>(child);
            if (temp->selected == true)
            {
                temp->selected = false;
                temp->setStyleSheet("QWidget { background-color: rgb(236, 233, 231); }");
            }
        }
        
        // 2. 设置当前组件的 rgb
        this->selected = true;
        this->setStyleSheet("QWidget { background-color: rgb(200, 199, 198); }");
    }
    
    void enterEvent(QEnterEvent* event) override
    {
        // 当前组件被点击,则不处理
        if (selected == true) return; 
        this->setStyleSheet("QWidget { background-color: rgb(222, 220, 218); }");
    }
    
    void leaveEvent(QEvent* event) override
    {
        if (selected == true) return;
        this->setStyleSheet("QWidget { background-color: rgb(236, 233, 231); }");
    }
private:
    QWidget* owner;
    bool selected;
};

  • 自定义事件处理器:处理一些特殊类型的事件,可以通过事件过滤器或子类化 QObject 来实现。
3. 事件过滤器

在 Qt 中,事件过滤器(eventFilter)提供了一种机制,允许一个对象 监视并处理 另一个对象的事件

3.1 基本概念

事件过滤器允许拦截发送给某个对象的所有事件,并在这些事件被该对象处理之前,决定如何处理它们。

步骤:

  • 安装事件过滤器:通过调用 installEventFilter() 方法,将一个对象设置为另一个对象的事件过滤器。
  • 重写 eventFilter() 函数:在作为事件过滤器的对象中,重写 bool eventFilter(QObject* obj, QEvent* event) 函数,来定义具体的事件处理逻辑。
3.2 工作原理

QMainWindow 不能直接设置布局,需要通过中央部件来管理布局。

this->setCentralWidget()

// mainwindow.h
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    bool eventFilter(QObject* object, QEvent* event) override;

    QWidget* mainWidget;
    QPushButton* button;

private:
    Ui::MainWindow *ui;
};

// mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    mainWidget = new QWidget(this);
    this->setCentralWidget(mainWidget);

    QVBoxLayout* layout = new QVBoxLayout();
    mainWidget->setLayout(layout);

    button = new QPushButton();
    button->setText("点击");
    button->setFixedSize(QSize(50, 50));
    layout->addWidget(button);
	
    // 安装事件过滤器
    button->installEventFilter(this);
}

bool MainWindow::eventFilter(QObject* object, QEvent* event)
{
    if (object == button)
    {
        if (event->type() == QEvent::MouseButtonPress)
        {
            qDebug() << "Button_Press";
        }
        else if (event->type() == QEvent::Enter)
        {
            qDebug() << "Button_Enter";
        }
        else if (event->type() == QEvent::Leave)
        {
            qDebug() << "Button_Leave";
        }
        // return false; // 允许事件继续传递
        return true;
    }
    // 交给其它事件处理器处理
    return QMainWindow::eventFilter(object, event);
}

鼠标进入按钮上方,鼠标点击按钮,鼠标离开按钮

installEventFilter 的作用目标
  • 事件过滤器的本质:当一个对象 A 调用 installEventFilter(B) ,意味着对象 B 会优先接收到对象 A 的事件,并可以通过 eventFilter() 方法拦截或处理这些事件。

  • 如果希望在事件滤波器 B 处理完事件后,仍然让目标对象 A 继续处理该事件,可以在事件滤波器逻辑的末尾 return false。这样,事件会继续按照正常的事件处理流程传递下去。

  • 自定义类 能够作为事件过滤器、并被目标对象设置,需要确保该类重载了 eventfilter() 方法;原生的 Qt 对象(如 QWidget)无法直接担任事件过滤器的角色,因为它们没有提供对 eventfilter() 的重载接口。

4. 事件系统

Qt 的事件系统基于事件驱动模型工作,其核心是通过事件队列(Event Queue)实现异步调用。

以下是其基本运作流程:

4.1 事件产生

当用户与应用程序进行交互,或系统内部发生某些变化,就会产生相应的事件。

所有事件均封装为 QEvent 的子类对象。

例如,用户按下鼠标左键,会产生一个 QMouseEvent 对象,该对象包含了关于鼠标点击的所有信息,包括点击的位置、按钮的状态等;

键盘按键的按下或释放会产生 QKeyEvent 对象。

4.2 事件传递
  • 对于同步事件
  1. 使用 QCoreApplication::sendEvent() 方法时,事件会被立即发送给接收者。

  2. 发送事件的线程会等待,直到事件被完全处理。

  • 对于异步事件
  1. 使用 QCoreApplication::postEvent() 方法时,生成的事件并非被立即处理,而是被放入目标对象所属线程的事件队列中,等待调度。

  2. QCoreApplication::exec() 启动的主事件循环会从队列中取出事件进行处理(非阻塞),并通过 QCoreApplication::notify() 方法来分发该事件到目标对象(事件通知),确保每个事件都能被正确地发生给它的接收者。

  3. 在事件被传递给目标对象之前,如果有安装事件过滤器,首先会调用这些过滤器的 eventFilter() 方法;如果该事件没有被过滤器拦截,则继续传递给目标对象。

4.3 事件处理

通过重写 event() 方法,或特定的事件处理器(如 mousePressEvent() ),实现业务逻辑。

如果事件未被处理(如未重写 event() 或未调用基类实现),某些对象会向父对象传递(冒泡机制),直至顶层对象。

事件是否传递取决于事件类型和 accept() / ignore() 的标记。

QMouseEvent 默认 accept() ,QKeyEvent() 默认 ignore() ,QPaintEvent 仅在目标对象处理、不会传递。

void MainWindow::mousePressEvent(QMouseEvent* event)
{
    qDebug() << "button is pressed";
    event->accept(); // 标记事件已处理
    // event->ignore(); // 允许事件继续传递
}
4.4 事件结束

事件对象会在处理完成后,由 Qt 自动销毁;除非事件被通过 deleteLater() 标记为 deferred delete


http://www.kler.cn/a/527511.html

相关文章:

  • 1.27刷题记录
  • 使用 cmake
  • 10.3 LangChain实战指南:解锁大模型应用的10大核心场景与架构设计
  • Java中的注解与反射:深入理解getAnnotation(Class<T> annotationClass)方法
  • 《LLM大语言模型+RAG实战+Langchain+ChatGLM-4+Transformer》
  • 爬虫基础(一)HTTP协议 :请求与响应
  • 【机器学习】Google开源大模型Gemma2:原理、微调训练及推理部署实战
  • R 字符串:深入理解与高效应用
  • 推荐一款好用的翻译类浏览器扩展插件
  • 11.QT控件:输入类控件
  • 实验八 JSP访问数据库
  • 【llm对话系统】大模型 Llama 源码分析之并行训练方案
  • 各种CNN 卷积特征图可视化理解方法(链接)
  • 网站标签页图标如何添加
  • SpringBoot 数据访问(MyBatis)
  • Java实战:图像浏览器
  • (三)QT——信号与槽机制——计数器程序
  • 51单片机CLD1602显示万年历+闹钟+农历+整点报时
  • 【B站保姆级视频教程:Jetson配置YOLOv11环境(五)Miniconda安装与配置】
  • 大厂面试题备份20250130
  • 【deepseek-r1】ollama如何更改安装位置以及自定义模型下载位置
  • Flink Forward Asia 2024 - 总结和展望(附PPT下载链接)
  • 游戏引擎 Unity - Unity 下载与安装
  • Ae 表达式语言引用:Marker Key
  • 深入解析 Linux 内核中的页面错误处理机制
  • 智慧园区如何利用智能化手段提升居民幸福感与环境可持续性