PDA

View Full Version : Selective signaling



csaken
28th April 2010, 23:07
Is there a mechanism in Qt to do selective signal emit?

Let's assume I have a Event class, which I pass across my objects with Qt signal-slot mechanism.
For new type of events, I add new classes,let's say AppEvent, which extends Event class. While I can still use the signal-slot mechanism to emit signal with AppEvent instance as parameter, all my slots which are interested in AppEvent are now required to check if the parameter is instance of AppEvent. Worse, all other slots, who are not potentially interested in AppEvent, get called, only to realize they have no interest in the event.

Does Qt support anything which would allow implementing this kind of signal selection?

Regards,
Jancsi Farkas

aamer4yu
29th April 2010, 06:30
Connect the slots properly..you have signature for slots.. dont you ?
Also can you show some code snippet what you are trying to do ?

csaken
29th April 2010, 08:23
This post is related to another post of mine, regarding implementing hierarchical model-view-controller. The idea is that each controller to have signal like event(Event*), eventUp(Event*), eventDown(Event*) and slots like handleEvent(Event*), fireEvent(Event*) ..., and each parent and child are connected using these signal-slots, where Event is a base class for the events propagating between controllers.

Obviously I will have many types of events, propagating between controllers, this is ok, since I can extend Event, but I cannot change the signature of the signal/slots easily, because then interconnecting I have to somehow find out what signals to connect to parent/child controllers, and for those, child controllers need to have slots, parent controllers need to have signals, and vice-versa.

Having something which allows signaling to slots based on some criteria of signal parameter would be great. I'm thinking of something like EventRouter, to which various controllers get registered, with a given string parameter, which is the class of the event, and then when you fire an event(Event*) signal, then the EventRouter will call only slots which where registered for the class of Event passed as parameter. This will allow to call one set of slots for AppEvent, another set of slots for SomeOtherEvent, through same EventRouter, and allow handling of those events through the same interface (event slot), in different controllers.

There is any kind of signal routing like this implemented in Qt, or there is a mechanism which can be used to implement this?

Regards,
Jancsi Farkas

squidge
29th April 2010, 08:46
It sounds to me like you are describing Qt's signal slots mechanism all over again...

Why not just have one signal with all the required information contained in the signal, such as type (Up/down, etc) ?

csaken
29th April 2010, 09:37
Because it cannot be. or at least not easily.
I have 3 controllers, one processing AppEvent, one processing ToolbarEvent, and another one processing some other XXXEvent.
I need a unified event/signal model, because I want to connect these in hierarchy, any of these controllers can be parent for other or vice-versa so if one controller cannot handle an event, then passes it to it's parent controller to handle it and so on.
Also, should be possible that controllers send received event down to their child controllers.
While the Event is something generic, yet I should be able to identify what kind of event I'm handling, in order to respond in a controller to a specific event. While I should be able to identify the type of the Event (App, Toolbar, XXX) I cannot use different signal/slot for each event (one slot for AppEvent, one slot for ToolbarEvent, one for XXXEvent), because then in order to be able to interconnect the controllers, all my controllers have to implement those signals and slots. This means if I add a new event, I have to recode all my controllers to have signal and slot for handling that new event type, and signals / slots for bubbling up / passing down the event to their parent/child controller.
If I use only one single signal/slot, handling events, I can identify the event type, for example by using the className(), however, if I have 1000 controllers interconnected, and only one of them is interested in AppEvent, when the controller one emit signal(Event*) with an AppEvent instance as parameter, 999 controllers will check to see if the event is one they are interested in, while only one is interested, this also means 999 times the event type is checked just to realize, it is not the right kind of event.

It is the same like Widget handling key event. If cannot handle or don't want to handle the key event, then sends it to parent widget to handle it. Same for mouse events. But what happens if You don't know how many event types your widget will need to possibly pass to parent, yet, you are bound to pass unknown / unhandled events to your parent widget?

csaken
29th April 2010, 12:22
This is how I have resolved it:
I have created a EventRouter object which uses a helper object to hook slots on the signal. For each event type, there is a helper object with potential slots hooked up to it's signal. When event is fired, EventRouter has only to get the helper object and fire the event on that, which in turn will call all registered slots, using Qt's slot/signal mechanism.

More about this here: http://blog.bigpixel.ro/2010/04/29/selective-slot-activation-in-qt-using-signal-slot-mechanism-3/

Are there any issues with this approach?

Regards,
Jancsi Farkas

wysota
29th April 2010, 16:54
Why not use the event mechanism (including the event filters)? Or why not create your own subscriber pattern implementation? Then you could only receive those objects you are interested in.

