PDA

View Full Version : Event filtering



Cruz
28th March 2011, 02:22
Hello!

I have three widgets stacked. A is parent of B and B is parent of C. All mouse and keyboard events should go to A. I have installed an event filter in A for B and that works, A gets everything before B does. I also installed a filter in B for C, so now B gets all the clicks meant for C. But they don't go to A! And I don't want to install A as an event filter for C explicitly, because A is not supposed to know about C. How can I do this?

Thanks
Cruz

high_flyer
28th March 2011, 09:49
Sounds like a bad design problem.
You have conflicting requirements: A needs to filter C's events without knowing about C.
The solution should be a design solution not implementation workaround.

One work around is to send signals from the event filter in B - but I suggest you rethink your deign.

wysota
28th March 2011, 11:08
Why do you want to reverse the natural event propagation scheme?

Cruz
28th March 2011, 11:53
Well, we could discuss if the program structure makes sense or not, but that would be a different topic. There are cases when you don't have the time to refactor. The situation is that this issue is a part of a large software and a workaround has to be found TODAY. Anything else is secondary. Nobody cares what the code looks like inside.

In a nutshell: the troubled software is about keyframe interpolation for a robotic arm. A sequence of keyframes defines a motion. Keyframe widgets are lined up in a QScrollArea such that they can be dragged and dropped with the mouse to change their order. The QScrollArea (or more like its widget) is the parent A, that holds Keyframe objects, that are Bs. Each Keyframe object also shows a 3D GLWidget of the robot in the current pose, these are the children C. Now the GLWidgets receive mouse input so that they can be rotated and panned, which is needed in other parts of the whole software. But not in this case. When the Keyframes are in the scroll area, every mouse action has to be routed up from C to A and from B to A because A handles the drag and drop feature.

So please, can anyone suggest one or two lines of code that somehow solves this problem for now and worry about rewriting months of work later? I installed an event filter in the Keyframes already that eats the mouse clicks from the GLWidget. I just don't know how to resend the events so that the scroll area gets them.

wysota
28th March 2011, 11:58
Well, then that's easy. You don't want A to get C's events before C gets it, you want A to get them instead of C. The simplest way is to add a flag to B and C that they should ignore input events and in appropriate event handlers (mouse{Press,Move,Release}Event, key{Press,Release}Event) call QEvent::ignore() on the event object you get. Then events from C will be propagated to the parent (B) which will ignore them again and they'll reach A where you can handle them in the regular event handlers.


class C : public ... {
public:
void setIgnoreInputEvents(bool v) { m_ignoreInput = v; }
protected:
void mousePressEvent(QMouseEvent *ev) {
if(m_ignoreInput) { ev->ignore(); return; }
// do your regular stuff here
}
private:
bool m_ignoreInput;
};

Cruz
28th March 2011, 13:15
Beautiful, thank you! It works. I especially like that the special handling starts exactly where one would expect, so it can be documented in the source ow things work and why they are messy.

wysota
28th March 2011, 13:35
Just for completeness - there is an alternative that doesn't require you to modify B and C. You can install an event filter on the application object to intercept all events in your application. Then you can check if the target of the event is your own descendant and if so, steal the event. But this solution is slower than the one suggested earlier.