c++qtqmlqt5qproperty

Read C++ QVector of structs in QML


In my C++ class I have

struct trackPoint {
  QString lat;
  QString lon;
  QString elevation;
};

  QVector<trackPoint> trackPoints;

In QML I want to access this as a multi-dimensional array of lon,lat pairs

[[0,1],[1,1],[2,1]]

Is this possible using the Q_Property mechanism? As I am pretty sure that structs cannot be exposed to QML?

I've tied:-

 Q_PROPERTY(QVector<trackPoint> trackPoints READ gpx)

With a method:-

QVector<trackPoint> GPXFileIO::gpx() const {
 return trackPoints;
}

But this gives me the error:-

QMetaProperty::read: Unable to handle unregistered datatype 'QVector<trackPoint>' for property 'GPXFileIO::trackPoints'

Solution

  • A simple way to expose a struct to QML is using Q_GADGET with Q_PROPERTY so we can get each element of the structure, they will not be part of an array. On the other hand QVector is supporting a number of elements with QString, int, QUrl, etc. but not for new types, in which case QVariantList should be used.

    main.cpp

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQmlContext>
    #include <QVector>
    
    struct TrackPoint {
        Q_GADGET
        Q_PROPERTY(qreal lat MEMBER lat)
        Q_PROPERTY(qreal lon MEMBER lon)
        Q_PROPERTY(qreal elevation MEMBER elevation)
    public:
        qreal lat;
        qreal lon;
        qreal elevation;
    };
    
    class TrackClass: public QObject
    {
        Q_OBJECT
        Q_PROPERTY(QVariantList trackpoints READ gpx)
    public:
        TrackClass(QObject *parent=nullptr):QObject(parent){
            trackPoints << TrackPoint{10, 10, 10} << TrackPoint{11, 11, 11};
        }
        QVariantList gpx() const{
            QVariantList l;
            for(const TrackPoint & p: trackPoints){
                l << QVariant::fromValue(p);
            }
            return  l;
        }
    private:
        QVector<TrackPoint> trackPoints;
    };
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
        QGuiApplication app(argc, argv);
        TrackClass track;
        QQmlApplicationEngine engine;
        engine.rootContext()->setContextProperty("track", &track);
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
        if (engine.rootObjects().isEmpty())
            return -1;
        return app.exec();
    }
    
    #include "main.moc"
    

    main.qml

    import QtQuick 2.9
    import QtQuick.Window 2.2
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
        Component.onCompleted: {
            for( var i in track.trackpoints){
                var p = track.trackpoints[i];
                console.log("lat: ", p.lat, "lon: ", p.lon, "elevation: ", p.elevation)
            }
        }
    }
    

    Output:

    qml: lat:  10 lon:  10 elevation:  10
    qml: lat:  11 lon:  11 elevation:  11