csaken
29th April 2010, 23:14
Event mechanism is partially the same as what I have implemented (using a base Event class which gets extended for various event types), except the fact that in my implementation, the decision on what slots are interested in a given event type is taken only once by the EventRouter class, which in turn calls all slots interested in that event type, while (correct me if I'm wrong) when you use / install event filters, all filters gets called by Qt event mechanism, regardless of they being interested in a given event type or not, and you still have to check in each of your filter object's eventFilter() slot, if the event is the one for you, like in the code bellow:



bool FilterObject::eventFilter(QObject *object, QEvent *event)
{
// this condition is the one I want not to have.
// I want to have this decision before even calling event filter,
// so in this function I know for sure, the event is the one I expect
// and also, I want to have this calls to eventFilter slot of various
// objects made by the Qt signal slot mechanism
if (object == target && event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Tab) {
// Special tab handling
return true;
} else
return false;
}
return false;
}


Actually what I implemented is a subscriber pattern implementation, where Targets subscribe to a given event type, and they are notified by EventRouter in a single shot using Qt signal slot mechanism. Only oneevent type check is made, in the EventRouter, versus many event type checking in each of the installed event filter's filterEvent() slot.

Do you know any issues related to this implementation, or there is another better approach to this?

Regards,
Jancsi Farkas

wysota
29th April 2010, 23:31
Event mechanism is partially the same as what I have implemented (using a base Event class which gets extended for various event types), except the fact that in my implementation, the decision on what slots are interested in a given event type is taken only once by the EventRouter class, which in turn calls all slots interested in that event type, while (correct me if I'm wrong) when you use / install event filters, all filters gets called by Qt event mechanism, regardless of they being interested in a given event type or not, and you still have to check in each of your filter object's eventFilter() slot, if the event is the one for you, like in the code bellow:

It's exactly the same thing. The only difference is where the decision takes place. Computational complexity is more or less the same. What you wrote in your comment can be implemented using the following pattern:


class Object : public QObject {
// ...
protected:
bool eventFilter(QObject *o, QEvent *e){
if(o == whatIExpect){
switch(e->type()){
case TypeThatIExpect:
return mySpecialEventTypeHandler(static_cast<MySpecialEvent*>(e));
case ...: // ...
}
}
return QObject::eventFilter(o,e);
}

virtual bool mySpecialEventTypeHandler(MySpecialEvent *e) {
// your code goes here
return whetherYouWantOthersToSeeTheEvent;
}
};

Now you only need to implement the last method.

A side note - signalling can't be "selective". The reason for that is that a signal informs its environment about the object state without caring if anyone is interested in that information. The whole idea of signals is that they are not selective. Otherwise you couldn't have loose coupling between objects.

csaken
30th April 2010, 00:02
I understand what you are saying, but the issue is that, as I have written in previous posts, I'm:
- bound to have one signature for signals, otherwise I cannot have a generic event passing between the controller layers. This indeed can be solved by the event mechanism provided by Qt.
- event mechanism provided by Qt calls ALL installed event filters, regardless of the event they want to filter out, and the handler itself has to check if he got an event he is interested in. Having this in a pyramidal hierarchy would mean that the top controller would signal event down to many child controllers, which in turn could signal event down to many child controllers,etc, and lot of those controllers might not even be interested in the event, because they were installed for an another event type. Yet each one of them has to check if the event is the one they are interested in.

Although the speed could not be a concern, and those checks could not take significant amount of time, still, many small things sum up, and also, from architecture's p.o.v. I find a bit unclean to have each filter called, even if they are not interested in the event.

wysota
30th April 2010, 00:21
I understand what you are saying, but the issue is that, as I have written in previous posts, I'm:
- bound to have one signature for signals, otherwise I cannot have a generic event passing between the controller layers.
But why use signals in the first place? The fact that you have some mechanism (i.e. signals) or technology (i.e. Qt) doesn't mean you have to use it for literally everything.



- event mechanism provided by Qt calls ALL installed event filters, regardless of the event they want to filter out, and the handler itself has to check if he got an event he is interested in.
As I said earlier this is just a matter of where the decision is made and I have shown you how to do it in a clean way. The filtering object can be a separate object that can act as a place where you subscribe to certain events and only those events are forwarded to you.



Having this in a pyramidal hierarchy would mean that the top controller would signal event down to many child controllers, which in turn could signal event down to many child controllers,etc, and lot of those controllers might not even be interested in the event, because they were installed for an another event type. Yet each one of them has to check if the event is the one they are interested in.
How is it different from having to check each coming event (be it a real event or a signal or any other kind of message) against each registered listener? If any object can subscribe to any event then you need to check your whole list of subscribers for each coming message type (more or less).


Although the speed could not be a concern, and those checks could not take significant amount of time, still, many small things sum up, and also, from architecture's p.o.v. I find a bit unclean to have each filter called, even if they are not interested in the event.

So don't use filters. Don't use events. Don't use Qt for the task. Who said all that was the best choice for this particular use-case? Implement something on your own, don't try to force Qt into doing something it was never meant to do.

csaken
30th April 2010, 00:35
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 want to use event / slot mechanism because it provides a clean way to pass information from one object to another.

Just as a side-note, where the decision is taken could be very important, specially if decision is taken inside or outside a loop.



How is it different from having to check each coming event (be it a real event or a signal or any other kind of message) against each registered listener? If any object can subscribe to any event then you need to check your whole list of subscribers for each coming message type (more or less).


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.
On signaling I only have to get the object from map, by event type, and call the registered slots by emitting a signal in that relay object.



As I said earlier this is just a matter of where the decision is made and I have shown you how to do it in a clean way. The filtering object can be a separate object that can act as a place where you subscribe to certain events and only those events are forwarded to you.


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. This also means, each time a new event is added and I want to add it to filter, I have to change the filter's code, to add this new event type and the code required to forward the event, which is not acceptable.

Regards,
Jancsi Farkas

wysota
30th April 2010, 01:57
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:
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;
};

