Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to make an eventfilter?



  • I need to use events in various controls (line edits, comboboxes, etc.), in addition to the events (slots) already in the menu.

    I've read the eventfilters documentation below:

    https://doc.qt.io/qt-5/eventsandfilters.html

    I tried to do as explained, but I guess I didn't know where to put each of the instructions (and maybe there were other errors of mine).

    I would like to know exactly how to do it.

    @mrjj , can you help me?


  • Lifetime Qt Champion

    Hi
    Luckly its not super complicated.

    Lets try to catch the focusIn/out

    we need to add a event filter function to some object that will handle it for us.
    Often mainwindow is used as the widget we monitor is living there.
    Place a QLineEdit on the form.

    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    private:
        Ui::MainWindow *ui;
        bool eventFilter(QObject *object, QEvent *event); <<< add this
    };
    

    then in cpp.

    in the Mainwindow constructor, we add.
    ui->lineEdit->installEventFilter(this);

    now we need the body of the filter function

    
    bool MainWindow::eventFilter(QObject *object, QEvent *event)
    {
    
        if ( object == ui->lineEdit &&  ( event->type() == QEvent::HoverEnter )  ) {
            ui->lineEdit->setText("IN");
        }
    
    
        if ( object == ui->lineEdit &&  (event->type() == QEvent::HoverLeave )  ) {
            ui->lineEdit->setText("out");
        }
    
        // false means it should be send to target also. as in , we dont remove it.
        // if you return true , you will take the event and widget never sees it so be carefull with that.
        return false; 
    }
    

    and if we then run it
    alt text

    This is a very powerful way of altering how the std. widgets works but if you use for all cases where subclassing ( like we did in other thread) would be more self-contained, you will discover its hard to understand a program if there are filters all over the place.
    But a filter is very good for one widget to alter something. However if to be used in many place and many forms in the app. Then subclassing is far less messy.


  • Lifetime Qt Champion

    Hi
    Luckly its not super complicated.

    Lets try to catch the focusIn/out

    we need to add a event filter function to some object that will handle it for us.
    Often mainwindow is used as the widget we monitor is living there.
    Place a QLineEdit on the form.

    class MainWindow : public QMainWindow
    {
        Q_OBJECT
    
    public:
        explicit MainWindow(QWidget *parent = nullptr);
        ~MainWindow();
    private:
        Ui::MainWindow *ui;
        bool eventFilter(QObject *object, QEvent *event); <<< add this
    };
    

    then in cpp.

    in the Mainwindow constructor, we add.
    ui->lineEdit->installEventFilter(this);

    now we need the body of the filter function

    
    bool MainWindow::eventFilter(QObject *object, QEvent *event)
    {
    
        if ( object == ui->lineEdit &&  ( event->type() == QEvent::HoverEnter )  ) {
            ui->lineEdit->setText("IN");
        }
    
    
        if ( object == ui->lineEdit &&  (event->type() == QEvent::HoverLeave )  ) {
            ui->lineEdit->setText("out");
        }
    
        // false means it should be send to target also. as in , we dont remove it.
        // if you return true , you will take the event and widget never sees it so be carefull with that.
        return false; 
    }
    

    and if we then run it
    alt text

    This is a very powerful way of altering how the std. widgets works but if you use for all cases where subclassing ( like we did in other thread) would be more self-contained, you will discover its hard to understand a program if there are filters all over the place.
    But a filter is very good for one widget to alter something. However if to be used in many place and many forms in the app. Then subclassing is far less messy.



  • @mrjj said in How to make an eventfilter?:

    This is a very powerful way of altering how the std. widgets works but if you use for all cases where subclassing ( like we did in other thread) would be more self-contained, you will discover its hard to understand a program if there are filters all over the place.
    But a filter is very good for one widget to alter something. However if to be used in many place and many forms in the app. Then subclassing is far less messy.

    It worked!

    One more great and accurate tip, mrjj!

    This is exactly what I was looking for from the beginning.

    With eventfilter, it is much easier to intercept the events of the controls I want and the difficulty is almost nil.

    What's more, it's much faster.

    About comparing eventfilters and subclasses ... I have a question:

    Can I put events from all the controls I want into the same function body as the event filter? Example: Within the same function body, put events of a line edit, a combobox and a text edit. Can i do that?

    If so, I think it is much easier to understand such a program, as I only need to make 1 prototype (in the .h file) and 1 function (in the .cpp file) and throw all the events in this body (I am inexperienced in QT and my program is still in the beginning, so I may be wrong). I'm wrong?

    Thank you so much again.


  • Lifetime Qt Champion

    @Alexandre-Camelo
    Hi
    Good it just worked :)

    Yes, you can handle events to any number of widgets in the filter function.
    The function is
    bool eventFilter(QObject *object, QEvent *event);

    and the object is the object (doh) that is about to get the event.

    Since the widget comes as base QObject * you will/might need to use
    qobject_cast to cast it to the actual type so your logic can

    say if its a QLineEdit , do
    if its a ComboBox do

    You will need a line
    ui->XXXX->installEventFilter(this);
    for each widget, you want to control this way.

    The standard way to implement an event function is normally via subclassing.
    And then simply use this class instead of the std. one.

    However, for simple things like focus in/out , event filters can be used.

    Issues with event filters will show as soon as you need to remember something between events for a given class. Then the filter function can become quite complicated and
    a subclass is waaaay better design.

    Also Qt is based on subclassing so event filters are not a replacement for that.

    However, for a simple app with one mainform and no popup dialogs etc then eventfilter will work super fine.



  • @mrjj OK.

    Understood.

    As I plan to do big projects in the future, I will start using subclasses then.

    Thanks again.


  • Lifetime Qt Champion

    @Alexandre-Camelo
    Hi
    Ok deal. :)
    Also as soon as you want to use custom painting, (paintEvent)
    then event filters becomes complicated.



  • Recently I need to substitute the event type of keypress of a Pong Gui with a self defined event type MyEvent(to Use UDP signal instead <- -> to control the game). However the problem is that seems for self-defined event I need to manually send or post them, while the event filter can't receive the self-defined event (the inbuilt keypress or mouseclick seems need no manually send). The event filter was installed as in mainwindow.cpp: ui->boardView->installEventFilter(gameloop iLoop); the eventfilter is realized in gameplay.cpp:
    '''

    bool Gameplay::eventFilter(QObject *target, QEvent *e)
    {
    Q_UNUSED(target);

    bool handled = false;
    if(e->type() == MyEventType)
    {
        MyEvent *myevent = (MyEvent *)e;
        if ( myevent->sg >0)
        {
            //pong paddle move left
            iP1Direction = (iP1Direction == 0 ? -5 : 0);
            handled = true;
        }
        else if ( myevent->sg <0 )
        {
            //pong paddle move right
            iP1Direction  = (iP1Direction == 0 ? 5 : 0);
            handled = true;
        }
    }
    if(e->type() == MyEventType)
    {
        qDebug()<<"abc";
        handled = true;
    }
    return handled;
    

    }
    '''

    The main.cpp '''

    #include <QtWidgets/QApplication>
    #include "mainwindow.h"
    #include "MyEvent.h"
    #include <iostream>
    #include <cmath>
    int main(int argc, char *argv[])
    {

    QApplication a(argc, argv);
    
    MainWindow w;
    w.show();
    
    
    
    for(int i=0;i<1000;i++){
    MyEvent myEvent1(MyEventType);
    myEvent1.set_ch(1);
    myEvent1.load_sg(1);
    QCoreApplication::postEvent(&w, &myEvent1);
    }
    
    return a.exec();
    

    }
    '''
    the mainwindow.cpp is

    '''
    //cited from https://github.com/ynonp/Pong
    #include <QPen>
    #include <QResizeEvent>
    #include <QDebug>
    #include <QtWidgets/QApplication>
    #include "mainwindow.h"
    #include "MyEvent.h"
    #include <QCoreApplication>
    #include <QEvent>
    #include <QObject>
    #include <QDebug>
    #include <iostream>

    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    iScore ( 0 )
    {
    ui->setupUi(this);

    QGraphicsScene *scene = new QGraphicsScene(this);
    
    QGraphicsRectItem *p1 = new QGraphicsRectItem(0, 0, 80, 20);
    p1->setBrush(QBrush(Qt::blue));
    QGraphicsRectItem *p2 = new QGraphicsRectItem(0, 0, 80, 20);
    p2->setBrush(QBrush(Qt::green));
    
    QGraphicsEllipseItem *ball = new QGraphicsEllipseItem(0, 0, 15, 15);
    ball->setBrush(QBrush(Qt::magenta));
    
    ui->boardView->setScene(scene);
    
    iLoop = new Gameplay(*scene, p1, p2, ball, this);
    QSize m(scene->sceneRect().size().width() + 10, scene->sceneRect().size().height() + 10);
    ui->boardView->setMinimumSize(m);
    
    resize(minimumSize());
    ui->boardView->installEventFilter(iLoop);
    
    
    QObject::connect(iLoop, SIGNAL(goal(int)),
                     this, SLOT(addScore(int)));
    

    }

    MainWindow::~MainWindow()
    {
    delete ui;
    }

    void MainWindow::addScore(int count)
    {
    iScore += count;
    ui->lcdNumber->display(iScore);
    }
    '''

    So how can I feed my self defined event into the GUI event filter?

    Thanks


Log in to reply