c++qtqevent

How can I handle and filter events that are accepted and discarded deep inside some hierarchy without having to subclass every component on the way?


Assume following structure:

MainWindow
--->MySpecialWidget
    |-->QLineEdit
    |-->QSpinBox
    |-->QPushButton
    ---><Basically any other Widget accepting QMouseEvent>

Clicking on any on the above mentioned will cause its respective feature to be activated and the QMouseEvent to be accepted and discarded.

I would like to react inside my MainWindow on any mouseclick in a generic way. More specifically I would like to hinder basic Qt widgets from accepting QMouseEvent while still reacting to it in their usual way. Not accepting should only occur when they are children of MySpecialWidget. Since the QMouseEvent is accepted two layers deep, my MainWindow can not access it by the means of a direct eventFilter.

My current solution (simplified):

void MySpecialWidget::eventFilter(QObject *o, QEvent *e)
{
    if(e->type() == QEvent::MouseButtonPress)
    {
        QMouseEvent *mE = static_cast<QMouseEvent*>(e);
        
        QMouseEvent newMouseEvent = new QMouseEvent(QMouseEvent::MouseButtonPress, mE->pos(), mE->button(), mE->buttons(), mE->modifiers());
        qApp->notify(this, &newMouseEvent);
        // Check how this handled the copied Mouseevent
        return /*result of check*/;
    }
    return Base::eventFilter(o, e);
}

I was also thinking about attaching my MainWindows eventfilter to everything relevant but that sounds like a nightmare to handle in bigger projects.

The question arises: How can I handle and filter events that are accepted and discarded deep inside some hierarchy without having to subclass every component on the way?

Has anyone ever done something similar or do you know if it is even possible in another way? Is my current solution acceptable?

Edit: I noticed for different QWidgets you get different behavior when a QMouseEvent is "eaten". For example MySpecialWidget can filter QMouseEvents of QLineEdit but can not of QSpinBox since it itself contains a QLineEdit. I find this behavior of this library highly irritating.

Edit2 clarification: Although vahanchoos comment would solve the problem, I'm looking for a solution that can work with MySpecialWidget without touching the QApplication or contained classes.

Any comment on my approach or the feasibility of my request would be highly appriciated!


Solution

  • I hope I understood the keypoint behind this question.

    Basically, if your issue is "How to catch the events for a big number of widgets of various type, including those that are made of sub-widgets (like QSpinbox > QLineEdit) without making it a nightmare to manage", you can make use of QObject::findChildren.

    More precisely, add this to your window constructor after the call to ui->setupUi() and that will catch everything under the instance of MySpecialWidget:

    auto children = ui->mySpecialWidget->findChildren<QWidget*>(QString(), Qt::FindChildrenRecursively);
    for (QWidget* w : children)
        w->installEventFilter(ui->mySpecialWidget);
    

    As you probably already understood, this will explore all its children widget (recursively) and install the instance as a common event filter for all.


    Side note:

    Something is wrong with your implementation of MySpecialWidget::eventFilter. It is supposed to return a boolean. Overriding this method usually is done like this:

    bool MySpecialWidget::eventFilter(QObject *obj, QEvent *event)
    {
        if(event->type() == QEvent::MouseButtonRelease)
        {
            QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
            //do something
            return true;
        }
        else // standard event processing
            return QObject::eventFilter(obj, event);
    }