PDA

View Full Version : Event filter question



d_stranz
6th July 2011, 17:43
I am still working on my custom rubber band widget, derived from QWidget. The behavior is as follows:

- on mouse press, the rubberband widget is shown
- on mouse move, the selection area is appended to and displayed on the widget
- on mouse release, the rubberband widget is hidden

The RB widget is created as a child of the widget it interacts with (the "parent widget"). If I implement the RB widget and mouse event handlers directly in the parent widget, everything works fine - the rubberband is displayed, drawn, hidden, and the parent widget receives all signals from the RB.

Following the model in Qwt, I have abstracted this into a "picker" class that now owns the rubberband widget. The picker class is derived from QObject and is created with no parent. The RB widget is created as a child of the parent widget. When the picker is "enabled", it installs an event filter on the parent widget to handle mouse events.

What is happening is this: When the filter is active, every mouse press event is caught by the filter, but after that, only every other set of mouse move events is caught. That is, if you click and drag, the first time you do this, qDebug() shows that the MouseButtonPress event was caught by the filter, but none of the MouseMove events are caught, and no MouseButtonRelease. If you immediately click and drag again, this time, all the events are caught. If you do it a third time, only the mouse press and nothing else. Off, on, off, on for as long as you want to play with it..

I am guessing that some other widget is eating the mouse move events, but the same code is being executed every time.

Can anyone give me some hints to try to determine why this behavior happens, and what I might do to prevent it? Obviously, when the rubberband widget is being shown, it should be getting all of the events.

d_stranz
6th July 2011, 21:54
OK, after a lot of hacking around, I have found that:

If I create mouse event handlers for the parent widget and call QEvent::ignore() in those handlers when the picker is enabled, then I get the behavior I want, all the time.

If I add the handlers to the parent widget, but do nothing in them (i.e empty code blocks), then my picker's eventFilter never gets called for those events.

If I remove the handlers completely from the parent widget, then it is back to the off / on / off / on behavior.

Is there any explanation for this? In the last case, it almost seems as though the rubberband widget and parent widget are flip-flopping Z-order, but except for my code calling show() and hide() on the rubberband widget, there isn't anything that could cause this. And if the parent widget is initially on top, then I would expect that calling show() on the rubberband widget would always bring it to the top (not alternating as it seems to do now).

In the Qt docs (QObject::installEventFilter()), it says:


If multiple event filters are installed on a single object, the filter that was installed last is activated first.

Is there a way that another event filter could be getting in the way of mine? Does calling QWidget::show() or QWidget::hide() somehow result in an event filter being installed or removed?

wysota
7th July 2011, 01:10
I don't think show() and hide() install any event filters. You must have some problem in your code. I have never experienced any on-off behaviour of event filters.

d_stranz
7th July 2011, 04:29
You must have some problem in your code.

Yes, this is very strange behaviour. I have not used event filters before, so I have probably done something wrong. It does not seem right that I should have to implement event handlers in the class that I want to filter events from, and to have those handlers ignore() the events in order to make the filter work at all.

I see from the Qt Quarterly article on events (http://doc.qt.nokia.com/qq/qq11-events.html) that the default QWidget event handling is ignore(), so being forced to call it in the filtered parent widget handler doesn't make sense. It also doesn't make sense that my event filter doesn't get called sometimes, because in my understanding of what I read, once it is installed it will always be called before the parent widget's own event handler is called.

In my event filter, I am handling only MouseButtonPress, MouseMove, and MouseButtonRelease events, and after my handling they and all other events are passed to QObject::eventFilter() for further processing.

Are there other events I should be handling that might make this work properly?

Does it make a difference that the QWidget class I am filtering events from is derived from QGraphicsView?

stampede
7th July 2011, 07:31
Maybe post your eventFilter code.

wysota
7th July 2011, 07:35
Can we see some code?

d_stranz
7th July 2011, 16:07
I have a "test" project that I am using to develop this, before moving it into my mainstream project. In this case, I decided that it would be straight-forward enough to simply implement it in the mainstream project first. My mistake. So I will go back to the test project, see if I can get it working correctly there. If not, I will post code and ask for comments.

It is a little awkward, because the picker code I implemented has some dependencies on the rest of the framework I am developing. If I post, I will try to extract out the minimum parts to demonstrate the functionality.

d_stranz
7th July 2011, 23:08
OK, I rewrote the "picker" class as part of the test project, stripped out anything that depended on the rest of the architecture and got it all working without strange off / on behaviour. Unfortunately, it is nearly identical code to what I had in the non-working version, so I am still not sure what was wrong.

Two things of importance, though:

- If the picker (and the rubberband widget it iteracts with) are instantiated on a parent derived directly from QWidget, then the rubberband works with no need to implement mouse event handlers in the parent widget.

- If the parent widget is derived from QGraphicsView, then the event filter is not called unless mouse event handlers are implemented in the view class and QEvent::ignore() is called.

I verified this by simply changing the derivation of my main window central widget from QWidget to QGraphicsWidget; when it is QWidget and I comment out the mouse event handlers, the rubberbanding works fine. If I change to QGraphicsWidget, nothing works until I put the mouse handlers back.

My suspicion is that in the original code, I was not setting the geometry for the rubberband widget correctly, which resulted in the widget being located in a different place on screen than I thought. Consequently, some other widget was actually getting the mouse events, not my event filter.

Thanks to Wysota for useful discussion.