Qt 事件处理机制 (二):事件过滤器

目录

事件过滤的方法

1. 在 QObject 中安装事件过滤器

2. 在 QApplication 对象中安装事件过滤器

3. 子类化 QApplication,并重写notify()

事件过滤的示例

单个事件过滤器

多个事件过滤器


事件过滤的方法

1. 在 QObject 中安装事件过滤器

调用 installEventFilter() 注册监视对象后, 目标对象的所有事件会首先发送给这个监视对象的 eventFilter(), 它可以根据需要检查和丢弃事件。 若同一个对象安装了多个事件过滤器,那么就会按照安装顺序逆序激活。可以使用 QObject::removeEventFilter() 函数删除现有的事件过滤器。

2. 在 QApplication 对象中安装事件过滤器

一旦为 qApp 注册了事件过滤器,那么该应用程序的每个对象的每个事件都会在发送到其他事件过滤器之前,首先发送给这个对象的 eventFilter()。

3. 子类化 QApplication,并重写notify()

Qt 调用该函数来发送一个事件,重写该函数是在事件过滤器得到所有事件之前获得它们的唯一方法。

事件过滤的示例

第一种方法最常用,我们使用上一章的例子( MyWidget )来观察在 QObject 中安装事件过滤器后事件的传递。

Qt 事件处理机制 (二):事件过滤器_第1张图片

单个事件过滤器

为 w0 安装事件过滤器 w2,然后重写 eventFilter() 函数,当鼠标按下事件发生时,打印出过滤器及目标对象的索引,代码如下。结合代码,可以先思考下面两个问题:

  1. 如果在 Widget0 的区域点击,会打印出什么信息?
  2. 如果在 Widget2 的区域点击,会打印出什么信息?
  3. 如果 MyWidget::eventFilter() 函数中,鼠标按下事件的 if 语句中返回 false,会打印出什么信息?
MyWidget::MyWidget(int index, QWidget *parent) :
    QWidget(parent),
    index(index)
{
}

void MyWidget::paintEvent(QPaintEvent *event)
{
    Q_UNUSED(event)

    QRect rect = this->rect();
    rect.adjust(-1, 1, -1, -1);

    QPainter painter(this);
    painter.drawRect(rect);
    painter.drawText(rect.right() - 50, rect.bottom(), QString("Widget%1").arg(index));
}

void MyWidget::mousePressEvent(QMouseEvent *event)
{
    qDebug() << QString("Widget%1 mousePressEvent in...").arg(index);
    event->ignore();
    //QWidget::mousePressEvent(event);
}

bool MyWidget::eventFilter(QObject *obj, QEvent *event)
{
    if (obj->inherits("MyWidget")) {
        MyWidget *widget = qobject_cast(obj);
        if (event->type() == QEvent::MouseButtonPress) {
            qDebug() << QString("QEvent::MouseButtonPress event filter in, this is Widget%1, target object is Widget%2").arg(this->index).arg(widget->index);
            return true;
        } 
    }
    return false;
}


void showWidget()
{
    MyWidget* w0 = new MyWidget(0);
    MyWidget* w1 = new MyWidget(1, w0);
    MyWidget* w2 = new MyWidget(2, w1);

    w0->resize(500, 500);
    w1->resize(400, 400);
    w2->resize(300, 300);

    w0->installEventFilter(w2);

    w0->show();
}

上面三个问题答案:

1. 如果在 Widget0 的区域点击,会触发 Widget0 的鼠标事件, 而该事件会先被 Widget2 处理,在 MyWidget::eventFilter() 中,处理了鼠标按下事件后,返回了 true,则表示该事件已被该对象处理,不会再发送给其他对象处理,打印的信息如下:

"QEvent::MouseButtonPress event filter in, this is Widget2, target object is Widget0"

