当前位置:   article > 正文

Qt 事件过滤器eventFilter(QObject* obj, QEvent* e) (**笔记**)

eventfilter

Qt 事件过滤器eventFilter(QObject* obj, QEvent* e) (**)

installEventFilter、eventFilter函数理解

Qt 事件过滤器(秒懂)

QT的Event Filter[翻译]:QT中eventFilter的使用方法

eventFilter(QObject* obj, QEvent* e) 实例说明  (**笔记**)

-------------------------------------------------

============================

Qt 事件过滤器eventFilter(QObject* obj, QEvent* e) (**)

1、事件过滤器、的用途

用于:拦截 传递到 目标对象的 事件,这样可以实现 监视目标对象事件 的作用。

2、实现事件过滤器的步骤:

2.1、Qt 调用

void QObject::installEventFilter (QObject* filterObj)

把 filterObj 对象设置安装 ( 或注册 ) 为事件过滤器, filterObj 也称为过滤器对象。事件过滤器通常在构造函数中进行注册

2.2、在上一步注册的 filterObj 对象,通过调用

bool QObject:: eventFilter (QObject* obj, QEvent* e);

接收拦截到的事件。也就是说:拦截到的事件filterObj 对象中的 eventFilter 函数中处理。 eventFilter 的第一个参数 obj 指向的是事件本应传递到的目标对象

2.3、使用

QObject::removeEventFilter(QObject *obj)

函数可以删除事件过滤器。

3、 事件过滤器处理事件的规则

    ①、过滤器对象的 eventFilter()函数可以接受拒绝拦截到的事件,

若该函数返回 false, 则表示事件需要作进一步处理,此时事件会被发送到目标对象本身进行处理(注意: 这里并未向父对象进行传递),

若 evetnFilter()返回 true则表示停止处理该事件,此时 目标对象后面安装的事件过滤器 就无法获得该事件。

    ②、若同一对象安装了多个事件过滤器,则最后安装的过滤器首先被激活

4、理解事件过滤器

观察者模式:其原理为,设有一目标对象 W,它有多个观察该对象的对象 G1 , G2 ,G3, 当 W发生变化时,W的观察者会依情形改变自身。

应用于 Qt 事件过滤器,则是,首先使 用 W的成员函数 installEventFilter 函数把 G1 , G2 , G3 设置为 W 的观察者

所有本应传递 给 W的事件 E,则先传递给观察者 G1 , G2 , G3 ,然后观察者调用其成员函数 eventFilter 对传递进来的事件进行处理,

若 eventFilter 返回 true 表示事件处理完毕,返回 false 则返 回给被观察者 W 进行处理

见下图。

————————————————
版权声明:本文为CSDN博主「cc_rong」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_41653875/article/details/120856266

installEventFilter、eventFilter函数理解

installEventFilter函数如下:

void QObject::installEventFilter(QObject *filterObj)

Qt助手的解释如下:

在对象上安装一个事件过滤器filterObj。如下:

monitoredObj->installEventFilter(filterObj);

其中monitoredObj、filterObj都是QObject的子类。上面代码意思是:在monitoredObj对象上安装一个事件过滤器filterObj。该函数一般和如下函数配合使用:

[virtual] bool QObject::eventFilter(QObject *watched, QEvent *event)

注意:该函数是虚函数,也就是说派生自QObject的子类可以重写该函数。

     上面monitoredObj对象安装一个filterObj过滤器后,则可以在filterObj对象所在类的eventFilter函数中、拦截发送到 monitoredObj对象的事件。如下为KeyPressEater类:

     
      class KeyPressEater : public QObject
      {
          Q_OBJECT
          ...
     
      protected:
          bool eventFilter(QObject *obj, QEvent *event) override;
      };
     
      bool KeyPressEater::eventFilter(QObject *obj, QEvent *event)
      {
          if (event->type() == QEvent::KeyPress) {
              QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
              qDebug("Ate key press %d", keyEvent->key());
              return true;
          } else {
              // standard event processing
              return QObject::eventFilter(obj, event);
          }
      }

