[Solved] Are nested event filters allowed/recommended
-
I was wondering, if it is it allowed/recommended to nest event filter? (void QObject::installEventFilter(QObject * filterObj))
I find this especially interesting with derives classes where the base class might have an event filter installed and the derived one as well. -
Hi Dieter,
if you read the "documentation":http://doc.qt.nokia.com/4.7/qobject.html#installEventFilter , it is stated:
bq. If multiple event filters are installed on a single object, the filter that was installed last is activated first.
So nested event filters should work.
-
Let me try to see if I got your question right. "nested" is a tricky concept, I think. It seems you are talking about what happens in this case:
@
class ObjectParent: public QObject {
objectParent(QObject* someWatchedObject, QObject* parent):
QObject(parent)
{
someWatchedObject->installEventFilter(this);
}
}class ObjectChild: public ObjectParent {
objectChild(QObject* someWatchedObject, QObject* parent):
ObjectParent(someWatchedObject, parent)
{
someWatchedObject->installEventFilter(this);
}
}
@That is: in both the subclass and the superclass you install an eventfilter on the same object. Am I right? It is an interesting question. The documentation doesn't state what happens if you try to install the same event filter twice. I think that that is what you are doing, since the QObject pointer you pass to the watched object remains the same. However, you should try this to be sure.
The first observation is of course that there is only one bool eventFilter() method called, as it is virtual. You will receive it in the subclass. If you want to enable any processing in superclasses, you'd have to call this:
@
bool ObjectChild::eventFilter ( QObject * watched, QEvent * event )
{
// your own processing. May return early for events you needed.return ObjectParent::eventFilter(watched, event);
}
@If I misunderstood what you mean by "nested event filters", please rephrase your question.
-
Why not have a look in the source code?
@
void QObject::installEventFilter(QObject *obj)
{
Q_D(QObject);
if (!obj)
return;
if (d->threadData != obj->d_func()->threadData) {
qWarning("QObject::installEventFilter(): Cannot filter events for objects in a different thread.");
return;
}// clean up unused items in the list d->eventFilters.removeAll((QObject*)0); d->eventFilters.removeAll(obj); d->eventFilters.prepend(obj);
}
@As the this pointer in both classes (base and child) is the same, it is removed from the list of event filters and re-added at the beginning.
Due to the fact that eventFilter() is virtual, the method of the most derived class is invoked. Calling the ObjectParent's eventFilter method at the end of the ObjectChild's one is in fact a good idea.
-
Volker, you are right of course that I could have referenced the source code. Thanks for doing this, and confirming my suspision that Qt won't do funny stuff such as adding the same object twice in the filter chain. Not sure if you can rely on this behaviour 100% in the future, as it is not documented and thus may change. Sure, not very likely, but still.
Perhaps we should wait for an answer from Dieter however to hear if I understood him correctly on what he was worried about and what he meant with "nested event filters".
-
[quote author="Andre" date="1294652387"]Let me try to see if I got your question right. "nested" is a tricky concept, I think. It seems you are talking about what happens in this case:
@
class ObjectParent: public QObject {
objectParent(QObject* someWatchedObject, QObject* parent):
QObject(parent)
{
someWatchedObject->installEventFilter(this);
}
}class ObjectChild: public ObjectParent {
objectChild(QObject* someWatchedObject, QObject* parent):
ObjectParent(someWatchedObject, parent)
{
someWatchedObject->installEventFilter(this);
}
}
@
[/quote]This is exactly what I was referring to and the answer I've received are (as always) excellent!
The reason why I've asked this in the first place is actually less "abstract" then what it might seem.
I my use case the base class is part of a mostly static framework and the derived class will be instantiated by a user of the library. As described the intuitive way to install an event filter in the base class would not work as expected if the derived class is not aware of this implementation detail and this is what I wanted to prevent in the framework.If I got it right, I would need to install an event filter to a stand-alone event filter object to get this solved in a transparent way like in the following example:
@
class objectParent;class EventFilterParent: public QObject
{
public:
eventFilterParent(objectParent* parent): QObject(parent), itsParent(parent) {}
bool eventFilter(QObject* obj, QEvent* event)
{
return itsParent->event(obj, event);
}private:
objectParent* itsParent;
}class objectParent: public QObject
{
objectParent()
{
this->installEventFilter(new eventFilterParent(this));
}
bool eventFilter(QObject* obj, QEvent* event)
{
// do stuff
return ???;
}
}class ObjectChild: public ObjectParent {
objectChild(QObject* someWatchedObject, QObject* parent):
ObjectParent(someWatchedObject, parent)
{
someWatchedObject->installEventFilter(this);
}
}
@ -
Hi Dieter,
Why do you implement eventFilter in the objectParent class?
You install an event filter on your own with a helper that calls your eventFilter method?
Then you can just overwrite event(), that has the same effect.The EventFilterParent class is totally not needed here.
From your example, you can remove the eventFilter stuff from the objectParent class and the complete EventFilterParent class.
-
In principle, I think you are right (though your implementation lacks a bit). You could protect your eventFilter implementation by putting the actual event filter in a separate, private object in your class and either handle it there, or defer the handling again to a private method in the widget. Still, you have to ask yourself how useful that is. You run the risk that the user is unaware of the internals of your class not only with the eventFilter method, but with every virtual method in your widget, including those inherited from QObject and QWidget. And those are certainly not the -most- least important ones! When reimplementing a virtual method, you have to know what you're doing, and it often is a great idea to call the base implementation for the cases you don't handle yourself anyway. You have to ask yourself if you really want to try to protect the user of your library from all these possible pitfals?
-
The API docs on "QObject::eventFilter() ":http://doc.qt.nokia.com/stable/qobject.html#eventFilter even mentions this:
bq. Notice in the example above that unhandled events are passed to the base class's eventFilter() function, since the base class might have reimplemented eventFilter() for its own internal purposes.
You can add a prominent message in your API docs to warn your users.
Andre is totally right. It is good practice to call the base class' virtual method unless you really know what you're doing.
If you really want to, you can of course put the event filter in a private object of the parent class. If you do that, then there is no need to define and implement an eventFilter() method in the parent class.
-
Volker, if you read the example code of Dieter, you see:
@
class CInternalFilterObject : public QObject
{
public:
CInternalFilterObject(CObjectToFilter* parent): QObject(parent), itsParent(parent) {}
bool eventFilter(QObject* obj, QEvent* event)
{
return itsParent->event(obj, event);
}private:
CObjectToFilter* itsParent;
}class CObjectToFilter: public QObject
{
CObjectToFilter()
{
installEventFilter(new CInternalFilterObject(this));
}
bool eventFilter(QObject* obj, QEvent* event)
{
// do stuff
return ???;
}
}
@That means, in the class CObjectToFilter, he creates an object to filter the events of CObjectToFilter (CInternalFilterObject), which then calls CObjectToFilter. This is a loop from CObjectToFilter over CInternalFilterObject to CObjectToFilter. That means, it can also be removed and will have the same effect.
-
Thank you for all your valuable input.
-
I must admit that I did not even know of the option to just overload the method QObject::event()
This is an interesting alternative to the use of an event filter and would clearly make my second code snippet simpler. -
For my specific problem I will stick with the good old "call the base class’ virtual method" approach as suggested.
@
bool derivedClass::eventFilter(QObjectobj, QEvent event)
{
if (event->type() == ...)
{
// do stuff ...
return ???;
}
else
{
// if it's not "ours", just pass the event on to our parent class
return parentClass::eventFilter(obj, event);
}
}
@ -
-
[quote author="Dieter" date="1294754903"]
- I must admit that I did not even know of the option to just overload the method QObject::event()
This is an interesting alternative to the use of an event filter and would clearly make my second code snippet simpler.
[/quote]
If you are only interested in some special events, you can also just overwrite the specific event handlers (see "QObject":http://doc.qt.nokia.com/latest/qobject.html#protected-functions and "QWidget":http://doc.qt.nokia.com/latest/qwidget.html#protected-functions documentation).
- I must admit that I did not even know of the option to just overload the method QObject::event()
-
QObject::event() only works for the events that are actually delivered to the QObject where this method is (re)implemented.
QObject::eventFilter() is for one QObject that filters the events of another QObject. Although it can be abused on this object too (thus filtering events on itself), it would definitely be better to reimplement event() (or any of the specialized xyzEvent() methods) in this case! Please do everyone a favor and consider this!
Also, watch out to check the obj pointer in eventFilter()! You should react on the event type and the proper object!
-
Thank you for the additional clarifications!