自学内容网 自学内容网

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_OBJECT

public:
    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 events
            if (!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 filters
    if (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 leave
        if ((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);
#endif

        if (QLayout *layout=widget->d_func()->layout) {
            layout->widgetEvent(e);
        }
    }

    // send to all receiver event filters
    if (sendThroughObjectEventFilters(receiver, e)) {
        filtered = true;
        return filtered;
    }

    // deliver the event
    consumed = 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++ 开发从业者


原文地址:https://blog.csdn.net/qq_40945965/article/details/137477133

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!