现在我们在按钮或QListView两个窗体部件上安装过滤器,如下:

     
      KeyPressEater *keyPressEater = new KeyPressEater(this);
      QPushButton *pushButton = new QPushButton(this);
      QListView *listView = new QListView(this);
     
      pushButton->installEventFilter(keyPressEater);
      listView->installEventFilter(keyPressEater);

此时如果在按钮或QListView获取到键盘焦点时按下Esc键,按键事件被 KeyPressEater类的eventFilter函数拦截,从而弹出:

Ate key press 1048576

其中1048576为Esc键的虚拟键码。Qt的QShortcut类就是采取这种技术实现的。


注意:

  •     事件过滤器对象filterObj能阻止(拦截)或放行发向对象monitoredObj的事件。
  •     事件过滤器对象filterObj通过其类的eventFilter函数来接收事件。
  •     如果过滤器对象filterObj所在类的eventFilter函数返回 true,则该事件被拦截,也就是原本发向monitoredObj对象的事件不再发向monitoredObj对象;如果过滤器对象filterObj所在类的eventFilter函数返回false,则不拦截该事件,事件依然发向monitoredObj对象。

  •     如果同一个对象上安装多个过滤器,则最后一个过滤器首先被激活调用。
  •     如果在eventFilter函数中删除了接收事件的对象,请确保eventFilter函数返回true,否则Qt将会发送事件到删除的接收对象上,这将导致程序崩溃。
  •     monitoredObj对象和filterObj对象必须位于同一个线程中。如果filterObj对象在不同的线程,调用installEventFilter函数则Qt什么都不会做。

  •     如果调用installEventFilter函数后,monitoredObj对象和filterObj对象被移到不同的线程中去了,则eventFilter函数不会被调用,直到monitoredObj对象和filterObj对象回到同一线程时才会被调用。
  •     过滤器对象filterObj所在类的eventFilter函数的第一个参数是被拦截对象monitoredObj,即installEventFilter函数的调用者。以上面的 KeyPressEater例子来说, KeyPressEater类的eventFilter函数的第一个参数是pushButton或listView

————————————————
版权声明:本文为CSDN博主「荆楚闲人」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/danshiming/article/details/122792144

Qt 事件过滤器(秒懂)

1.事件过滤器

void QObject::installEventFilter(QObject *filterObj)

bool eventFilter(QObject *obj, QEvent *event);

Qt的事件过滤由以上两个方法实现,首先安装一个事件过滤器,然后重写bool eventFilter(QObject *obj, QEvent *event)。

filterObj表示事件筛选器对象,它接收发送到此QObject对象的所有事件。筛选器可以停止事件,也可以将事件转发给此QObject对象。事件过滤器filterObj通过它的eventFilter()函数接收事件。

eventFilter()有返回值。

    如果返回true,表示事件过滤,不会发送到对象本身。
    如果返回false,表示事件未过滤,会通过event()方法将事件分发到对象。
    返回给基类进行处理,例:return QObject::eventFilter(obj, event)。

2.示例

一个label,当鼠标进入的时候变成红色,鼠标离开的时候变为黑色

    #include "widget.h"
    #include "ui_widget.h"
     
    QString redStyle = "QLabel#label{color:#FF0000}";
     
    QString blackStyle = "QLabel#label{color:#000000}";
     
    Widget::Widget(QWidget *parent) :
        QWidget(parent),
        ui(new Ui::Widget)
    {
        ui->setupUi(this);
     
        ui->label->installEventFilter(this);
    }
     
    Widget::~Widget()
    {
        delete ui;
    }
     
    bool Widget::eventFilter(QObject *obj, QEvent *event)
    {
        if(obj == ui->label)
        {
            //鼠标进入的时候
            if (event->type() == QEvent::Enter)
            {
                ui->label->setText("我是红色");
                ui->label->setStyleSheet(redStyle);
                return true; //
            }
            else if(event->type() == QEvent::Leave) //鼠标离开
            {
                ui->label->setText("我是黑色");
                ui->label->setStyleSheet(blackStyle);
                return true;
            }
     
            return false;  //别的事件会传给label对象
        }
     
        // standard event processing
        return QWidget::eventFilter(obj, event);
    }

