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

news/2025/2/1 18:50:55 标签: qt, 开发语言, 华为, harmonyos, 华为云, 华为od, git
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.niftyadmin.cn/n/5839523.html

相关文章

学习数据结构(5)单向链表的实现

&#xff08;1&#xff09;头部插入 &#xff08;2&#xff09;尾部删除 &#xff08;3&#xff09;头部删除 &#xff08;4&#xff09;查找 &#xff08;5&#xff09;在指定位置之前插入节点 &#xff08;6&#xff09;在指定位置之后插入节点 &#xff08;7&#xff09;删除…

Linux stat 命令使用详解

简介 stat 命令打印文件和文件系统的详细信息。该工具提供有关所有者是谁、修改日期、访问权限、大小、类型等信息。 该实用程序对于故障排除、在更改文件之前获取有关文件的信息以及例行文件和系统管理任务至关重要。 基本语法 stat [arguments] [filename]常用选项 -L, -…

158页精品PPT | 机械行业数字化生产供应链产品解决方案

《机械行业数字化生产供应链产品解决方案》是一套全面且深入的数字化转型方案&#xff0c;旨在通过先进的信息技术和管理理念&#xff0c;助力机械行业企业实现从传统制造到智能制造的升级。该方案涵盖了从生产管理、研发设计到供应链协同的全流程优化&#xff0c;通过用友U8精…

Day31-【AI思考】-深度学习方法论全解析——科学提升学习效率的终极指南

文章目录 深度学习方法论全解析——科学提升学习效率的终极指南**一、影子跟读法&#xff08;Shadowing&#xff09;——听力突破核武器****二、番茄工作法&#xff08;Pomodoro&#xff09;——时间管理手术刀****三、费曼技巧&#xff08;Feynman Technique&#xff09;——知…

buuuctf_秘密文件

题目&#xff1a; 应该是分析流量包了&#xff0c;用wireshark打开 我追踪http流未果&#xff0c;分析下ftp流 追踪流看看 用户 “ctf” 使用密码 “ctf” 登录。 PORT命令用于为后续操作设置数据连接。 LIST命令用于列出 FTP 服务器上目录的内容&#xff0c;但在此日志中未…

Day49:添加字典元素

在 Python 中&#xff0c;字典是一个可变的数据类型&#xff0c;这意味着你可以随时添加新的键值对。今天我们将学习如何向字典中添加元素。 1. 使用方括号 ([]) 添加新元素 最简单的方法是通过字典的键&#xff0c;使用方括号 [] 来添加新的键值对。如果该键已经存在&#x…

二叉树相关oj题

二叉树相关oj题 检查两颗树是否相同。OJ链接 另一颗树的子树。OJ链接 if(rootnull)易漏掉 会导致空指针异常 翻转二叉树。OJ链接 判断一颗二叉树是否是平衡二叉树。OJ链接 可以在求树高度的过程中判断树是否平衡 对称二叉树。OJ链接 二叉树的构建及遍历。OJ链接 注…

使用Aardio库在Python中创建桌面应用:简单指南

引言 随着软件开发需求的不断增长&#xff0c;开发者们需要更加灵活和高效的工具来快速构建应用程序。Python以其简洁易读的语法和强大的社区支持而闻名&#xff0c;但在创建图形用户界面&#xff08;GUI&#xff09;时&#xff0c;可能会遇到一些挑战。Aardio作为一种轻量级的…