C++ class as qml module in Qt Design Studio

Dear Toradex community,

I am doing my first steps with Qt on a Toradex SOM. I setup a demo QT QML sample project via the Toradex extension for VS Code.
To edit the qml and ui.qml files I am using the Qt Design Studio.

To interact with some business logic I setup a counter.cpp class.
I am able to compile and run the following code on the SOM:

Code

Counter.h

// File Counter.h
#ifndef COUNTER_H
#define COUNTER_H


/* === INCLUDE FILES =============================================================== */
#include <QObject>
#include <qqml.h>


/* === CLASS ======================================================================= */
class Counter : public QObject
{
Q_OBJECT
QML_ELEMENT
public:
    explicit Counter(QObject *parent = 0);
    Q_INVOKABLE void IncrementCounter();

signals:

public slots:

private:
    int m_counter;
};

#endif

Counter.cpp

// File Counter.cpp
#include "Counter.h"
#include <QDebug>
 
Counter::Counter(QObject *parent) :
QObject(parent), m_counter(0)
{
}

void Counter::IncrementCounter() {
   m_counter++;
   qDebug() << "value: " << QString().number(m_counter);
}

HelloWorldQml.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QDebug>
#include "Counter.h"
 
int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QGuiApplication app(argc, argv);

    // Register Counter with version 1.0
    qmlRegisterType<Counter>("Counter", 1, 0, "Counter");
 
    QQmlApplicationEngine engine;
    const QUrl url(QStringLiteral("qrc:/mainwindow.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();
}

HelloWorldQml.pro

TEMPLATE = app

TARGET = HelloWorldQtQml

HEADERS = HelloWorldQtQml.h \
        Source/Counter.h

SOURCES = HelloWorldQtQml.cpp \
        Source/Counter.cpp

# $$PWD refers to folder that contains the .pro file
INCLUDEPATH += $$PWD/Source

DESTDIR = $$(QMAKE_DESTIDIR)

QT+= core quick qml

RESOURCES += qml.qrc

# Specifies the directory where all intermediate objects should be placed
OBJECTS_DIR = ./build
MOC_DIR = ./build
UI_DIR = ./build

So far so good.

Now my Qt Design Studio does not recognise this Counter module:

Screenshot from 2022-06-23 11-53-13

I assume that I have to register this module.

If I try to continue my .pro file as described here:
https://doc.qt.io/qt-5/qtqml-cppintegration-definetypes.html

Continued project file
TEMPLATE = app

TARGET = HelloWorldQtQml


CONFIG += qmltypes
QML_IMPORT_NAME = counter
QML_IMPORT_MAJOR_VERSION = 1


HEADERS = HelloWorldQtQml.h \
        Source/Counter.h

SOURCES = HelloWorldQtQml.cpp \
        Source/Counter.cpp

# $$PWD refers to folder that contains the .pro file
INCLUDEPATH += $$PWD/Source

DESTDIR = $$(QMAKE_DESTIDIR)

QT+= core quick qml

RESOURCES += qml.qrc

# Specifies the directory where all intermediate objects should be placed
OBJECTS_DIR = ./build
MOC_DIR = ./build
UI_DIR = ./build

it ends in the following error:

/usr/lib/qt5/bin/moc --collect-json -o helloworldqtqml_metatypes.json build/moc_Counter.cpp.json
Error opening build/moc_Counter.cpp.json for reading
make: *** [Makefile:432: helloworldqtqml_metatypes.json] Error 1
The terminal process "/bin/bash '-c', 'make'" failed to launch (exit code: 2).

What is missing to import and use this counter.cpp module in my qml file?
How do I “link” the counter.cpp module with QML_IMPORT_NAME in the .pro file?

My VS code folder looks like this:

Project directory
.
├── appconfig_0
│   ├── config.yaml
│   ├── id_rsa
│   ├── id_rsa.pub
│   ├── sdkfiles
│   │   ├── core.py
│   │   ├── __init__.py
│   │   └── typeinfo.py
│   └── work
│       ├── debug.tar
│       ├── docker-compose.yml.debug
│       ├── Dockerfile.debug
│       ├── Dockerfile_SDK.debug
│       └── HelloWorldQtQml
│           └── HelloWorldQtQml
├── build
│   ├── Counter.o
│   ├── HelloWorldQtQml.o
│   ├── moc_Counter.cpp
│   ├── moc_Counter.o
│   ├── moc_predefs.h
│   └── qrc_qml.o
├── HelloWorldQtQml.cpp
├── HelloWorldQtQml.h
├── helloworldqtqml_metatypes.json
├── HelloWorldQtQml.pro
├── mainwindow.qml
├── Makefile
├── qml.qrc
├── qrc_qml.cpp
└── Source
    ├── Counter.cpp
    └── Counter.h

I am using:

  • QMake version: 3.1
  • Using Qt version: 5.9.5 in /usr/lib/x86_64-linux-gnu
  • Visual Studio Code: 1.68.1
  • Toradex Torizon Support extension (v1.4.0)
  • Qt Design Studio 1.6.1
  • iMX8M Mini Dual Light
  • Verdin carrier board

Thank you in advance and best regards,
Zag

There are 3 rules to success with Qt no matter what platform.

  1. Never use QML for anything, it’s a buggy feeble creature.
  2. Never use QML for anything, it’s slower than molasses dripping in deep winter.
  3. Never use QML for anything, it’s unstable, every minor release forces you to rewrite everything.

You can look at these two blog posts and pull the source down via the links. (I didn’t check, the first one might have the link in either the previous or next post in the series if you don’t find it in that post.)

You can’t pass a raw QObject to QML. It has to be a Q_GADGET at least.

Never ever use “Counter.” There is soooo much stuff named counter in the Qt code base you can really get clobbered by that. Part of the garbage collection Qt provides is done by reference counting for QObjects.

What version of Qt are you using? QML_ELEMENT is rather recent (see previous comment about QML being unstable). The buildable runable examples in the two links I provided were using Qt 5.x pre-QML_ELEMENT.

CONFIG += qmltypes
QML_IMPORT_NAME = Counter
QML_IMPORT_MAJOR_VERSION = 1

Even if it is not your only problem, the import name needs to match the class name. You can’t have “Counter” as the class name and have the import name be “counter”