事件的传递流程

弄清事件的传递流程,其实就是为了弄清如何砍断事件的传递,比如你希望某个事件只传给窗口里的控件,而不传给窗口等,这样在一个由多个widget和控件多层嵌套的界面中做出符合想要的事件处理动作,下面看有哪几个时机可以砍断事件的传递

事件处理函数中调用accep()还是ignore()

首先,在重写控件的事件函数时。我们可以在事件函数中通过调用QEvent的accep()方法或者ignore()方法

  • accept():表示接受这个事件,该事件到了这个控件被处理后,就不再传递给他的父控件,其父控件就不会收到这个事件
  • ignore():表示忽略这个事件,该事件到达这个控件被处理后,依然可以传给他的父控件,他的父控件可以在收到这个事件后继续对该事件做出处理

举例:

先自定义1个标签,重写mousePressEvent函数,鼠标按下后弹出对话框,在重写这个函数时,我们调用accept函数,来中断这个事件的传递,这样其父控件就不会收到这个事件了

同样,该控件所在的父控件/窗口也重写mousePressEvent函数,鼠标按下后弹出对话框,来作区分

自定义标签:

#ifndef MYLABEL_H
#define MYLABEL_H
#include<QLabel>
#include<QMouseEvent>
#include<QMessageBox>


class MyLabel : public QLabel
{
    Q_OBJECT

public:
    using QLabel::QLabel;
protected:
    //重写鼠标按下事件
    void mousePressEvent(QMouseEvent* ev)override
    {
        QMessageBox::information(this,"","来自MyLabel!");

        //显示调用accept方法,阻止该事件的传递
        //其父窗口/控件就不会收到该事件了
        ev->accept();
        //也可以什么都不调用,默认就是accept
    }

};

#endif // MYLABEL_H

重写其父窗口的鼠标按下事件:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QVBoxLayout>
#include"MyLabel.h"

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr):QWidget(parent)
    {
        this->resize(500,500);
        QVBoxLayout* layout=new QVBoxLayout(this);
        layout->setSpacing(0);
        layout->setContentsMargins(0,0,0,0);


        //把MyLabel放进来
        MyLabel* label=new MyLabel(this);
        label->setFixedHeight(100);
        label->setStyleSheet(".MyLabel{background-color:red}");

        layout->addWidget(label);


    }

    ~Widget(){}
protected:
    //重写父窗口/控件的鼠标按下事件,来做对比
    void mousePressEvent(QMouseEvent* ev)override
    {
        QMessageBox::information(this,"","来自父窗口!");
    }

};
#endif // WIDGET_H

把这个父窗口/控件显示出来,然后点击这个标签,只弹出了1个提示框,可以看到事件传递到MyLabel后就不再往外层传递了

接下来我们再在MyLabel的事件处理函数中调用ignore,让事件接着传递到父窗口/控件的事件处理函数,那么再次点击就会看到2个对话框:

#ifndef MYLABEL_H
#define MYLABEL_H
#include<QLabel>
#include<QMouseEvent>
#include<QMessageBox>


class MyLabel : public QLabel
{
    Q_OBJECT

public:
    using QLabel::QLabel;
protected:
    //重写鼠标按下事件
    void mousePressEvent(QMouseEvent* ev)override
    {
        QMessageBox::information(this,"","来自MyLabel!");

        //显示调用accept方法,阻止该事件的传递
        //其父窗口/控件就不会收到该事件了
        //ev->accept();
        //也可以什么都不调用,默认就是accept

        ev->ignore();
    }

};

#endif // MYLABEL_H

效果如下,可以看到事件接着传递给了他的父窗口/控件,弹出了对话框:

事件分发函数event

然后,在控件的事件分发函数event()中,我们同样可以进行事件的截获

查看Qt源码qwidget.cpp,可以看到even()函数的默认实现,就是根据不同的事件类型,调用默认的事件处理函数:我们重写控件的事件分发函数event(),

在event函数中提前捕获鼠标按下事件,最后再调用父类的event函数,我们重写的鼠标按下事件也会被触发

#ifndef MYLABEL_H
#define MYLABEL_H
#include<QLabel>
#include<QMouseEvent>
#include<QMessageBox>


