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

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

任意对象都可以提前拦截其他任意对象的事件,拦截到的事件都会在本对象的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(event);//前面已经确认过事件类型为鼠标类型,所以这里可以放心的进行静态转换
            qDebug()<<"Filter mainwindow MouseButtonPress";
            if(e->button() == Qt::LeftButton)//鼠标左键
                return true; //true=拦截
            else
                return false; //false=继续传播

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

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

 ------QT的事件分发、事件过滤器_第1张图片

可以看到:

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

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

 

 

 

 

你可能感兴趣的:(QT)