identifying signal senders
-
@mzimmers said in identifying signal senders:
I know that QNetworkAccessManager::get() returns a reply identifier, so I can keep a hash of these along with something to identify the sender, but...I don't know what to do with this in order to forward the reply back to the correct model.
This is the issue? To identify which
QNetworkReply
belongs to whichQNetworkAccessManager::get(const QNetworkRequest &request)
at a later date when processing theQNetworkReply
? You could subclass theQNetworkRequest
to add some identifier, and retrieve that fromQNetworkReply::request()
. OrQNetworkReply
is aQObject
so you could add aQObject::setProperty()
when it is returned fromQNetworkAccessManager::get()
. These approaches do not require maintain a hash.Whatever you add must be usable to identify which model the request came from. Subclassing
QNetworkRequest
could allow you add aQAbstractItemModel *
to directly identify the data model. Or as you say you could maintain some hash/mapping if necessary.Finally, if for whatever reason the manager does not have any access to the models, or the request/reply is tagged with an identifier so opaque to the manager that it cannot identify the model from it but the models themselves can identify whether it pertains to them, you could have the manager
emit
a signal with the reply on which all the models have a slot. They could then identify whether the reply is for them. Obviously a small overhead here if they all have to listen to every reply in order to see if its for them individually.I don't really see the problem?
-
@JonB said in identifying signal senders:
This is the issue? To identify which QNetworkReply belongs to which QNetworkAccessManager::get(const QNetworkRequest &request) at a later date when processing the QNetworkReply?
Sort of (this issue is tough to explain)...I can easily enough associate the reply with the get, but I'm not sure how to go about knowing to which model the reply should be forwarded.
Thinking about this a little more, it's probably better to ascertain the source of the request at the point of the request, rather than after the reply comes in. I guess I could add another parameter to my signals to the manager:
class MessageMgr : public QObject { public slots: void sendGet(QString message); // my current way void sendGet(QString message, QString model); // an alternative
When a module emits a send request, it would include a string containing its name. This would be entered into a hash, with the NetworkReply * as the key, and the model name as the value.
When the reply comes back, the message manager would look up the model in the hash, and add it to the signal that the reply came back. The signal would then go to each model, which would check for a name match before attempting to process.
This sounds like it would work, but it's hardly elegant. A variant on this theme would be to have separate signals for each model, but that's also really a hack.
I'm beginning to wonder if perhaps the signal/slot mechanism isn't the best way to approach this (a traditional callback method would actually work better in this case), but I'm not sure what to do.
I'll think a little about your subclassing ideas.
-
@mzimmers
I don't understand your complications. If you don't want hashes and strings and lookups I said you could always just store theQAbstractItemModel *
, or whateverModel *
, to access the model directly. Only you know what is and is not available to you in your code. -
@JonB I suppose that's an option. Of course, each model would have to have the same handler signature, but that's probably best practices anyway. I suppose I could take it a step further, and pass a pointer to the handler function instead of the entire object?
-
Return the QNetworkReply from MessageMgr::sendGet(), connect the appropriate model slot to QNetworkReply::finished(), and eliminate the intermediary handling of the reply.
Another alternative is QNetworkRequest::setOriginatingObject() and QNetworkReply::originatingObject().
-
@mzimmers said in identifying signal senders:
@SGaist right, but it's the same problem -- what do I do with it? How do I go from knowing the sender to invoking a method in the sender class?
Again, I don't understand. I wrote earlier for this suggestion:
Subclassing
QNetworkRequest
could allow you add aQAbstractItemModel *
to directly identify the data model.So you would have a pointer to the model as a
QAbstractItemModel *
. On which you can call (QAbstractItemModel
) methods directly. Or if your models share some other common interface you could derive them fromYourModelBase
and pass that as parameter to be able to call its methods. Or you could useqobject_cast<SpecificModelType *>
to test their type and call specific model methods.Or the approaches @jeremy_k mentions. Using
QNetworkRequest::setOriginatingObject(someModel)
saves subclassing if you don't want to do that, but it's the same approach. His direct connection offinished
signal could be used if you do not actually want/need the manager to see/handle the replies.One way or another you can either have the models send an identifier for themselves to the manager, which then routes replies to the model from that, or you can have the manager return the
QNetworkReply *
fromQNetworkAccessManager::get()
to the model and that puts e.g. a slot onQNetworkReply::finished()
to its own code. -
@JonB said in identifying signal senders:
His direct connection of
finished
signal could be usedif you do not actually want/need the manager to see/handle the replies.The struck-out portion is inaccurate. The manager can view the reply status via any of the const methods, or read content via QIODevice::peek() without interfering with later connections to the finished or readyRead signals.
-
@jeremy_k
Yes, but that's not what's meant. It's a question of whether the OP wants the replies routed through the manager, possibly to choose whether to send on to the models or to ignore/not do so, or whether the model receives thefinished
regardless and acts on it. I said "could" be used if OP does not want manager to see/handle the replies, not that it would preclude the manager seeing the responses as you wrote. As you said, not attaching thefinished()
signal to the model to "eliminate the intermediary handling of the reply" means that it will be attached and handled by the manager as "intermediary". Depends what the OP wants, -
@JonB said in identifying signal senders:
@jeremy_k
Yes, but that's not what's meant. It's a question of whether the OP wants the replies routed through the manager, possibly to choose whether to send on to the models or to ignore/not do so, or whether the model receives thefinished
regardless and acts on it. I said "could" be used if OP does not want manager to see/handle the replies, not that it would preclude the manager seeing the responses as you wrote. As you said, not attaching thefinished()
signal to the model to "eliminate the intermediary handling of the reply" means that it will be handled by the manager as "intermediary".Fair enough. I had interpreted "see/handle" as non-exclusive.
To me, a manager that inhibits what the model sees from a successful request is acting as part of the model. HTTP and QNetworkReply has error codes to indicate lack of success. -
Feels like the issue has little to do with the network request.
The issue is that the manager doesn't keep state.
So, if model X makes a request to the manager which is then forwarded to get an async reply. What you want to do is to ignore the fact on how its implemented and simply do what all async APIs do. Create a new QObject that emits a signal when its got a data update.
So what you see, as an example API, networkmanger::get() do, they return an object with a bunch of signals.
What you want to do is make you message-manager do the same. This means it needs to keep state. Which means it needs to route messages only back to the object it actually owns. An equivalent to the NetworkReply, maybe called SignalManagerReply...
The fact that the message-manager itself uses the network manager privately is a nice parallel you can probably use to mirror the workflows.
Edit; oh, and want to add that your initial statement of a 'model' asking he signal-manager something is breaking the model-view-controller design. You need a controller to handle an async API like the signalmanager would have (as a result of it using networkmanager).
Such a controller-style class is where the SignalManagerReply is used.
-