Sigmatic
Sigmatic

Reputation: 173

QML engine doesn't import C++ model correctly

Overview

I'm working on a project to learn how to implement C++ models in a QML project and I've ran into an issue that I can't quite figure out. I believe I've implemented the model correctly since I can manipulate it on the C++ side without issue, but I'm doing something wrong trying to expose it to the QML engine.

What I'm trying to do:

After doing the above, the project runs but nothing is displayed in the window. Then I tried to run main.qml using Qt Creator's tool QML Utility tool. Again, it runs but nothing is displayed. In the General Messages tab I get the message:

Starting external tool "/path/to/Qt/6.3.1/gcc_64/bin/qml /path/to/project/main.qml"
file:///path/to/project/main.qml:23: ReferenceError: cellModel is not defined

Some Context For The Curious

The project is to implement Conway's Game of Life as a simple proof of concept to prepare for a more complex future project. Behind the scenes, the C++ model CellModel uses class Environment which manages a 2D list of pointers to instances of class Cell which handle updating at timed intervals and holding the state values respectively.

Relevant Code

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

#include "cellmodel.h"


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

    CellModel model = CellModel(nullptr);

    model.resize(40, 40);
    model.fillWithSoup();

    QQmlApplicationEngine engine;

    QQmlContext* context = engine.rootContext();
    context->setContextProperty("cellModel", &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();
}

cellmodel.h

#include <QAbstractItemModel>
#include <QDir>
#include <QHash>
#include <QByteArray>

class Environment;
class EnvironmentBuilder;

class CellModel : public QAbstractItemModel
{
    Q_OBJECT

public:

    enum Roles {
        Living = Qt::UserRole + 1
    };
    Q_ENUM(Roles)

    explicit CellModel(QObject *parent = nullptr);

    Environment* environment() const {return m_environment;}
    void setEnvironmefnt(Environment* environment);

    int rowCount(const QModelIndex &parent = QModelIndex()) const override;
    int columnCount(const QModelIndex &parent = QModelIndex()) const override;
    QVariant data(const QModelIndex &index, int role) const override;
    bool setData(const QModelIndex &index, const QVariant &value,
                 int role = Qt::EditRole) override;
    QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const override;
    QModelIndex parent(const QModelIndex& index) const override;
    QHash<int, QByteArray> roleNames() const override;
    Qt::ItemFlags flags(const QModelIndex &index) const override;

signals:
    void stateUpdated();

public slots:
    Q_INVOKABLE void load(QString filename = "save.gol");
    Q_INVOKABLE void save(QString filename = "save.gol");
    Q_INVOKABLE void setSaveDirectory(QString directory = QDir::current().path());

    Q_INVOKABLE void clearAll();
    Q_INVOKABLE void fillWithSoup(qreal density = 0.4);

    Q_INVOKABLE void nextStep();

    Q_INVOKABLE void resize(int height = 1, int width = 1);

private:
    EnvironmentBuilder* m_builder;
    Environment* m_environment;
};

main.qml

import QtQuick 2.15
import QtQuick.Window 2.15

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

    GridView {
        property real cellScale: 10

        implicitHeight: 400
        implicitWidth: 400

        anchors.horizontalCenter: parent.horizontalCenter
        anchors.verticalCenter: parent.verticalCenter

        cellHeight: cellScale
        cellWidth: cellScale

        model: cellModel

        delegate: Rectangle {
            border.width: 1
            border.color: "black"
            color: model.living ? "#cacaca" : "#424242"
        }
    }
}

What I've Tried

To check whether I made some error in subclassing QAbstractItemModel, I made a simpler model inheriting QAbstractListModel and tried using it instead only to face the same results. So the issue must lie in how I've tried exposing the model to the QML engine, but the setContextProperty() function seems so straightforward I fail to see how I could be misusing it.

Upvotes: 2

Views: 312

Answers (1)

Sigmatic
Sigmatic

Reputation: 173

It turns out that I misunderstood my issue! As @Amfasis pointed out, a TableView component would be more appropriate for my application. I changed the GridView component accordingly and everything works. I was exposing the model to QML correctly the whole time.

Also of note: In my question text I mentioned that I ran main.qml in the QML Utility tool and got a ReferenceError. However, I've realized that the QML Utility tool would always read the file without my model being in the QML context since I exposed it in main.cpp which the tool doesn't read.

Upvotes: 2

Related Questions