上述代码,假如我们不使用事件过滤器,我们就无法实现上述鼠标进入、离开功能,只能自己继承QLabel,重写鼠标进入、离开事件。

3.简单分析

自定义一个label类继承QLabel,查看事件的处理顺序,以及过滤器是否起作用。

    #ifndef WLABEL_H
    #define WLABEL_H
     
    #include <QLabel>
    #include <QEvent>
     
    class WLabel : public QLabel
    {
    public:
        WLabel(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
     
    protected:
        virtual bool event(QEvent *e);
     
        virtual void enterEvent(QEvent *event);
     
        virtual void leaveEvent(QEvent *event);
     
    };
     
    #endif // WLABEL_H
     
     
     
    #include "wlabel.h"
    #include <QDebug>
     
    WLabel::WLabel(QWidget *parent, Qt::WindowFlags f)
        : QLabel(parent,f)
    {
     
    }
     
    bool WLabel::event(QEvent *e)
    {
        if(e->type() == QEvent::Enter)
        {
            qDebug()<<"WLabel event :enter";
        }
        else if(e->type() == QEvent::Leave)
        {
            qDebug()<<"WLabel event :Leave";
        }
     
        return QLabel::event(e);
    }
     
    void WLabel::enterEvent(QEvent *event)
    {
        qDebug()<<"WLabel enterEvent";
    }
     
    void WLabel::leaveEvent(QEvent *event)
    {
        qDebug()<<"WLabel leaveEvent";
    }

自己定义了一个WLabel类,重写了event事件,鼠标进入事件enterEvent,鼠标离开事件leaveEvent。

然后将之前界面上的QLabel提升为我们现在定义的类。

如果在bool Widget::eventFilter(QObject *obj, QEvent *event)方法中,将我们鼠标进入离开事件返回false后,事件会发送到Label本身,如下图所示。

结论:事件的派发顺序是先进入eventFilter中,看是否过滤掉此事件,然后进入到bool WLabel::event(QEvent *e)事件中,由event去分发事件,最后进入到enterEvent(QEvent *event)或者void leaveEvent(QEvent *event);


————————————————
版权声明:本文为CSDN博主「Mr.codeee」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wzz953200463/article/details/124258028

QT的Event Filter[翻译]:QT中eventFilter的使用方法

这篇文章一扫我心头的疑云,对QT中的事件加深了理解,希望这篇文章也能帮到你。

一直对Event Filter似懂非懂, 通过看C++ GUI Programming with Qt4, Second Edition, 争取搞明白. 顺便自己把英文翻译成中文, 算是自己做的笔记了.

Installing Event Filters

QT事件模块一个真正强大的特性是可以设置一个QObject的实例去监测另外一个QObject实例的事件,在被监测的实例see之前.

假设我们有一个CustomerInfoDialog这样的widget, 它由几个QLineEdit组成.我们想用Spacer键来转变focus到下一个QLineEdit.这个非标准的行为可能对一个内部的程序很合适, 需要培训它的用户来使用它. 一个直接的方法是子类QLineEdit,然后重新实现keyPressEvent()来调用focusNextChild(), 像这样:

void MyLineEdit::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Space)
    {
        focusNextChild();
    }
    else
    {
        QLineEdit::keyPressEvent(event);
    }
}

这个方法有一个主要的弊端: 如果我们在这个form中用到几个不同类型的widget(比如QComboBox和QSpinBox), 我们必须也子类化它们来表现出相同的行为.

