简体   繁体   中英

How to pass data from QML to C++ as a mutable reference

I have a Q_INVOKABLE method in C++ class and I want this method to return data back to QML, within the same method, without the use of signals.

My mehtod is declared as:

Q_INVOKABLE void select_company(int index,QString *out);

And defined as:

void Companies::select_company(int index,QString *out) {

    out->clear();
    out->append("out string");
}

When I call it from a JavaScript function in QML:

var out_str;
data_model.select_company(index,out_str);
console.log(out_str);

I get this output on the console:

qrc:/CompaniesList.qml:56: Error: Unknown method parameter type: QString*

Is it possible to pass a (JavaScript) variable from QML to C++ method, and have C++ to modify this variable ? If it is not possible to do that by passing a pointer or reference, what is the (simplest) other approach to do this?

The only way I have found so far is by passing a QJSValue and set properties to it, like this:

Declaration:

Q_INVOKABLE void select_company(int index,QJSValue out);

Definition:

void Companies::select_company(int index,QJSValue out) {

    out.setProperty("company_name","Acme, Inc.");
    out.setProperty("identity_id",29673);
}

QML:

var retval={};
data_model.select_company(index,retval);
console.log(retval.company_name);
console.log(retval.identity_id);

Would be interesting to find out all the possible ways to call a C++ method and return some data immediately.

If you want to "pass by reference" and edit a JavaScript object on the C++ side, then QJSValue is the way to go. There is no direct support for C++ types out of the box, there are some conversions for specific types.

Passing as a pointer will only work for QObject derived objects.

Keep in mind that QJSValue will work only if it is an Object , maybe also an array. It will not work for other types, as the object will be copied, and changes will be done on the copy and will not be reflected on the original object.

Not that I mind, but what stops you from declaring something like:

Q_INVOKABLE QString getSomeValueFromCPP(int index) {
 ...
 return value; 
}

and then in QML:

var val = getSomeValueFromCPP(index);

For more complicated structures if you need to return something by-value I suggest to have a look on Q_GADGET approach, it's awesome.

PS as there are so many comments I decided to make a small update in here rather then in comments.

First of all, I want to give a credits to all ddriver comments as they are all completely right.

It's absolutely true that first thing to do is to review a design, as any QML/C++ interactions are not really consider such approach as mentioned in the initial question. Second is that it's very important to make a distinguish between ownership for QObject based classes and passed-by-value instances. In case you are going to use a QObject based classed (doesn't matter QML or C++ ownership) you should clearly decide who's going to be a creator of these objects. Also pay attention that any QObjects created in C++ code without an explicit parent and passed into QML engine will be destroyed at some point by garbage collector. Thats very flexible and powerful approach allows to create very efficient data structures like QObject-based models with full properties support etc.

To avoid complications Qt provides another way of dealing with structured data in case you need just to pass values into QML from C++ (as I understand a question that's what you are trying to achieve). That's Q_GADGETS. Thats a similar to Q_OBJECT way of attaching metadata to structures allowing them to have a Q_PROPERTIES, full MetaType information (so qobject_cast will work for example, as well as any QVariant transformations), but excluding SLOTS/SIGNALS.

Major difference is that you are allowed to make a pass-by-value invocations, where you should't bother (to some point) about ownership as well as live-cycle. In simple words in case of Q_GADGET return type QML will get a COPY of initial object and will destroy it then it's not needed.

As mentioned in ddriver answer it's possible to simulate reference parameter then calling C++ method from QML, but I have my doubts it's right way to go as from my point of view it's lack in design.

In your question you are asking about certain technique, but point is that - proper answer is to review a design as what you are trying to do is not how it's supposed to work. Again as ddriver mentioned you should look on C++ part as on "core" providing data in certain way - it can be QObject based models/standalone objects or value-based instances based on Q_GADGET, while QML part is a "representation" logic, it shouldn't be used to create/hold something which C++ suppose to modify, especially by reference.

Ragaring Nulik comments - typical use case is to return Q_GADGET based classes into QML from C++. You shouldn't try to pass those values by reference for modifications inside C++ code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM