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

Qt事件处理机制2-事件函数的传播

所有继承自QObject的类都有event函数,该函数用来处理自身的事件,函数定义如下:

virtual bool QObject::event(QEvent *e)

Qt帮助文档:
This virtual function receives events to an object and should return true if the event e recognized and processed. The event() function can be reimplemented to customize the behavior of an object. Make sure you call the parent event class implementation for all the events you did not handle.

大致意思:在event函数中,当事件被识别并处理后,如果返回true,表示该event函数执行结束,不再希望执行具体的事件处理函数,例如mousePressEventpaintEvent等;如果返回false,也表示该event函数执行结束,不再希望执行具体的事件处理函数,但事件会向上传递,即传递给父组件,进入父组件的event函数。也可以event函数中调用父类的event函数,交给父类来处理,父类仍旧遵循以上规则。
:当event函数中返回true之前,若已经设置了e->ignore(),仍旧会调用父组件的event函数;只有当event函数返回true,并且e->isAccepted()也为true的时候,才不再调用父组件的event函数。QEvent *e的默认值是true,除非在eventFilter函数中设置为false

下面关于文档里说的返回值,进行演示说明

MyButton.h

#ifndef MYBUTTON_H
#define MYBUTTON_H#include <QPushButton>class MyButton : public QPushButton
{Q_OBJECT
public:MyButton(QWidget *parent = nullptr);~MyButton();protected:bool event(QEvent *e) override;
};#endif // MYBUTTON_H

MyButton.cpp

#include "MyButton.h"
#include <QDebug>
#include <QEvent>MyButton::MyButton(QWidget *parent) : QPushButton(parent){}MyButton::~MyButton(){}bool MyButton::event(QEvent *e)
{if (e->type() == QEvent::MouseButtonPress){qDebug() << __FUNCTION__ << event->isAccepted();return true;}return QPushButton::event(e);
}

MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);~MainWindow();protected:bool event(QEvent *e) override;private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H

MainWindow.cpp

#include "MainWindow.h"
#include "ui_MainWindow.h"
#include <QDebug>
#include <QEvent>
#include <QMouseEvent>MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);
}MainWindow::~MainWindow()
{delete ui;
}bool MainWindow::event(QEvent *e)
{if (e->type() == QEvent::MouseButtonPress){qDebug() << __FUNCTION__ << e->isAccepted();}return QMainWindow::event(e);
}

鼠标点击MyButton类按钮,运行结果: 事件向父类传播

// 情形1:直接return false
bool MyButton::event(QEvent *e)
{if (e->type() == QEvent::MouseButtonPress){qDebug() << __FUNCTION__ << event->isAccepted();return false;}return QPushButton::event(e);
}// 情形2:e->ignore()后return true
bool MyButton::event(QEvent *e)
{if (e->type() == QEvent::MouseButtonPress){qDebug() << __FUNCTION__ << event->isAccepted();e->ignore();return true;}return QPushButton::event(e);
}// 运行结果都如下:
MyButton::event true
MainWindow::event true

鼠标点击MyButton类按钮,运行结果: 事件停止向父类传播

// 情形3:直接return true
bool MyButton::event(QEvent *e)
{if (e->type() == QEvent::MouseButtonPress){qDebug() << __FUNCTION__ << event->isAccepted();return true;}return QPushButton::event(e);
}// 情形3:返回父类event函数的处理结果
bool MyButton::event(QEvent *e)
{if (e->type() == QEvent::MouseButtonPress){qDebug() << __FUNCTION__ << event->isAccepted();}return QPushButton::event(e);
}// 运行结果都如下:
MyButton::event true

补充1:QT源码中事件处理过程中调用函数如下:

QApplication::exec()QCoreApplication::exec()QEventLoop::exec(ProcessEventsFlags )QEventLoop::processEvents(ProcessEventsFlags )QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags)QT_WIN_CALLBACK QtWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)bool QETWidget::translateMouseEvent(const MSG &msg)bool QApplicationPrivate::sendMouseEvent(...)inline bool QCoreApplication::sendSpontaneousEvent(QObject *receiver, QEvent *event)bool QCoreApplication::notifyInternal(QObject *receiver, QEvent *event)bool QApplication::notify(QObject *receiver, QEvent *e)bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)bool QWidget::event(QEvent *event)

补充2:notify函数中处理事件传播
notify函数中处理事件传播的时候,会调用notify_helper函数,当(res && eventAccepted)为真时停止向父类传播,否则w = w->parentWidget(),进而调用父类的notify_helper函数,而notify_helper函数中会调用receiver->event(e)函数。

