0
votes

In my program I have a tree-like architecture of objects that I need to access from the QML. I cannot figure out how can I create a Q_PROPERTY getter function, that would allow me to access the item at the Index of my choosing. The function looks like this in the C++ part:

/**
 * @brief   Risk object getter function.
 * @param   index: The index of the risk from the \ref m_risks container.
 * @return  Pointer to the risk object if \p index is valid. Otherwise 0.
 */
CRiskData* CVessel::getRisk(const int index)
{
    if (index >= m_risks.length())
        return nullptr;

    return m_risks[index];
}

But it seems that this QML <-> Qt property communication system only allows a getter that has no input parameters. I tried to define it like this:

Q_PROPERTY(CRiskData* risk READ getRisk)

I get a compiler error that no matching function is found for:

moc_cvessel.cpp:122: error: no matching function for call to 'CVessel::getRisk()'
         case 0: *reinterpret_cast< CRiskData**>(_v) = _t->getRisk(); break;
                                                                   ^

So MOC created the function with no input arguments... Is there any way around this? The goal is to be able to access each element of an object from the QML side in a hierarchical way.

1
You can use Q_INVOKABLE to make it a function that can have arguments, however, this might not be handy with regards to binding, but that cannot be examined since that part of code is not givenAmfasis
@Amfasis thank you for answer. I think this will work, will try to implement it.Łukasz Przeniosło

1 Answers

1
votes

Looking at your question, I think you should use something with related to MVVM (https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel) or MVC. So you should create your own C++ model that you expose to QML, by subclassing QAbstractItemModel. In this model, create a Q_INVOKABLE method in your list model:

cvessel.hpp:

#ifndef CVESSEL_HPP
#define CVESSEL_HPP

#include <QAbstractItemModel>
#include <QList>
class CRiskData;

class CVessel: public QAbstractItemModel
{
    Q_OBJECT

    public:
        CVessel(QObject * parent = nullptr);
        Q_INVOKABLE CRiskData* getRisk(const int index);
        static void declareQML();

        // QAbstractItemModel subclassing & rest of the header

    protected:
        QList<CRiskData *> m_risks;
};

#endif    // CVESSEL_HPP

cvessel.cpp:

#include "cvessel.hpp"
#include <QQmlEngine>
#include "criskdata.hpp"

CVessel::CVessel(QObject * parent = nullptr) :
    QAbstractItemModel(parent),
    m_risks()
{}

CRiskData* CVessel::getRisk(const int index);
{
    if (index >= m_risks.length())
        return nullptr;

    return m_risks[index];
}

void CVessel::declareQML() {
    qmlRegisterType<CVessel>("Bremen", 3, 14, "CVessel");
}

// Rest of implementation

RiskComponent.qml:

import QtQuick 2.12
import Bremen 3.14

Item {
    id: risk_component

    // ...

    TreeItem {
        id: the_tree
        model: CVessel {}

        // ...
    }

    // ...

    function usingARisk(riskIndex) {
        var risk = risk_component.the_tree.model.getRisk(riskIndex)

        // Using your risk on the QML side. For Example:
        console.log(qsTr("Risk level: %1").arg(risk.level))
    }

    // ...

}

main.cpp:

#include <QApplication>
#include <QQmlApplicationEngine>
#include "criskdata.hpp"
#include "cvessel.hpp"

int main(int argc, char ** argv) {
    QApplication app(argc, argv);

    //...

    CRiskData::declareQML();    // Of course if you use it on the QML side.
    CVessel::declareQML();

    //...

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/qml/main.qml")));
    int res = engine.rootObjects().isEmpty() ? -1 : app.exec();

    //...

    return res;
}

For further information about model subclassing, please have a look at the "Model/View Programming" page in Qt documentation, especially at the "Model Subclassing Reference" section.