一个更好的方案是让CustomerInfoDialog来监控它的子widget的按键事件,

在监测的代码里执行需要的行为. 这可以用event Filter来达到. 设置一个event filter有两个步骤:

1. 在目标对象上调用installEventFilter(),将监测对象注册到目标对象上.
2. 在监测对象的eventFilter()方法里处理目标对象的事件.

在ctor里注册监测对象是一个好地方:

CustomerInfoDialog::CustomerInfoDialog(QWidget *parent) :QDialog(parent)
{
    ...
    firstNameEdit->installEventFilter(this);
    lastNameEdit->installEventFilter(this);
    cityEdit->installEventFilter(this);
    phoneNumberEdit->installEventFilter(this);
}

一旦event Filter注册了, 发送到firstNameEdit, lastNameEdit, cityEdit和phoneNumberEdit的事件在被发送到原来的目的地之前, 会先发到CustomerInfoDialog的eventFilter()函数.

这是接收这些事件的eventFilter()函数:

bool CustomerInfoDialog::eventFilter(QObject *target, QEvent *event)
{
    if (target == firstnameEdit || target == lastNameEdit
            || target == cityEdit || target == phoneNumberEdit)
    {
        if(event->type() == QEvent::KeyPress)
        {
            QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
            if (keyEvent->key() == Qt::Key_Space)
            {
                focusNextChild();
                return true;
            }
        }
    }
    return QDialog::eventFilter(target, event);
}

首先,我们检查是否目标widget是一个QLineEdit. 如果是个key Press事件, 把它转换为QKeyEvent, 并检查哪个键值被按下.

如果是space, 我们调用focusNextChild()把focus传到focus链上的下一个widget上, 返回true告诉Qt我们已经处理了这个事件. 如果我们返回false, Qt会发送这个event到它原来的目的地,导致一个假的空格被插入到QLineEdit.

如果目标widget不是QLineEdit, 或者这个event不是一个space按键, 我们把控制权传回到基类的eventFilter去. 目标wdiget可以是基类-QDialog正在监测的某个widget.

Qt提供了5个级别来处理和过滤事件.

1. 我们可以重新实现特定的event handler.
重新实现像mousePressEvent(), keyPressEvent()和paintEvent()这样的event Handler是目前处理event最普通的方式.

2. 我们可以重新实现QObject::event().
通过重新实现event(),我们可以在事件到达特定的event handler之前对它们作出处理. 这个方法主要是用来覆写Tab键的缺省实现. 也可以用来处理不同发生的事件类型,对它们,就没有特定的event handler. 当重新实现event()的时候,我们必须调用基类的event()来处理我们不显式处理的情况.

3. 我们可以安装一个event filter到一个单独的QObject.
一旦一个对象用installEventFilter注册了, 发到目标对象的所有事件都会先发到监测对象的eventFilter(). 如果同一object安装了多个event filter, filter会依次被激活, 从最近安装的回到第一个.

4. 我们可以在QApplication对象上安装event filter.
一旦一个event filter被注册到qApp(唯一的QApplication对象), 程序里发到每个对象的每个事件在发到其他event filter之前,都要首先发到eventFilter(). 这个方法对debugging非常有用. 也可以用来处理发到disable的widget上的事件, QApplication通常会丢弃它们.

5. 我们可以子类QApplication并重新实现notify().
Qt调用QApplication::notify()来发出事件. 在任何event filter得到之前, 重新实现这个函数是得到所有事件的唯一方法. event filter通常更有用, 因为可以有任意数目且同时存在的event filter, 但是只有一个notify()函数.

许多事件类型,包括鼠标和按键事件, 可以被传播. 如果一个事件没有在传到目标对象的过程中被处理,或者被目标对象本身处理, 整个事件处理过程会重复, 不过, 这次, 目标对象的parent作为新的目标对象. 从parent到parent,这样继续下去,知道事件被处理了,或者到达了顶层的对象.

