14

In C++ I have a class with an invokable function, what I would like to do is call that method from QML/Javascript (this I've gotten to work) and pass it a Javascript callback.

In code, I define my class like:

class MyObject: public QObject
{

Q_OBJECT

public:
    Q_INVOKABLE void doSomething(quint64 x, /* what goes here? */ jsCallback)
    {
        x += 1;

        // I suspect this will require a invocation mechanism but 
        // this shows what I'd like to do
        jsCallback(x);
    }
};

And in my QML, I would like to do something like:

Rectangle {

    function myCallback(x){
        console.log("x=" + x);
    }

    MouseArea{
        anchors.fill: parent
        onClicked:{
            myObject.doSomething(2, myCallback);
        }
    }
}

So that when I click on the Rectangle, I would see x=3 in the console. How would I define the parameter in C++ and invoke the callback in order to accomplish this?

Thank you!

BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
Addy
  • 2,414
  • 1
  • 23
  • 43

1 Answers1

17

I think I've figured this out. What I ended up doing was implementing this in my C++ class like such:

class MyObject: public QObject
{

Q_OBJECT

public:
    Q_INVOKABLE void doSomething(quint64 x, QJSValue jsCallback)
    {
        x += 1;

        QJSValue val = jsCallback.engine()->newObject();
        val.setProperty("x", x);

        jsCallback.call(QJSValueList { val });
    }
};

And then I can access the value in my callback like:

function myCallback(x){
    console.log("x=" + x.x);
}
BaCaRoZzo
  • 7,502
  • 6
  • 51
  • 82
Addy
  • 2,414
  • 1
  • 23
  • 43
  • Also if you use in lambda function inside function (for example, after network reply), pass QJSValue as mutable \ by value. – Alex May 04 '17 at 17:36
  • Can anyone clarify if this is considered good practice? I'm facing a similar problem where I want to push an Item onto a StackView but only after an asynchronous operation (called from QML, implemented in C++) is finished – tomalbrc Mar 12 '20 at 16:54
  • 1
    As of now `QJSValue::engine()` is deprecated but there's no suggested replacement in the Qt documentation. I'd be great to have an updated answer accounting for this. – user2580621 Apr 21 '20 at 19:09
  • Since Qt5.5, assuming `MyObject` is being invoked from a script running in a `QJSEngine` (or `QQmlEngine`), the current engine instance can be obtained with `qjsEngine(this)` (or `qmlEngine(this)`). https://doc.qt.io/qt-6/qjsengine.html#qjsEnginex – Maxim Paperno Dec 17 '22 at 13:33