QT的事件分发、事件过滤器

一、事件的流向

QT的各种控件(QObject的子类)都有事件处理成员函数,例如:

bool QObject::event(QEvent *e);//所有事件
dragEnterEvent(QDragEnterEvent *);//拖拽进入事件
focusInEvent(QFocusEvent *);//获得焦点事件
mousePressEvent(QMouseEvent *);//鼠标压下事件
····//还有几十个各种类型的事件,不一一列举了,任何一个控件的帮助文件里都可以查到

这些事件需要继承父类重写覆盖之后才能使用,这里主要想说明一点,事件的分发方向,是从子控件一步步向上传递到祖宗控件的,如果子控件拦截了事件,那么父控件就接收不到事件了。子控件怎么拦截事件,怎么不拦截事件,可以先看这个例子:

添加新类QPushButtonEx,它的父类为QPushButton,QPushButtonEx 覆盖 QPushButton 的鼠标按下事件:

void QButtonEx::mousePressEvent(QMouseEvent *e)
{
    qDebug()<<"button pressed";
    e->ignore();//ignore的作用是使事件继续向父控件传递
}

在一个MainWindow上添加一个QPushButton(提升为QPushButtonEx),也重写MainWindow的鼠标按下事件:

void MainWindow::mousePressEvent(QMouseEvent *e)
{
    qDebug()<<"mainwindow pressed";
    e->ignore();
}

点击按钮,运行结果如下:

这也就验证了事件的传递方向,子控件先接收到事件,父控件后接收到事件。尤其注意代码中的e->ignore();这句的作用是使得事件能够继续流向父控件;与之相反的是e->accept();它将事件拦截,父控件将无法收到已经被accept过的事件;重写的事件处理函数中,如果不写accept或ignore,那么默认为:事件被accept(拦截)!

上面的代码为例,如果QButtonEx::mousePressEvent()函数中不写e->ignore(),或者写e->accept(),那么

MainWindow::mousePressEvent()函数将不会被触发,运行结果中只能看到打印:button pressed

二、事件过滤器

考虑一下上面的这种事件流向机制,有何不足?先来看这几种应用场景:

1、某事件发生了,我想在父控件先处理,处理完再让子控件处理(和前面所述的流向相反)

2、某些控件已经重写了一些事件处理函数,我想临时让这些事件处理函数失效,待会再恢复

3、Qt 创建了QEvent事件对象之后,会调用QObject的event()函数处理事件的分发。显然,我们可以在event()函数中实现拦截的操作。由于event()函数是 protected 的,因此,需要继承已有类。如果组件很多,就需要重写很多个event()函数。这当然相当麻烦,更不用说重写event()函数还得小心一堆问题。https://blog.csdn.net/gusgao/article/details/48917427

4、绘制了一个按钮,按钮上又覆盖上一层label,那么这个label会遮住button,使得这个按钮无法被点击到。

这些问题都可以用事件过滤器来解决,事件过滤器的作用用一句话来说就是:

任意对象都可以提前拦截其他任意对象的事件,拦截到的事件都会在本对象的eventFilter函数中被接收到,程序员可以决定拦截并处理后是否继续放行。

举例:一个按钮被点击,本应是按钮先收到点击事件,通过事件过滤器,可以让窗体先收到这个事件,窗体再决定是否把这个事件继续传播给按钮。从事件过滤器的功能来看,不妨叫做:事件监视器、事件拦截器。

假设有两个对象,分别叫做“老师A”和“学生a”,实现事件过滤器需要做两个工作:

1、学生a给老师A授权,允许老师查看自己的所有事件;

2、老师A重写eventFilter函数,对拦截到的学生的事件进行处理;

第1个工作就是一句代码,例如:

ui->pushButton->installEventFilter(this);//pushButton设置mainwindow作为自己的事件监视器(事件过滤器)

第2个工作就是重写监视对象A的成员函数:

bool MainWindow::eventFilter(QObject *watched, QEvent *event)//pushButton的所有事件都要先流到这里,这里放行后pushButton才能收到事件
{
    if(watched == ui->pushButton)//确认被监视的对象
    {
        if(event->type() == QEvent::MouseButtonPress)//确认事件的类型
        {
            QMouseEvent *e = static_cast<QMouseEvent *>(event);//前面已经确认过事件类型为鼠标类型,所以这里可以放心的进行静态转换
            qDebug()<<"Filter mainwindow MouseButtonPress";
            if(e->button() == Qt::LeftButton)//鼠标左键
                return true; //true=拦截
            else
                return false; //false=继续传播

        }
    }
    return false; //false=继续传播
}

分别单击鼠标左、右键,运行的结果分别为:

 ------

可以看到:

1、按钮并没有收到左键被按下事件,因为这一事件被监视对象给拦截后,没有放行;

2、按钮收到了右键按下事件,这一事件依次被以下函数执行:监视对象的eventFilter函数-->按钮的mousePressEvent()函数-->窗口的mousePressEvent()函数

 

 

 

 

  • 9
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值