arnaudm
arnaudm

Reputation: 157

How to correctly pass Object from C++ to QML

I'm currently practicing the QML and I need to pass several data from the C++ to the Qml.

The aim of my "project" is to display data which were red from an external JSON file.

Example:

{
    "entries":
    [
        {
            "title": "test",
            "description": "desc",
            "command": "blabla"
        },
        {
            "title": "test2",
            "description": "desc2",
            "command": "blabla2"
        },
    ]
}

My first try was to parse and create a basic model like :

class Configuration : public QObject {
    Q_OBJECT
public:
Q_INVOKABLE QString title(void) { return _title; }
Q_INVOKABLE QString description(void) { return _description; }
Q_INVOKABLE QString command(void) { return _command; }

void setTitle(const QString & title) { _title = title; }
void setDescription(const QString & desc) { _description = desc; }
void setCommand(const QString & command) { _command = command; }
...
private:
QString _title;
QString _description;
QString _command;
...

and to pass this model with _engine->rootContext()->setContextProperty("itemsModel", _configuration);

(_engine is of type : QQmlApplicationEngine)

But with this solution, I'm not able to call these methods because this class has a "custom" type (not recognized from the Qml).

So I'm searching a way to create and to properly share my data from the C++ to the QML :)

Upvotes: 1

Views: 1463

Answers (1)

eyllanesc
eyllanesc

Reputation: 244282

The logic is to convert the json into a Qt model (a class that inherits from QAbstractItemModel) and that can be done from:

  • QML:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QVariant>

int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif

    const QString data = R"({
    "entries": [{
            "title": "test",
            "description": "desc",
            "command": "blabla"
        },
        {
            "title": "test2",
            "description": "desc2",
            "command": "blabla2"
        }
    ]
})";

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("jsonData", data);

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}
import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    ListView{
        id: view
        model: listModel
        anchors.fill: parent
        delegate:Text{
            width: parent.width
            text: model.title + " " + model.description  + " " + model.command
        }
    }
    ListModel{
        id: listModel
        dynamicRoles: true
    }

    Component.onCompleted: {
        var o = JSON.parse(jsonData);
        var entries = o["entries"];
        for(var i in entries){
            var entry = entries[i]
            listModel.append(entry)
        }
    }
}
  • C++:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QStandardItemModel>
#include <QVariant>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>

class EntryModel: public QStandardItemModel{
public:
    enum EntryRole{
        TitleRole = Qt::UserRole,
        DescriptionRole,
        CommandRole
    };
    EntryModel(QObject *parent=nullptr): QStandardItemModel(parent){
        QHash<int, QByteArray> roles;
        roles[TitleRole]= "title";
        roles[DescriptionRole]= "description";
        roles[CommandRole]= "command";
        setItemRoleNames(roles);
    }
    void append(const QString & title, const QString & description, const QString &command){
        QStandardItem *item = new QStandardItem;
        item->setData(title, TitleRole);
        item->setData(description, DescriptionRole);
        item->setData(command, CommandRole);
        appendRow(item);
    }
};

int main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif

    const QByteArray data = R"({
    "entries": [{
            "title": "test",
            "description": "desc",
            "command": "blabla"
        },
        {
            "title": "test2",
            "description": "desc2",
            "command": "blabla2"
        }
    ]
})";

    QGuiApplication app(argc, argv);

    EntryModel model;

    QJsonDocument doc = QJsonDocument::fromJson(data);
    QJsonArray entries = doc["entries"].toArray();
    for(const QJsonValueRef & r: entries){
        QJsonObject entry = r.toObject();
        model.append(entry["title"].toString(), entry["description"].toString(), entry["command"].toString());
    }

    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("entryModel", &model);

    const QUrl url(QStringLiteral("qrc:/main.qml"));
    QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                     &app, [url](QObject *obj, const QUrl &objUrl) {
        if (!obj && url == objUrl)
            QCoreApplication::exit(-1);
    }, Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}
import QtQuick 2.12
import QtQuick.Window 2.12

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    ListView{
        id: view
        model: entryModel
        anchors.fill: parent
        delegate:Text{
            width: parent.width
            text: model.title + " " + model.description  + " " + model.command
        }
    }
}

Upvotes: 1

Related Questions