0
votes

How do you pass a pointer to string property from QML to C++?

When I attempted to do it the obvious way, I got this error:

qrc:/NewAccount.qml:236: Error: Unknown method parameter type: QString*

Which means, QML engine can't convert new_account.error_string property to C++ when calling save_account (Q_INVOKABLE) method

This is my code in QML:

import myproject.aewm.ethkey 1.0
import myproject.aewm.walletaccount 1.0
...
id: new_account
property EthKey key: EthKey{}
property WalletAccount account: WalletAccount{}
property string error_string: ""
....
if (aewm_obj.save_account(key,account,new_account.error_string)) {
    wallet_accounts_tabbar.currentIndex=0
} else {
    console.log("error occurred:"+new_account.error_string)
}

Where aewm_obj is a C++ type (registered in QML) and save_account is declared in C++ as :

Q_INVOKABLE     bool save_account(EthKey *key, WalletAccount *account, QString *err_str);

The docs say that a (QML) string property is a QString object, and that these types are automatically converted from from C++ and QML . I am passing pointers to my custom QObject-derived classes without any problems after qmlRegisterType() call, so why I can't do the same with strings?

I thought that maybe string pointers are not supported and I tried to add this line:

qRegisterMetaType<QString*>("QString*");

but after this change, the pointer I received at C++ side was 0x0 and I got a segfault.

So, how do you pass pointers to QML string properties from QML to C++ ?

Or, do you think I should register QString class with qmlRegisterType() too? I tried it but I have encountered some compilation issues, so I think it won't compile.

The last solution would be to create a custom object with a QString inside, and send a pointer to it from QML to C++. But, this would be an overkill, if QString exists why not find a way to use it?

Will appreciate very much your comments. I definitely want to use pointers , it is safer than dealing with object ownership when exchanging data between C++ and QML.

2
Why do you want to spend a pointer and not just the value?eyllanesc
QML can only pass QObject pointers, in the case of QString only passes the copied value, why does it say that it is safer to pass pointers?eyllanesc
@eyllanesc, because I am returning the error , the C++ code sets the error and QML receives itNulik
@eyllanesc, QML can pass pointers to objects, if you do qmlRegisterType() on an class, you can pass a pointer to its objects from QML to C++ without any problem. I am doing this all the time. But it only works with my custom classes, it doesn't work with QString. But why ? Is there a way to make QString pointers to work like other class pointers?Nulik
qmlRegisterType() works only with QObject subclasses, and QString ain't that. And it is only so that you can create the objects declaratively. Regular QObjects can be perfectly accessed from QML without registering. And QString is already automatically converted to a string on the QML side. Supporting QObjects via pointers is just an auxiliary, it doesn't mean QML supports pointers, it does not.dtech

2 Answers

2
votes

As I said in the comments, QML only passes pointers from the QObjects, and QString is not a QObject.

I think you are giving an incorrect approach to the problem, you could create a property in the object that performs the calculations that have the error message as shown below.

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>

class Foo: public QObject{
    Q_OBJECT
    Q_PROPERTY(QString errorMessage READ errorMessage)
public:
    using QObject::QObject;
    Q_INVOKABLE bool process(int a, int b, int res){
        bool status;
        // some operation
        status = (a+b) == res;
        mErrorMessage = status? "": QString("error message: %1 + %2 is different to %3").arg(a).arg(b).arg(res);
        return status;
    }
    QString errorMessage() const{
        return mErrorMessage;
    }

private:
    QString mErrorMessage;
};

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    qmlRegisterType<Foo>("com.eyllanesc.org", 1, 0, "Foo");

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

#include "main.moc"

main.qml

import QtQuick 2.9
import QtQuick.Window 2.2
import QtQuick.Controls 1.4

import com.eyllanesc.org 1.0

Window {
    visible: true
    width: 640
    height: 480

    Foo{ id: foo }

    Row{
        SpinBox{ id: a }
        SpinBox{ id: b }
        SpinBox{ id: c }

        Button{
            text: "process"
            onClicked: {
                var message;
                if(foo.process(a.value, b.value, c.value)){
                    message = "correct"
                    console.log("successful")
                }
                else{
                    message = foo.errorMessage
                    console.log("error is "+ message)
                }
                txt.text = message
            }
        }
        Label{ id: txt }
    }
}
1
votes

With Qt, the C++ API and the QML API are completely different, and practically incompatible layers. There is a lot of conversion of data back and forth in order to make the whole thing work. And you don't really have control over it when it comes to primitives like strings. So just get that idea out of your head.

If you want to access a particular property of a particular object, you need to pass the object that will be received as a pointer, and the property name, which then you can access via its name string via QObjects property() and setProperty().

But in your case that is entirely redundant, simply pass the string itself.