Home > front end >  Displaying a C class via QML in QtQuick2
Displaying a C class via QML in QtQuick2

Time:03-19

I am working on a QtQuick 2 application (Qt version 6.2.3). I created one C class (let's call this class "Example") that contains the data that my application should deal with. This class can be instantiated several times, representing different datasets to be displayed.

class ExampleObject : public QObject {
    Q_OBJECT
    Q_PROPERTY(QString property1 MEMBER property1 CONSTANT)
    ...

    public:
    QString property1;
};

Q_DECLARE_METATYPE(ExampleObject*)

I want to be able to display instances of this class via QML, therefore I created a "Example" custom component with a property pointing to the Example C object containing the data I want to display.

ExampleComponent {
    property var exampleCppObject // exampleCppObject is a pointer to an instance of ExampleObject

    Label {
        text: exampleCppObject.property1
    }
}

To be able to change the Example instance used by the QML component, I created functions to "reinitialize" and "update" the component:

ExampleComponent {
    property var exampleCppObject // exampleCppObject is a pointer to an instance of ExampleObject
    property string textToDisplay

    function update() {
        textToDisplay=Qt.binding(() => exampleCppObject.property1);
    }

    function reinitialize() {
        textToDisplay=""
    }

    Label {
        text: textToDisplay
    }
}

I call these functions after changing or deleting the Example object pointed by ExampleCppObject, and this works quite fine. But I feel like this isn't best practice, and it seems to me that I am doing things wrong.

What are better ways of connecting C to QML, in the situation I described?


Edit: my main.cpp essentially consists in:

MainModel mainModel;
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("mainModel", &mainModel);
engine.load(QStringLiteral("qrc:/main.qml"));

Where mainModel is an object which can create different instances of ExampleObject while the application is running.

CodePudding user response:

You can optimize the binding textToDisplay such that you don't have to call the upate and reinitialize functions, which seems to be the question you are after:

property var exampleCppObject
property string textToDisplay : exampleCppObject ? exampleCppObject.property1 : ""

In case you need more complex logic in the future, you can also use braces:

property string textToDisplay: {
    console.log("log me everytime the binding is reevalutated")
    if(condition1)
       return "invalid"
    else if(condition2)
       return exampleCppObject.property2
    else
       return exampleCppObject.property1
}

The best part of this, is that QQmlEngine actually reevaluates the binding for every property that is used in this binding (which has a notify signal), so if crafted correctly, you can largely leave the binding alone (meaning you don't need the update and reinitialize function)

CodePudding user response:

For connecting c class in QML : This is one example :

  1. First you have one c class, you should use Q_PROPERTY if you want to use class objects in qml READ and WRITE is for its get and set function and NOTIFY for class signals. If you write one function and you want to use it in QML you should use Q_INVOKABLE.

This is my c class and I want to use it in QML :

#ifndef TEST_H
#define TEST_H

#include <QObject>


class Test: public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString userName READ userName WRITE setUserName NOTIFY userNameChanged)

public:
    explicit Test(QObject *parent = nullptr);

    const QString   & userName() const;

    void              setUserName(const QString &newUserName);

    Q_INVOKABLE void  myFunc();

signals:
    void              userNameChanged();

private:
    QString  m_userName;
};

#endif // TEST_H

The important part is that in main.cpp you use qmlRegisterType I didn't see that you do this in your code and your problem maybe because of this.

in main.cpp:

#include <QApplication>
#include <QQmlApplicationEngine>

#include "test.h"

int  main(int argc, char *argv[])
{
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif
    QApplication  app(argc, argv);
    qmlRegisterType<Test>("Test", 1, 0, "Test");

    QQmlApplicationEngine  engine;
    auto                   root_context = engine.rootContext();

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    if (engine.rootObjects().isEmpty())
    {
        return -1;
    }

    return app.exec();
}

and in main.qml:

import QtQuick 2.15
import QtQuick.Window 2.15
import Test 1.0
Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")

    Test{
        id:tst
        userName: "Parisa"


    }
}

I import Test 1.0 and create one Test object and I have access to all its members and functions and signals.

  • Related