class MyLabel : public QLabel
{
    Q_OBJECT

public:
    using QLabel::QLabel;
protected:
    //重写鼠标按下事件
    void mousePressEvent(QMouseEvent* ev)override
    {
        QMessageBox::information(this,"","来自MyLabel!");

        //显示调用accept方法,阻止该事件的传递
        //其父窗口/控件就不会收到该事件了
        //ev->accept();
        //也可以什么都不调用,默认就是accept

        ev->ignore();
    }

    bool event(QEvent* ev) override
    {
        if(ev->type()==QEvent::MouseButtonPress)
        {
            //在事件分发函数中我们提前截获鼠标按下事件,然后进行处理
            QMessageBox::information(this,"","来自MyLabel的Event函数!");
        }

        //调用父类的默认event函数,事件继续分发下去,前面重写的鼠标按下事件函数也会被调用
        return QLabel::event(ev);
    }
};

#endif // MYLABEL_H

最后运行效果就是:先进入MyLabel的事件分发函数,再进入MyLabel的鼠标按下事件函数,最后进入父窗口/控件的鼠标按下事件函数:

但是,我们可以在重写event函数时,提前返回true或false,来截断事件的传递

  • 提前返回true:事件到了MyLabel的event函数就不会往下传递了,MyLabel的鼠标按下事件函数也不会被调用,其父窗口/空间的鼠标按下事件函数也不会被调用
#ifndef MYLABEL_H
#define MYLABEL_H
#include<QLabel>
#include<QMouseEvent>
#include<QMessageBox>


class MyLabel : public QLabel
{
    Q_OBJECT

public:
    using QLabel::QLabel;
protected:
    //重写鼠标按下事件
    void mousePressEvent(QMouseEvent* ev)override
    {
        QMessageBox::information(this,"","来自MyLabel!");

        //显示调用accept方法,阻止该事件的传递
        //其父窗口/控件就不会收到该事件了
        //ev->accept();
        //也可以什么都不调用,默认就是accept

        ev->ignore();
    }

    bool event(QEvent* ev) override
    {
        if(ev->type()==QEvent::MouseButtonPress)
        {
            //在事件分发函数中我们提前截获鼠标按下事件,然后进行处理
            QMessageBox::information(this,"","来自MyLabel的Event函数!");
            
            //提前返回true,事件到此为止
            return true;
        }

        //调用父类的默认even函数,事件继续分发下去,前面重写的鼠标按下事件函数也会被调用
        return QLabel::event(ev);
    }
};

#endif // MYLABEL_H

  • 提前返回false,表示事件没有被识别,事件会接着传递到父窗口/控件。但是由于提前return了false,没有调用父类的事件分发函数event,因此事件不会分发到mousePressEvent函数。事件就只到达了MyLabel的event函数和MyLabel的父窗口/控件鼠标按下事件处理函数
#ifndef MYLABEL_H
#define MYLABEL_H
#include<QLabel>
#include<QMouseEvent>
#include<QMessageBox>


class MyLabel : public QLabel
{
    Q_OBJECT

public:
    using QLabel::QLabel;
protected:
    //重写鼠标按下事件
    void mousePressEvent(QMouseEvent* ev)override
    {
        QMessageBox::information(this,"","来自MyLabel!");

        //显示调用accept方法,阻止该事件的传递
        //其父窗口/控件就不会收到该事件了
        //ev->accept();
        //也可以什么都不调用,默认就是accept

        ev->ignore();
    }

    bool event(QEvent* ev) override
    {
        if(ev->type()==QEvent::MouseButtonPress)
        {
            //在事件分发函数中我们提前截获鼠标按下事件,然后进行处理
            QMessageBox::information(this,"","来自MyLabel的Event函数!");

            //提前返回true,事件到此为止
            //return true;

            //提前返回false,表示事件没有识别,会接着传递到父窗口/控件
            //但没有调用后面的QLabel::event(ev),事件不会到达重写的mousePressEvent函数了
            return false;
        }

        //调用父类的默认even函数,事件继续分发下去,前面重写的鼠标按下事件函数也会被调用
        return QLabel::event(ev);
    }
};

#endif // MYLABEL_H

