Qt の C++ から QML の function を実行する方法

QML 内に記述された JavaScript の function を C++ から呼び出してみたいと思ったことはありませんか?ありませんか?

というわけで(以前に試してみてたしかダメだったのですが、Qt 5.2 から JavaScript の実行エンジンが変わったことによって、ひょっとして今ならできるかも?と思って再度)試してみました。

exec-function-in-qml-from-cpp.pro

TEMPLATE = app
TARGET = exec-function-in-qml-from-cpp
QT = qml

SOURCES = main.cpp

RESOURCES = main.qrc

シンプルなコンソールアプリで QML の実験をするための最小構成になっています。

main.qml

import QtQml 2.2

QtObject {
    id: root
    function echo(msg) {
        console.debug(msg)
        return msg
    }
}

これが今回使用する QML ファイルで、この echo 関数を C++ から実行します。

main.qrc

<RCC>
    <qresource prefix="/">
        <file>main.qml</file>
    </qresource>
</RCC>

QML ファイルをリソースに埋め込みます。

main.cpp

#include <QtQml>

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
    QQmlEngine engine;
    QQmlComponent component(&engine);
    component.loadUrl(QUrl("qrc:/main.qml"));
    QObject *object = component.create();
    {
        const QMetaObject *metaObject = object->metaObject();
        for (int i = object->staticMetaObject.methodCount(); i < metaObject->methodCount(); i++) {
            qDebug() << i << metaObject->method(i).name();
            QVariant ret;
            qDebug() << metaObject->method(i).invoke(object, Qt::DirectConnection, Q_RETURN_ARG(QVariant, ret), Q_ARG(QVariant, QStringLiteral("test")));
            qDebug() << ret;
        }
    }
    delete object;

    QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection);
    return app.exec();
}

QQmlEngine を使用して main.qml を実行します。

作成した main.qml のオブジェクトのメタオブジェクトから main.qml 内で作成された function を取得し、実行結果をデバッグ出力しています。

実行

$ qmake
$ make
$ ./exec-function-in-qml-from-cpp
5 "echo"
qml: test
true
QVariant(QString, "test")

というわけで、C++ から QML で定義した関数を呼べることがわかりました。QVariant で返値と引数を渡してあげるのがポイントですね。