bool QApplication::notify(QObject *receiver, QEvent *e)
{QWidget* w = static_cast<QWidget *>(receiver);QMouseEvent* mouse = static_cast<QMouseEvent*>(e);QPoint relpos = mouse->pos();...switch (e->type()) {...case QEvent::MouseButtonPress:case QEvent::MouseButtonRelease:case QEvent::MouseButtonDblClick:case QEvent::MouseMove:{...bool eventAccepted = mouse->isAccepted();QPointer<QWidget> pw = w;while (w) {QMouseEvent me(mouse->type(), relpos, mouse->windowPos(), mouse->globalPos(),mouse->button(), mouse->buttons(), mouse->modifiers(), mouse->source());me.spont = mouse->spontaneous();me.setTimestamp(mouse->timestamp());QGuiApplicationPrivate::setMouseEventFlags(&me, mouse->flags());// throw away any mouse-tracking-only mouse eventsif (!w->hasMouseTracking()&& mouse->type() == QEvent::MouseMove && mouse->buttons() == 0) {// but still send them through all application event filters (normally done by notify_helper)d->sendThroughApplicationEventFilters(w, w == receiver ? mouse : &me);res = true;} else {w->setAttribute(Qt::WA_NoMouseReplay, false);res = d->notify_helper(w, w == receiver ? mouse : &me);e->spont = false;}eventAccepted = (w == receiver ? mouse : &me)->isAccepted();if (res && eventAccepted)break;if (w->isWindow() || w->testAttribute(Qt::WA_NoMousePropagation))break;relpos += w->pos();w = w->parentWidget();}...}...
}
bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
{// These tracepoints (and the whole function, actually) are very similar// to the ones in QCoreApplicationPrivate::notify_helper; the reason for their// duplication is because tracepoint symbols are not exported by QtCore.// If you adjust the tracepoints here, consider adjusting QCoreApplicationPrivate too.Q_TRACE(QApplication_notify_entry, receiver, e, e->type());bool consumed = false;bool filtered = false;Q_TRACE_EXIT(QApplication_notify_exit, consumed, filtered);// send to all application event filtersif (threadRequiresCoreApplication()&& receiver->d_func()->threadData->thread == mainThread()&& sendThroughApplicationEventFilters(receiver, e)) {filtered = true;return filtered;}if (receiver->isWidgetType()) {QWidget *widget = static_cast<QWidget *>(receiver);#if !defined(QT_NO_CURSOR)// toggle HasMouse widget state on enter and leaveif ((e->type() == QEvent::Enter || e->type() == QEvent::DragEnter) &&(!QApplication::activePopupWidget() || QApplication::activePopupWidget() == widget->window()))widget->setAttribute(Qt::WA_UnderMouse, true);else if (e->type() == QEvent::Leave || e->type() == QEvent::DragLeave)widget->setAttribute(Qt::WA_UnderMouse, false);
#endifif (QLayout *layout=widget->d_func()->layout) {layout->widgetEvent(e);}}// send to all receiver event filtersif (sendThroughObjectEventFilters(receiver, e)) {filtered = true;return filtered;}// deliver the eventconsumed = receiver->event(e);QCoreApplicationPrivate::setEventSpontaneous(e, false);return consumed;
}

注: event函数中调用e->ignore()之后,父对象的event函数中e->isAccepted()值仍然为true的原因是每一次调用notify_helper函数时,传递的事件e都是临时的。

QMouseEvent* mouse = static_cast<QMouseEvent*>(e);
...
QMouseEvent me(mouse->type(), relpos, mouse->windowPos(), mouse->globalPos(), mouse->button(), mouse->buttons(), mouse->modifiers(), mouse->source());
...
res = d->notify_helper(w, w == receiver ? mouse : &me);

注: 补充1的内容引自QT QEvent 事件调用的来龙去脉,作者:QtC++ 开发从业者

http://www.lryc.cn/news/340880.html

相关文章:

  • 【PDF.js】PDF文件预览
  • 从建表语句带你学习doris_表索引
  • Linux CentOS 安装 MySQL 服务教程
  • MSSQL 命令行操作说明 sql server 2022 命令行下进行配置管理
  • 【系统分析师】系统安全分析与设计
  • ActiveMQ 07 集群配置
  • Redis(哨兵模式)
  • 一种基于镜像指示位办法的RingBuffer实现,解决Mirror和2的幂个数限制
  • 【Java开发指南 | 第十一篇】Java运算符
  • 【IC前端虚拟项目】验证环境方案思路和文档组织
  • 程序设计|C语言教学——C语言基础1:C语言的引入和入门
  • 初学python记录:力扣928. 尽量减少恶意软件的传播 II
  • LlamaIndex 组件 - Storing
  • 在Linux系统中设定延迟任务
  • JVM之方法区的详细解析
  • Go 使用ObjectID
  • 基于SpringBoot+Vue的疾病防控系统设计与实现(源码+文档+包运行)
  • 2024年阿里云4核8G配置云服务器价格低性能高!
  • 关于ContentProvider这一遍就够了
  • 《1w实盘and大盘基金预测 day23》
  • 向量数据库与图数据库:理解它们的区别
  • WIN7用上最新版Chrome
  • node.jd版本降级/升级
  • python+playwright 学习-88 禁止加载图片等资源
  • Linux:Redis7.2.4的简单在线部署(1)
  • HackMyVM-Connection
  • Prometheus接入AlterManager配置邮件告警(基于K8S环境部署)
  • find方法
  • TLS v1.3 导致JetBrains IDE jdk.internal.net.http.common CPU占用高
  • 计算机网络 2.2数据传输方式