事件过滤函数eventFilter

前面的事件分发函数event()和事件处理函数比如mousePressEvent()函数都是事件已经到达了控件,我们还可以在事件到达控件之前,提前拦截这个事件。

就是在父窗口/控件中给子控件安装事件过滤器

然后重写父窗口/控件eventFilter函数

  • 在这个函数中我们返回true,那么事件到父窗口/控件eventFilter函数中截获处理之后就到此为止了,不会再往下传递了
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QVBoxLayout>
#include"MyLabel.h"

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr):QWidget(parent)
    {
        this->resize(500,500);
        QVBoxLayout* layout=new QVBoxLayout(this);
        layout->setSpacing(0);
        layout->setContentsMargins(0,0,0,0);


        //把MyLabel放进来
        MyLabel* label=new MyLabel(this);
        label->setFixedHeight(100);
        label->setStyleSheet(".MyLabel{background-color:red}");

        layout->addWidget(label);

        label->setObjectName("mylabel");
        //给MyLabel安装事件过滤器
        label->installEventFilter(this);
    }

    ~Widget(){}
protected:
    //重写父窗口/控件的鼠标按下事件,来做对比
    void mousePressEvent(QMouseEvent* ev)override
    {
        QMessageBox::information(this,"","来自父窗口!");
    }

    //重写事件过滤器函数
    bool eventFilter(QObject* watched,QEvent* ev) override
    {
        if(watched==this->findChild<MyLabel*>("mylabel")&&
            ev->type()==QEvent::MouseButtonPress)
        {
            QMessageBox::information(this,"","来自父窗口的eventFiletr!");

            //返回true,这个事件就到此为止了,不会往下传递给MyLabel和父窗口/控件了
            return true;
        }



        return QObject::eventFilter(watched,ev);
    }
};
#endif // WIDGET_H

  • 如果我们返回false,那么就表示事件接着往下传递
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include<QVBoxLayout>
#include"MyLabel.h"

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr):QWidget(parent)
    {
        this->resize(500,500);
        QVBoxLayout* layout=new QVBoxLayout(this);
        layout->setSpacing(0);
        layout->setContentsMargins(0,0,0,0);


        //把MyLabel放进来
        MyLabel* label=new MyLabel(this);
        label->setFixedHeight(100);
        label->setStyleSheet(".MyLabel{background-color:red}");

        layout->addWidget(label);

        label->setObjectName("mylabel");
        //给MyLabel安装事件过滤器
        label->installEventFilter(this);
    }

    ~Widget(){}
protected:
    //重写父窗口/控件的鼠标按下事件,来做对比
    void mousePressEvent(QMouseEvent* ev)override
    {
        QMessageBox::information(this,"","来自父窗口!");
    }

    //重写事件过滤器函数
    bool eventFilter(QObject* watched,QEvent* ev) override
    {
        if(watched==this->findChild<MyLabel*>("mylabel")&&
            ev->type()==QEvent::MouseButtonPress)
        {
            QMessageBox::information(this,"","来自父窗口的eventFiletr!");

            //返回true,这个事件就到此为止了,不会往下传递给MyLabel和父窗口/控件了
            //return true;

            //事件就会接着往下传递
            return false;
        }



        return QObject::eventFilter(watched,ev);
    }
};
#endif // WIDGET_H

总结事件传递流程

以鼠标按下事件为例:

1.鼠标按下事件产生,然后Notify()

2.进入到父窗口/控件的eventFilter函数,在该函数中可以提前捕获鼠标按下事件做处理(相应的子控件要安装事件过滤器),处理完后返回true,那么该事件到此为止返回false那么事件会继续往下传递

3.进入到子控件的事件分发函数event(),在该函数中同样可以捕获到鼠标按下事件做相应处理,处理完后返回true,那么该事件到此为止返回false,那么事件就会继续到达父窗口/控件的鼠标按下事件函数返回父类的默认的事件分发函数event(ev),那么事件就会到达子控件的鼠标按下事件处理函数

4.进入子控件的鼠标按下事件函数,如果调用accept()方法,那么事件到此为止调用ignore()方法,那么事件会接着往下传递到父窗口/控件的鼠标按下事件函数

  • 12
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值