And to use it:

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"));
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?

csaken
30th April 2010, 08:52
class EventRouter
{
QMap<QString, EventRouterHelper> routes; //EventRouterHelper has fireEvent(Event* e) which
//emits event(e) signal,and also has an event(Event*) signal

// Traget has handleEvent(Event*) slot
void RegisterTarget(QString& type, Target * )
{
// if we have not previously had a helper object for this event type
// create one for this type, then connect object's signal th Target' s handleEvent slot
// this will efectivelly queue up all slots interetested in this event type
// on this helper object's signal
}

fireEvent(Event *e)
{
retrieve helper object which contains already the list of cpnnected slots who are interested
in this kind of signal, from the map
fire event on helper object, which uses Qt's infrastructure to deliver the signal to all connected slots
}
}

EventProcessor1: public Target { ..
void handleEvent(Event e)
{
we are sure here is the right kind of event, we are this target registered for.
process the event
}

Usage:
EventRouter r;
Target t1
r.register(classofEvent1, &t1);
...
r.register(lassofEvent2, &t2)

r.fireEvent(new Event1());
r.fireEvent(new Event2())


Basically we are talking about same thing, except the fact that you are using a list of Item's to hold the N observers, and at each event you are traversing the entire list of observers, to notify the observers if they are interested (you can have 10000 of observers, while only the last one is interested in the event you want to notify him about) , while I'm using N lists of observers, one for each type of event, and I use the list of slots registered on helper object's signal.
If your code were storing for each event type a list of notifiers, and then the solution was mostly the same, except of course the fact that in your code you have the loop to deliver the event, while I'm using Qt's signal-slot calling, which basically in the end reduces down to same thing, going over a list of interested parties, and call them.

wysota
30th April 2010, 09:53
Basically we are talking about same thing, except the fact that you are using a list of Item's to hold the N observers, and at each event you are traversing the entire list of observers, to notify the observers if they are interested (you can have 10000 of observers, while only the last one is interested in the event you want to notify him about) , while I'm using N lists of observers, one for each type of event, and I use the list of slots registered on helper object's signal.
See my comment in the code - I know it's inefficient, I only wanted to show the functionality without cluttering the code too much.


If your code were storing for each event type a list of notifiers, and then the solution was mostly the same, except of course the fact that in your code you have the loop to deliver the event, while I'm using Qt's signal-slot calling, which basically in the end reduces down to same thing, going over a list of interested parties, and call them.
What happens (with your code) when one object subscribes to multiple types of messages or messages of the same type coming from different objects? How does handleEvent() look like then?

csaken
30th April 2010, 10:13
Usually each processor handles one type of event or a small group of events.
If I decide to subscribe the same processor to more than one event, nothing changes in the way they are registered, or how it's called.

From the event router's point of view does not matter, since he has for each event type a QObject,and each of these helper objects has a list if slots queued up.

Still, in this case, in the Processor's code, I have to check for what signal was called, and proceed accordingly. However, the processors get called only for the event types they are interested in.




Processor1::handleEvent(Event* e)
{
if(typeofEvent is Event1) { ... }
if(typeofEvent is Event2) { ... }
}

EventRouter r;
Processor p;
r.register(typeofEvent1, &p);
r.register(typeofEvent2, &p);

r.fireEvent(new Event1()) // triggers Processor 1, since is in the typeofEvent1's list of queued slots for event() signal
r.fireEvent(new Event2()) // also triggers Processor 1, since it is also in the list of typeofEvent1;s list of queued slots for event() signal.


Also, the event sending mechanism I want to be lousy coupled, I'm not interested in who sent the event, I'm only interested in getting it from somewhere, and handling it then possibly send some more events, somewhere. Controllers on different layers will take care of sending up/down the events, although they are also not interested from where comes, only the direction, up or down (I think, I'm still ironing out things on this side)