How to catch all events before reaching child widgets at a container widget



  • The use case was like below,

    1. There was a container widget with some child widgets
    2. Parent widget should be capable of catching all events before reaching children, and it's up to parent to determine whether children should handle this event or not.
    3. I don't know how many children would be, so event filters can't be the solution, there's no way to install all filters.

    I've learned there's one way to get all events
    [quote]
    subclass QApplication and reimplement notify().

    Qt calls QApplication::notify() to send out an event. Reimplementing this function is the only way to get all the events, before any event filters get the opportunity to look at them.
    [/quote]
    But the difficulty was that, in notify(), I've no idea about if the event is the one sent to that widget or its children, at least not quite easy to get that.

    Any other ideas?
    Thanks in advance.



  • If the child widgets aren't supposed to catch certain events, explicitly ignore the event in those widgets. The event will then be passed to the parent.



  • That's not the case.
    No children is not supposed to catch certain events, it all depends on some rules managed by parent.

    [quote author="Franzk" date="1294391950"]If the child widgets aren't supposed to catch certain events, explicitly ignore the event in those widgets. The event will then be passed to the parent.[/quote]



  • fifth, you can build list of all children and compare receiver in notify() with this list. It will be the easiest way for you I think.



  • You get events when children are added, an on this "childEvent":http://doc.qt.nokia.com/4.7/qobject.html#childEvent , you can install an eventFilter. That would give you only the events of the children, not of the whole application.



  • That sounds interesting.

    Then I can install event filter by recursively monitor childEvent(). Thx.

    [quote author="Gerolf" date="1294395574"]You get events when children are added, an on this "childEvent":http://doc.qt.nokia.com/4.7/qobject.html#childEvent , you can install an eventFilter. That would give you only the events of the children, not of the whole application.[/quote]



  • Yes, that's the easiest way to make it. But I'm afraid of performance issue.

    [quote author="Denis Kormalev" date="1294393216"]fifth, you can build list of all children and compare receiver in notify() with this list. It will be the easiest way for you I think.[/quote]



  • fifth, all depends on rate of children adding/removing and their count.



  • The solutions sound too complicated to me.

    If you instantiate the widget in the first place, you can get all it's children and install the event filter on it.

    If some child is added later on, there must be some controlling instance that does the "new MyBlaWidget" and adds it to some layout etc. Have that instance install the event filter too and you're done.



  • I think Gerolf's solution is not complicated at all. I think it is good design to make the parent widget itself responsible. If you have to handle this for each widget you create as it's child, you're bound to forget it sometime, somewhere, let alone that you are duplicating code. The code for the childEvent is easy enough:

    @
    void MyWidget::childEvent(QChildEvent* e)
    {
    if (e->child()->isWidgetType()) {
    if (e->type() == QEvent::ChildAdded) {
    e->child()->installEventFilter(this);
    } else if (e->type() == QEvent::ChildRemoved) {
    e->child()->removeEventFilter(this);
    }
    }

    QWidget::childEvent(e);
    

    }
    @

    See QObject::childEvent.



  • Thanks all.

    To recursively install filters on every descendant, your code would work well plus below code,

    @
    bool MyWidget::eventFilter( QObject* target, QEvent* e )
    {
    switch (e->type())
    {
    case QEvent::ChildAdded:
    {
    QChildEvent* ce = (QChildEvent*)e;
    ce->child()->installEventFilter(this);
    }
    break;

    case QEvent::ChildRemoved:
        {
            QChildEvent* ce = (QChildEvent*)e;
            ce->child()->removeEventFilter(this);
        }
        break;
    }
    
    return QWidget::eventFilter(target, e);
    

    }
    @

    [quote author="Andre" date="1294516755"]I think Gerolf's solution is not complicated at all. I think it is good design to make the parent widget itself responsible. If you have to handle this for each widget you create as it's child, you're bound to forget it sometime, somewhere, let alone that you are duplicating code. The code for the childEvent is easy enough:

    @
    void MyWidget::childEvent(QChildEvent* e)
    {
    if (e->child()->isWidgetType()) {
    if (e->type() == QEvent::ChildAdded) {
    e->child()->installEventFilter(this);
    } else if (e->type() == QEvent::ChildRemoved) {
    e->child()->removeEventFilter(this);
    }
    }

    QWidget::childEvent(e);
    

    }
    @

    See QObject::childEvent.

    [/quote]

    EDIT (Gerolf): added @ tags for code



  • If you need to do it recursively, you still, I think, should only apply the filter to widget type children. Othewise, you'll end up filtering events for all QObjects in the hierarchy. That might have unexpected consequences.



  • [quote author="Andre" date="1294651517"]If you need to do it recursively, you still, I think, should only apply the filter to widget type children. Othewise, you'll end up filtering events for all QObjects in the hierarchy. That might have unexpected consequences. [/quote]

    Sure, thx for reminder.

    Unfortunately, I was too optimistic.
    The above code I added looked like not properly working on all descendants.
    I demoed and found that only first 2 levels of descendant got successfully installed filters. I was wondering where it went wrong.



  • -Well, one issue may be that you are only adding children that are created while they are already added. That may not always be the case. A widget, already containing child widgets, can be added to the hierarchy. If you really want to catch all of those, make sure you also add all existing child widgets of the new child widget being added.-

    Let me rephrase that, as I find it difficult to understand when I read it back myself:

    One issue may be that you are only adding an event filter on the the widget (W) that is added as the new child, but not on any child widgets (CC) that new widget (W) may already have when it is added to your container widget (T). Any composite widget (W) will have that problem, and that includes all widgets that contain a scroll bar, a scrollable area, but also for instance the combo box widget is a composite.



  • Hi fifth,

    you also have to take care, that, during ChildAdded / ChildRemoved, you install eventFilters on sub widgets. It might happen, that a widget with sub widgets is added (e.g. a widget with a ui file, a pre layouted widget, ...). That is also not handled by your code...

    @
    void MyWidget::addChild(QObject* pObject)
    {
    if(pObject && pObject->isWidgetType())
    {
    pObject->installEventFilter(this);

        const QObjectList& childList = pObject->children();
        for(QObjectList::const_iterator it = childList.begin(); it != childList.end(); ++it)
        {
            addChild(*it);
        }
    }
    

    }

    void MyWidget::removeChild(QObject* pObject)
    {
    if(pObject && pObject->isWidgetType())
    {
    pObject->removeEventFilter(this);

        const QObjectList& childList = pObject->children();
        for(QObjectList::const_iterator it = childList.begin(); it != childList.end(); ++it)
        {
            removeChild(*it);
        }
    }
    

    }

    void MyWidget::childEvent(QChildEvent* e)
    {
    if (e->child()->isWidgetType())
    {
    if (e->type() == QEvent::ChildAdded)
    {
    addChild(e->child());
    }
    else if (e->type() == QEvent::ChildRemoved)
    {
    removeChild(e->child());
    }
    }

    QWidget::childEvent(e);
    

    }

    bool MyWidget::eventFilter( QObject* target, QEvent* e )
    {
    switch (e->type())
    {
    case QEvent::ChildAdded:
    {
    QChildEvent* ce = (QChildEvent*)e;
    addChild(ce->child());
    }
    break;

    case QEvent::ChildRemoved:
        {
            QChildEvent* ce = (QChildEvent*)e;
            removeChild(ce->child());
        }
        break;
    }
    
    return QWidget::eventFilter(target, e);
    

    }
    @

    This example also includes, that widgets parents are always widgets, so if a child is a non widget, it may not contain widgets as children.



  • @Andre,

    Got your idea. It's really prone to forget those already existed widgets, like toolbar, scrollbar, etc.

    But my demo showed that newly created widgets still failed to get filter installed.

    Let's take it more detail.
    I built a QFrame-based widget as container widget, then a ScrollArea, its content widget, and simply 2 PushButtons on content widget. Everything was dynamically built while no Qt Designer got involved.

    However it proved that container widget was well aware of adding of ScrollArea, content widget, but not those 2 buttons. Weird.

    @Gerolf,
    I won't need to handle ui files, I will build everything dynamically.



  • The problem mentioned above I guess that it was something regarding to timing. The timing 2 buttons was created, the timing childEvent was handled, the timing content widget finished its creation.

    But Gerolf's code successfully handled everything by going through every child widget.

    Thanks all you guys.


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.