
Originally Posted by
csaken
Sorry if I have offended you with my questions.
I only asked a question, and then I have found a solution which is working, and then I have asked if anyone sees some major impediments with the approach I took, since I'm not very experienced with Qt library.
I have no problem with your solution and I don't feel offended.
I want to use event / slot mechanism because it provides a clean way to pass information from one object to another.
But not in every possible situation. Don't try to force it to become a swiss-army-knife of information passing.
Just as a side-note, where the decision is taken could be very important, specially if decision is taken inside or outside a loop.
I guess it depends on a case-by-case basis.
I don't have to check against all registered listeners, I have a intermediate string,QObject map, which maps event type to a QObject based helper relay object. Inside EventRouter, when a Target gets registered, I retrieve the relay object mapped for the given event, and the slot of all Target object get registered to this relay object.
Then you either have lots of data redundancy and/or lots of work when an object has to be unregistered from all events it listens to (i.e. when it is destroyed) - then you have to look through the whole data structure to find all messages it is subscribed to.
The issue is that the filter object needs to have the event types hard-coded in order to be able to check for given event type and then forward it.
No. Take a look at QObject::customEvent(). It wouldn't be there if event types had to be hardcoded.
And there is one more side effect of your design vs the one I suggested earlier - you have one method for handling all message types (a.k.a. "customEvent()") which leads to spaghetti code and if/switch code (you have to know what kind of event it is, right?) that you wanted to avoid having in the first place. You just moved it elsewhere. I don't really see a difference between such architecture and event filters where you have an identical if/switch block. On the first glimpse it seems you have the events "pre-filtered" but then when you look at your "router" object you see that all possible messages pass through it.
Here is a mockup of an equivalent of your system taking advantage of Qt's meta-object system:
class EventRouter
: public QObject {public:
Item it(subscriber, source, t, methodName);
m_subscribers << it;
if(!m_monitored.contains(source)) {
source->installEventFilter(this);
m_monitored << source;
}
// connect subscriber::destroyed() to make sure your list is in sync
}
void unsubscribe
(QObject *subscriber,
QObject *source, QEvnet
::Type t
) { removeItem(subscriber, source, t);
// disconnect subscriber::destroyed()
// check if anyone still wants to monitor source and remove the event filter if not
}
protected:
foreach(Item it, m_subscribers){
// highly inefficient, see below
if(it.source!=o || it.type!=e->type()) continue;
QMetaObject::invokeMethod(it.
subscriber, it.
methodName.
latin1(), e
);
}
return false;
}
private:
struct Item {
Item(...){ ... }
};
QList<Item> m_subscribers; // highly unoptimal, but since it's just an example...
QObjectList m_monitored;
};
class EventRouter : public QObject {
public:
EventRouter(QObject *parent = 0) : QObject(parent){}
void subscribe(QObject *subscriber, QObject *source, QEvent::Type t, QLatin1String methodName) {
Item it(subscriber, source, t, methodName);
m_subscribers << it;
if(!m_monitored.contains(source)) {
source->installEventFilter(this);
m_monitored << source;
}
// connect subscriber::destroyed() to make sure your list is in sync
}
void unsubscribe(QObject *subscriber, QObject *source, QEvnet::Type t) {
removeItem(subscriber, source, t);
// disconnect subscriber::destroyed()
// check if anyone still wants to monitor source and remove the event filter if not
}
protected:
bool eventFilter(QObject *o, QEvent *e) {
foreach(Item it, m_subscribers){
// highly inefficient, see below
if(it.source!=o || it.type!=e->type()) continue;
QMetaObject::invokeMethod(it.subscriber, it.methodName.latin1(), e);
}
return false;
}
private:
struct Item {
QObject *subscriber;
QObject *source;
QEvent::Type type;
QLatin1String methodName;
Item(...){ ... }
};
QList<Item> m_subscribers; // highly unoptimal, but since it's just an example...
QObjectList m_monitored;
};
To copy to clipboard, switch view to plain text mode
And to use it:
EventRouter *router = ... ;
MyObject *o = new MyObject; // MyObject has a "handleKeyPress(QEvent*)" invokable method (i.e. slot or Q_INVOKABLE)
EventRouter *router = ... ;
MyObject *o = new MyObject; // MyObject has a "handleKeyPress(QEvent*)" invokable method (i.e. slot or Q_INVOKABLE)
QObject *some = ...;
router->subscribe(o, some, QEvent::KeyPress, QLatin1String("handleKeyPress"));
To copy to clipboard, switch view to plain text mode
The additional benefit is that you can decide on the fly which method will handle the event. Of course that's not a very good design from the OOP perspective but hey... we prefer flexibility, right?
Bookmarks