Unsolved Callback-Slot mechanism
-
Hey there,
I am currently writing an application which controls several motors/sensors and with a small UI/HMI for that. I have some kind of an fundamental question about how to achieve a nice callback-mechanism.
Right now I have a set of controller-classes which handle my low-level business-logic. These controllers are called by several QObjects using signal/slot mechanisms. To model my process I am currently evaluating QStateMachine-framework using Slots for &QState::entered-signals which then call my low-level controller-classes.
To report the sucess/failure of any action I want my controller-classes to emit a signal which should be connected to a slot of that calling code. For example I would have my QStateMachine wait in a state until the controller-class emits some 'sucess' signal.
One solution I might think of is, that my controller-classes each have only one slot left which accept some QObject as parameter. That QObject itself would have some information on what method was requested (QString?! ) and a signal that has to be emittet on completition/failure.
Is there any best-practice for that? -
Are you asking about a best practice for encapsulating a message in a system?
The only best practice I can think of is modeling your system in a state diagram to make sure you capture the different states your system can be in. As far as best practice for a messaging system there are probably dozens of ways people do that. Sometimes a string works, sometimes you need more data and encapsulating that data in a specialized object works. It all depends upon your requirements needed after you model your system. So having a better understanding of your inputs and outputs might decide where to go next.
Is this answer vague enough? ;-)
-
- Controllers are interacting with sensors ? Are the qobjects ? One sensor maps to one cotroller ?
- How the are related to UI ?
- From where do u want call back ?
- UI to sensors and Sensors to UI communication exists ?
May be some clear details will help.
-
Hm, not quite what I was thinking of I guess.
I don't want to call every slot in a synchronous way (i.e. BlockingQueuedConnection) just to be able to get the information that is returned/generated by this slot. For simplicity this might be just 'true' or 'false' or 'Success' or 'Failure'.
So I have my controllerObject ControllerA with some slotvoid ControllerA::doSomeThing()
ProcessA is another QObject which invokes this slot. To be able to get some information back, I would create a signal on ControllerA and a corresponding Slot on ProcessA. ControllerA uses a StateMachine to save its current state and triggers a transition (using a private signal) when its own Slot is invoked. This might work in a very basic approach, but things will get inconsistent when another QObject ProessB also invokes ControllerA::doSomething() because ControllerA would again emit a Signal which will then invoke the Slot on ProcessA (and ProcessB). Or I use QMutex, but this is as good as running synchronous.
To circumvent this problem I want that callback-mechanism, i.e. to be able to control which Slot gets invoked.
Another example might be a simple Dialog with 2 Options (Yes, No) in the UI. The dialog would be invoked via Slot which is connected to many Signals in the backend. How would I get the result of that dialog (Y/N) back to the calling object only?
-
- Yes, Controllers are interacting with IO, sensors and actors mixed and yes, they are QObjects
- If some errors occur, I think it is nice to report the UI. Of cource the controller could just report the error to the QObject managing the process, but there is the same problem here
- I want to callback from a invoked slot.. Maybe from some controller-Slot ControllerC::StartMotorRPM(int rpm). The callback should then report some sucess or error-code
- Ui to sensors existis, how to implement they way Sensor to UI is part of my question
-
@kain said in Callback-Slot mechanism:
but things will get inconsistent when another QObject ProessB also invokes ControllerA::doSomething() because ControllerA would again emit a Signal which will then invoke the Slot on Process
Slots are not executed at the same time. The event loop takes one signal from the queue and executes connected slots one after the other.
-
@kain
I think, I get what you try to do, I'm however not entirely sure.I had something similar last year. A program that controls a couple of motors, each basically the same , just a different io-port.
So I ended up creating one base class with a enumeration about the motor type During initialization(construction) you can assign the instance the correct enum value.
And each signal(from the base class) than also transmit its own typ to the motor-manager class for a clear differentiation. -
@J.Hilk
Yes, this is kind of the application that I am developing. Another example is my ControllerA class beingt used my different other QObjects: UI, some NetworkSocket, another ControllerB, etc. So if my ControllerA emits a signal, I just want the corresponding QObject that made the Request to be 'signalled'. That is, if my ControllerA has some Slot invoked by a NetworkSocket-QObject, I do not want any slot invocation on my UI or my ControllerB about that but only on that NetworkSocket-QObject.
One idea I might follow is to create some callback-QObject likeclass RequestCallback : public QObject { Q_OBJECT public: explicit Request(QObject *parent = nullptr); signal: void someSignal(int returnCode); }
With that method I could create one such RequestCallback object everytime I want to invoke somethink and pass that QObject to that invoked slot. The slot could just 'emit someSignal(0)' for example.
This looks to me like some kind of workaround because I will probably need a several RequestCallback-objects. Or are there any better ideas? -
@kain said in Callback-Slot mechanism:
This looks to me like some kind of workaround because I will probably need a several RequestCallback-objects. Or are there any better ideas?
This makes me think of some sort of Notification Center.
Some objects emit a notificarion and other objects register themselves to receive that notification.
For example, with the preferences Dialog, when the user click OK, the prefDialog emits a PrefsDidChanged notification. Each windows or views that need to be informed of any changes register itself to receive that notification.
I'm using custom events to emit this notification.EventCenter().addObserver(this, Event_AppPrefsDidChange);
This line register 'this' to receive the AppPrefsDidChange notif.
The notificationCenter post the event:QApplication::postEvent(o.observer,new CustomEvent(eventType,sender));
And in the receiver:
void TextDocWindow::customEvent(QEvent* event) { CustomEvent* ev =static_cast<CustomEvent*>(event); switch (ev->subType()) { case Event_AppPrefsDidChange: Log("Prefs changed"); setupFromPrefs(); break; } }
Here it's a text editor, the prefs changes can be color of text, size of font. etc.
In the custom event object, you can pass any information you need to the receiver:class CustomEvent : public QEvent { public: CustomEvent(uint type,QObject* sender); virtual ~CustomEvent() { } QObject* sender() { return oSender; } uint subType() { return vSubType; } private: QObject* oSender; uint vSubType; };
It's possible to receive a type of notification from a particular object only.
void addObserver(QObject *observer, uint eventType, QObject* sender=0);
Here it is the sender extra parameter.
VoilĂ , just an idea, hope it can help you.