PDA

View Full Version : How to allow only one signal to be connected to a QObject's slot



neomilium
1st February 2013, 15:16
Hello,

I'm facing a problem I can't figure out and google does not help me so much...

Slots/Signals allow to connect multiples signals to multiples slots and that's really useful but this time I want to restrict a slot to have only one signal emitter.

On one side, I have some controls that produce values (ie. potentiometers which deliver a 0-127 value). On other side, I have properties that will be dynamically linked with only one control.

Currently, controls have a signal valueChanged(int) which is dynamically (when user want to) assigned to a dedicated property's slot (setValue(int)). This is working fine except this way I can map two controls to the same property which is absolutely the wanted behavior.

I look at QObject::disconnect() function but this does not allow to disconnect all emitters to a specified object's slot: its only useful if I want to disconnect all receivers of a specified signal.

Do someone how to disconnect all controls connected to my property ?

wysota
1st February 2013, 15:26
Slots/Signals allow to connect multiples signals to multiples slots and that's really useful but this time I want to restrict a slot to have only one signal emitter.
If you want that then you probably shouldn't use signals at all. There are other mechanisms in C++ that you can use, e.g. you can register a callback. Then you'll have total control.


class MyCallback {
public:
virtual ~MyCallback(){}
virtual void myCall() = 0;
}

class MyCallbackImpl : public MyCallback {
public:
// ...
void myCall() { doSomething(); }
};

void XXX::setCallback(MyCallback *cb) {
m_callback = cb;
}

// ...

void XXX::yyy() {
if(m_callback) m_callback->myCall();
}

neomilium
1st February 2013, 15:51
Hello,

Thanks for this quick reply but I want to keep the ability to connect same signal (ie. control) to different slots (ie. properties). With your solution, I'll need to keep locally a list of callbacks then provide accessors to create/remove/check callbacks where slot and signal mecanism already handle this the right way.

Is there is no way to drive it using Qt system ?

wysota
1st February 2013, 16:14
Thanks for this quick reply but I want to keep the ability to connect same signal (ie. control) to different slots
This doesn't change anything. The same mechanism can be applied.


Is there is no way to drive it using Qt system ?
No such abuse of the signal system is supported in Qt out of the box. Bear in mind all slots are regular functions that can be called by anyone anytime.

Santosh Reddy
1st February 2013, 16:26
I don't think this is possible directly using what Qt Object system gives you. The main reason is that the connection concept is almost completly driven fron the signal end, and there is no way (using APIs) to find out number of singals trigerring a objects slot. Other way round is possible, you could find out how many slots are connected to a given signal.

This is what I would do.

Educate the slot (or the object of the slot) about the signal sender() object, so that the slot will only care the incovation if the sender() has matched.

i.e. whenever a dynamic connection is made to a slot(), immediatly the sender is also set, code will look somthing like




connect(pushButton1, SIGNAL(clicked()), object, SLOT(singularSlot()));
object->setSignalSender(pushButton1);

connect(pushButton1, SIGNAL(clicked()), object, SLOT(singularSlot()));
object->setSignalSender(pushButton2); // this will overwrite the pushButton1