2. 如果在 Widget2 的区域点击,该鼠标事件会首先发送给拥有焦点的 Widget2,由于 MyWidget::mousePressEvent() 中 ignore 了该事件,该事件会传递到 Widget1, 再到 Widget0,而 Widget0 注册了过滤器 Widget2,所以原本发送给 Widget0 的事件会先发送给 Widget2,打印的信息如下:

"Widget2 mousePressEvent in..." 
"Widget1 mousePressEvent in..." 
"QEvent::MouseButtonPress event filter in, this is Widget2, target object is Widget0" 

3. 如果 MyWidget::eventFilter() 函数中,鼠标按下事件的 if 语句中返回 false,那么表示该事件未被该监控对象处理,会传递给目标对象。

此时在 Widget0 的区域点击,打印信息如下:

"QEvent::MouseButtonPress event filter in, this is Widget2, target object is Widget0" 
"Widget0 mousePressEvent in..." 

多个事件过滤器

为 w0 安装事件过滤器 w2 和 W1,修改代码如下,如果在 Widget0 的区域点击,会打印出什么信息?

void MyWidget::mousePressEvent(QMouseEvent *event)
{
    qDebug() << QString("Widget%1 mousePressEvent in...").arg(index);
    event->ignore();
}

bool MyWidget::eventFilter(QObject *obj, QEvent *event)
{
    if (obj->inherits("MyWidget")) {
        MyWidget *widget = qobject_cast(obj);
        if (event->type() == QEvent::MouseButtonPress) {
            qDebug() << QString("QEvent::MouseButtonPress event filter in, this is Widget%1, target object is Widget%2").arg(this->index).arg(widget->index);
        }
    }
    return false;
}

void showWidget()
{
    MyWidget* w0 = new MyWidget(0);
    MyWidget* w1 = new MyWidget(1, w0);
    MyWidget* w2 = new MyWidget(2, w1);

    w0->resize(500, 500);
    w1->resize(400, 400);
    w2->resize(300, 300);

    w0->installEventFilter(w2);
    w0->installEventFilter(w1);

    w0->show();
}

若同一个对象安装了多个事件过滤器,会按照安装顺序逆序激活,即该事件的传递顺序为:Widget1 -> Widget2 -> Widget0。打印信息如下:

"QEvent::MouseButtonPress event filter in, this is Widget1, target object is Widget0" 
"QEvent::MouseButtonPress event filter in, this is Widget2, target object is Widget0" 
"Widget0 mousePressEvent in..." 

思考一下,如果为 w0 安装事件过滤器 W1, 同时 W1 安装事件过滤器 W2,那么 W0 的事件会先传递给 W1,还是会先传递给 W2 ?

void MyWidget::mousePressEvent(QMouseEvent *event)
{
    qDebug() << QString("Widget%1 mousePressEvent in...").arg(index);
    event->ignore();
}

bool MyWidget::eventFilter(QObject *obj, QEvent *event)
{
    if (obj->inherits("MyWidget")) {
        MyWidget *widget = qobject_cast(obj);
        if (event->type() == QEvent::MouseButtonPress) {
            qDebug() << QString("QEvent::MouseButtonPress event filter in, this is Widget%1, target object is Widget%2").arg(this->index).arg(widget->index);
        }
    }
    return false;
}

void showWidget()
{
    MyWidget* w0 = new MyWidget(0);
    MyWidget* w1 = new MyWidget(1, w0);
    MyWidget* w2 = new MyWidget(2, w1);

    w0->resize(500, 500);
    w1->resize(400, 400);
    w2->resize(300, 300);

    w0->installEventFilter(w1);
    w1->installEventFilter(w2);

    w0->show();
}

W0 的事件会先传递给 W1,和W1 安装的事件过滤器 W2无关,在 W0 的区域点击,打印信息如下:

"QEvent::MouseButtonPress event filter in, this is Widget1, target object is Widget0" 
"Widget0 mousePressEvent in..." 

你可能感兴趣的:(C++,Qt,qt,c++,事件处理机制)