division of labor between QML and C++
-
Hi all -
My app displays a view of equipment items, populated from a list model. When selected, a detail screen appears, from which the user can edit properties of the selected item. It's important to note here that these edits don't go directly into the model; they are used to form an HTTP PATCH message (this is done in C++). When I get a response to this message, I then update the model (the response contains the changed properties, so I don't need to store them for subsequent retrieval).
Complicating this is the fact that the equipment struct will be sub-classed (sub-structed?) many times.
I'm trying to figure out the best division of labor between QML and C++. Seems to me I have a couple choices:
- I can create a temporary equipment item in my QML, update it with the user changes, and pass it back to my C++ which handles forming and sending the HTTP request. The signature would look like this:
Q_INVOKABLE void sendPatchRequest(Equipment equipment);
The difficulty here is the subclasses; I don't know how to make my C++ "receive" anything more specific than an equipment struct.
- I can form a JS array of the changes, using the roles and values, and send them to the C++. The signature would look like this:
Q_INVOKABLE void sendPatchRequest(QUuid uuid, QList<EquipmentRoles> roles, QList<QVariant> values);
As the subclass can be determined from one of the parent class properties (the "category"), I can then form the HTTP request from this array. This is viable, but it feels like I'm trying to do too much in the QML. This is also going to be a lot of coding, both in QML and C++.
I'd prefer the first option, but I don't know how to get around the issue of preserving the subclass properties in the call. Does anyone have any other ideas on how to go about this?
Thanks...
-
@SGaist the call from QML to my model must include the temporary object, as it doesn't exist within the model (I suppose I could change this).
The ideal flow on this would be:
- QML calls the model with the changed (temporary) object
- the model invokes a method in the subclass to populate a QJsonObject
- the model forms an outgoing message from the QJsonObject
but none of this will work if the model doesn't know what subclass it is receiving, and currently I haven't been able to find a way to do this.
-
Hi,
Your equipment base class could have a virtual method that returns the patch data and all the subclasses would reimplement it. That way you can have everything implemented in C++.
-
@SGaist a couple of questions:
- how best to return the patch data from QML to C++? Which of my above options seems preferable?
- would you expect the QML --> C++ route to go through the model, or directly to the item(s) in the list? I can do it either way, but if the latter, I think I'd either need to move my message management from the model into the equipment struct, or use a signal/slot mechanism, which seems kind of stilted.
- the real problem is how to get the equipment model to recognize what subclass of Equipment it's being passed. As long as the model's function signature expects a type of Equipment, there doesn't seem to be any way to convert it to a subclass of equipment. I lose all of the subclass' properties.
Thanks...
-
@mzimmers said in division of labor between QML and C++:
Q_INVOKABLE void sendPatchRequest(Equipment equipment);
This looks very dodgy if (like you said) you have multiple Equipment classes, since here you're slicing the object.
Would seem that you should use a pointer/reference here if the runtime type of 'equipment' is a polymorphic type.
-
@mzimmers said in division of labor between QML and C++:
the real problem is how to get the equipment model to recognize what subclass of Equipment it's being passed. As long as the model's function signature expects a type of Equipment, there doesn't seem to be any way to convert it to a subclass of equipment. I lose all of the subclass' properties.
Thanks...That's what down casting is for generally speaking.
But like @SGaist said perhaps you should consider adding a virtual interface method in the Equipment class that can convert the internal state of your "equipment" into some byte representation that you can send over HTTP.
-
@SamiV123 said in division of labor between QML and C++:
But like @SGaist said perhaps you should consider adding a virtual interface method in the Equipment class that can convert the internal state of your "equipment" into some byte representation that you can send over HTTP.
This approach definitely minimizes the work that I need to do in QML, which is good. But my equipment struct has no knowledge of the model, or any of its methods. And, since it's a Q_GADGET, I can't use signals to notify the model of the need for a change.
So each instance of Equipment and its subclasses would have to handle the entire patch operation: building the list of properties to patch, creating the JSON, and sending the message. I'd greatly prefer this be handled in one location (the model).
-
@mzimmers it is good that it has no knowledge of the model. The only think that it should do is return it's internal state in a format suitable to be sent. The fact that the model will use an HTTP request or GRPC does not have any impact on your Equipment classes.
It would be something along the lines of:
void MyModel::sendSomething(int index) { Equipement *equipment = data(index, Qt::UserRole).value<Equipment *>(); qnam->post("https://mythink.com/upload/", equipment->state()); }
Where
state
would return a json representation of its state. So the equipment does not care about the model nor what will be done with the value returned by thestate
method. So you have proper separation of concerns. -
@SGaist the call from QML to my model must include the temporary object, as it doesn't exist within the model (I suppose I could change this).
The ideal flow on this would be:
- QML calls the model with the changed (temporary) object
- the model invokes a method in the subclass to populate a QJsonObject
- the model forms an outgoing message from the QJsonObject
but none of this will work if the model doesn't know what subclass it is receiving, and currently I haven't been able to find a way to do this.
-
Just to close this topic out, here's what I ended up doing (posted elsewhere as well):
// will need one of these for each subclass void EquipmentModel::sendPatchRequest(const Vsp &equipment) { sendBaseRequest(equipment); } void EquipmentModel::sendBaseRequest(const Equipment &equipment) { ... // the line below will call the override function // for the appropriate subclass. equipment.addPatchFields(listEntry, qjo, rolesToKeys);
Not super elegant, but it works. Thanks to all for the suggestions.
-