class Object : public QObject
{
...

public:
setSignalSender(QObject * sender)
{
onlySender = sender;
}


public slots:
singularSlot()
{
if(onlySender == sender())
{
// setValue();
}
}

wysota
1st February 2013, 16:29
@Santosh Reddy: it doesn't make sense to do what you suggest. You already implement the whole listener pattern with your "setSignalSender" method (look at my snippet above, lines 13-15) which is what OP tries to not to do. Signals and slots mechanism is simply not meant to be used this way. If one is lazy enough not to write 10 lines of code, he will not write more than 10 lines of code which is what is required to make this whole abuse relatively safe.

Manual book-keeping is required here. There is no way around it.

Santosh Reddy
1st February 2013, 17:01
You already implement the whole listener pattern with your "setSignalSender" method (look at my snippet above, lines 13-15) which is what OP tries to not to do.
May be, but I see some difference, the "callback" approach is implemented at the signal sender end, where the sender can send only one signal (or call one operation), off cource one can have a list of call backs. OP wants to still be capable of sending multiple signals to multiple slots (of different object though).

The "sender filter" appraoch is implemented at the receiver end.



Signals and slots mechanism is simply not meant to be used this way
May be yes. Tell me one thing, why does QObject has a connectNotify(const char * signal) and does not have connectNotify(const char * slot).

I know there is warning in the documenation, but this function will be equally usful for slot for that same reason for a signal

d_stranz
1st February 2013, 17:10
I am puzzled why this signal / slot connection count issue is a problem for the OP at all. Unless the signal-emitting code is in a library where multiple apps can have access to it, then the OP is completely in charge of how many connections are made between signals and slots, because he's writing the code to set them up. And if the code is in a library and some other app comes along and uses the library to try to control the same physical device, I do not think there is anything in Qt to prevent that.

So I ask the OP is he's not creating a problem in his mind that doesn't actually exist at the coding level?

wysota
2nd February 2013, 09:25
May be, but I see some difference, the "callback" approach is implemented at the signal sender end, where the sender can send only one signal (or call one operation), off cource one can have a list of call backs. OP wants to still be capable of sending multiple signals to multiple slots (of different object though).

The "sender filter" appraoch is implemented at the receiver end.
You can put the callback at either end.


May be yes. Tell me one thing, why does QObject has a connectNotify(const char * signal) and does not have connectNotify(const char * slot).

I know there is warning in the documenation, but this function will be equally usful for slot for that same reason for a signal
It shouldn't have either of them :) But I can answer that question -- connectNotify can be used to detect situations when there is no slot connected to a signal which makes it possible to optimize out signal emission.

For a slot you can't really have an equivalent because slots can be called as regular methods. Thus knowing there are no senders for the slot, doesn't mean the slot can't be invoked -- either directly or through QMetaObject::invokeMethod().


@everyone:
In general, signals can be considered "public" in the way that they inform the environment of an object of a situation inside the object. They have no control over how this information is used by the environment. Using signals as a generic callback mechanism ("ok, I emit this signal and I know that there is a slot connected to it that will do this and that and then I can do this and that") is a clear abuse of the definition of the signal, that allows to have loosely-coupled objects. If you need tight coupling, then signal-slot mechanism is not the right way to go (You might say this situation is an anti-pattern of using signals and slots). You can still use meta-object facilities that Qt offers to ease your task, of course, but you should build your own mechanism for a one-one (or one-many) coupling. Signals and slots mechanism is clearly to be used in many-many situation. And there is no magic there either -- there is a list of receivers of each signal kept in each object that is iterated over when the signal is emitted and slots of each receiver are called in sequence. So abusing signals in a way described in this thread does not prevent any book-keeping or manual iteration over a list of senders/receivers, it just moves the programming effort elsewhere (to do wacky things to try and put a constraint on the use of signals).

A simple solution to the situation described here is to establish a connection from a signal to a slot in a custom manager object that will make sure that for each device/slot/whatever only one signal can be connected (using a custom "connect" function in the manager object) and then either use QMetaObject::invokeMethod() to invoke that slot or perform dynamic connect/disconnect on the objects involved so that they are connected to the original signal directly. I think it is a matter of writing no more than 20-30 lines of code while keeping all the rules of OOP and Qt programming happy.

Santosh Reddy
2nd February 2013, 11:03
You can put the callback at either end.
How does having a callback at receiver end will help the receiver know that the signal is emitted (I mean that sender wants to send somting)?


For a slot you can't really have an equivalent because slots can be called as regular methods.
Fair reason

wysota
2nd February 2013, 12:18
How does having a callback at receiver end will help the receiver know that the signal is emitted (I mean that sender wants to send somting)?
Instead of emitting a signal, one calls a notify method in the callback.

Anyway I think my solution with an external connection manager is better as it retains the ability to both connect to the signal and to the slot for reasons other than what the OP wants while at the same time containing one-many semantics of a particular set of connections in a neat black box.

Santosh Reddy
2nd February 2013, 14:39
Ok, I think there is a missunderstanding, may be my posts was not clear enough. All my posts talk about the the snippet you posted, and not about the callback pattern in general. I know things are possible the way you said.

Anyway, I get your point.