最后附上原文链接:QT的Event Filter[翻译] - smoozer - 博客园

eventFilter(QObject* obj, QEvent* e) 实例说明  (**笔记**)

源码:

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    
    ui->pushButton->installEventFilter(this);
    ui->pushButton_2->installEventFilter(this);

    
}

bool MainWindow::eventFilter(QObject *obj, QEvent *event)
{
    if(obj == ui->pushButton)
    {
        qDebug()<<"AA-111 : if(obj == ui->pushButton)"; //只要窗口激活,就会(总是会)有规定次数的输出
        
        if (event->type() == QEvent::MouseButtonRelease)
        {
            qDebug()<<"AA-112 : if(obj == ui->pushButton)"; //MouseButtonRelease有效,才会有输出
            
            ui->pushButton->setText("我是红色");
            return true;
        }
        else if(event->type() == QEvent::MouseButtonPress)
        {
            qDebug()<<"AA-113 : if(obj == ui->pushButton)";
            
            ui->pushButton->setText("我是黑色");
            return true;
        }
        
        return false;//别的事件会传给 pushButton对象
    }
    else if(obj == ui->pushButton_2)
    {
        qDebug()<<"BB-211 : if(obj == ui->pushButton)";  //只要窗口激活,就会(总是会)有规定次数的输出
    }
    else if(obj == ui->pushButton_3)  // 无效
    {
        qDebug()<<"CC-311 : if(obj == ui->pushButton)";  //没有输出,因为没有安装installEventFilter(this);
    }
    
    // standard event processing
    return QWidget::eventFilter(obj, event);
}

说明:

1. 只要窗口被激活,就会(总是会)有规定次数的输出。如下:

只有达到 Qt规定的、默认的、认为光标不再移动变化的次数时,才会停止输出。

如果没有输出,就意味着不能进入第一层的 if(),也就无法判断 pushButton 的动作,比如:离开,进入,按下,释放等等。

05:31:58: Starting /home/abc/build-untitled2-Desktop_Qt_5_14_2_GCC_64bit-Debug/untitled2 ...
AA-111 : if(obj == ui->pushButton)
BB-211 : if(obj == ui->pushButton)
AA-111 : if(obj == ui->pushButton)
AA-111 : if(obj == ui->pushButton)
AA-111 : if(obj == ui->pushButton)
AA-111 : if(obj == ui->pushButton)
BB-211 : if(obj == ui->pushButton)
BB-211 : if(obj == ui->pushButton)
BB-211 : if(obj == ui->pushButton)
BB-211 : if(obj == ui->pushButton)
AA-111 : if(obj == ui->pushButton)
BB-211 : if(obj == ui->pushButton)
AA-111 : if(obj == ui->pushButton)
BB-211 : if(obj == ui->pushButton)

2. 第二层的 if(),符合判断条件,才会有输出。

   if (event->type() == QEvent::MouseButtonRelease)
        {
            qDebug()<<"AA-112 : if(obj == ui->pushButton)"; //MouseButtonRelease有效,才会有输出

   else if(obj == ui->pushButton_2)
    {
        qDebug()<<"BB-211 : if(obj == ui->pushButton)";  //只要窗口激活,就会(总是会)有规定次数的输出
    }

3. 第二层的 if(),符合判断条件,才会有输出。

   qDebug()<<"CC-311 : if(obj == ui->pushButton)";  //没有输出,因为没有安装installEventFilter(this);

4. 小结

ui->pushButton->installEventFilter(this);

if(obj == ui->pushButton_3)

return QWidget::eventFilter(obj, event);

4.1. 在 eventFilter(QObject *obj, QEvent *event)中能够起作用的语句块,必须符合:

a. installEventFilter(this);中所安装的 fileObject.

b. 以及最后的、类似于:return QWidget::eventFilter(obj, event); // standard event processing 的这条语句。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/208331
推荐阅读
相关标签
  

闽ICP备14008679号