0
votes

I'm trying to figure out how to get QList> from C++ signal in QML, i'm only getting either QVariant(RecordList, ) or QVariant(QList, ). Tried with different supported sequence type and they work perfectly (QList. I'll appreciate if somebody can help me to understand my error. Kind regards.

jsonreaderasync.h

typedef QPair<qreal,qreal>Record;
typedef QList<Record>RecordList;
class JsonReaderAsync : public QObject
{
    Q_OBJECT
public:
    explicit JsonReaderAsync(QObject *parent = nullptr);
    Q_INVOKABLE void start(const QString& fileName);

signals:
    void started();
    //void finished(QList<qreal> record);
    void finished(RecordList record);
    //void finished(QList<Record> record); 

};

jsonreaderasync.cpp

#include <QDebug>

#include "jsonreaderasync.h"

Q_DECLARE_METATYPE(Record)
Q_DECLARE_METATYPE(RecordList)
//Q_DECLARE_METATYPE(QList<Record>)


JsonReaderAsync::JsonReaderAsync(QObject *parent) : QObject(parent)
{
    qRegisterMetaType<Record>("Record");
    qRegisterMetaType<RecordList>("RecordList"); // qml prints QVariant(RecordList, )
    //qRegisterMetaType<QList<Record>>("QList<Record>"); //qml prints QVariant(QList<Record>, )

}

void JsonReaderAsync::start(const QString &fileName)
{

    QList<Record> record;
    record.append(qMakePair(1,1));
    record.append(qMakePair(1,2));
    record.append(qMakePair(1,3));

//    QList<qreal> foo;
//    foo.append(1);
//    foo.append(1);
//    foo.append(1);

    emit finished(record);

}

main.cpp

#include "jsonreaderasync.h"

typedef QPair<qreal,qreal>Record;
typedef QList<QPair<qreal,qreal>>RecordList;


int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QQmlApplicationEngine *engine = new QQmlApplicationEngine;
    JsonReaderAsync* dataReaderAsync = new JsonReaderAsync();
    engine->rootContext()->setContextProperty("JsonReaderAsync", dataReaderAsync);
    engine->load(QUrl(QStringLiteral("main.qml")));

    return app.exec();
}

main.qml

import QtQuick 2.12
import QtQuick.Dialogs 1.2
import QtQuick.Layouts 1.13
import QtQuick.Window 2.13

import QtQuick.Controls 2.12
import QtCharts 2.3

Window {
  id: appWindow
  visible: true
  minimumWidth : 400
  minimumHeight: 400

  Connections
  {
    target: JsonReaderAsync
    onStarted:{
      console.log("onStarted")
    }
    onFinished:{
      console.log("onFinished")
      console.log(record)
      console.log(record[0])
    }
  }

  Button {
    width : 40
    height: 40
    anchors.centerIn: parent
    onClicked: {
      JsonReaderAsync.start("")
    }
  }

}
2

2 Answers

1
votes

In QML, the meta-type system is the only way that QML engine can access C++ structures from a QML environment.

That is, only Predefined C++ Types and custom objects that have Q_PROPERTY declarations could access from QML environment.

Here's my recommend (simplest) modification:

jsonreaderasync.h

#include <QObject>
#include <QVariant>

class Record : public QPair<qreal, qreal> {
    Q_GADGET

    Q_PROPERTY(qreal first  MEMBER first  CONSTANT FINAL)
    Q_PROPERTY(qreal second MEMBER second CONSTANT FINAL)

public:

    Record() = default;
    Record(qreal a, qreal b) : QPair<qreal, qreal>(a, b) {}
};

class JsonReaderAsync : public QObject
{
    Q_OBJECT
public:
    explicit JsonReaderAsync(QObject *parent = nullptr);
    Q_INVOKABLE void start(const QString& fileName);

signals:
    void started();

    void finished(QVariantList record);  // RecordList
};

jsonreaderasync.cpp

#include <QDebug>

#include "jsonreaderasync.h"

JsonReaderAsync::JsonReaderAsync(QObject *parent) : QObject(parent)
{
    qRegisterMetaType<Record>("Record");
}

void JsonReaderAsync::start(const QString &fileName)
{
    QVariantList record;
    record.append(QVariant::fromValue(Record(1,1)));
    record.append(QVariant::fromValue(Record(1,2)));
    record.append(QVariant::fromValue(Record(1,3)));

    emit finished(record);
}

Now you can access records from QML:

onFinished: {
    for (var i = 0; i < record.length; ++i)
        console.log(record[i].first + "->" + record[i].second);
}

Note that converting between C++ objects and QML objects does incur a bit overhead, if these codes are performance sensitive, please consider using C++ Data Models.

1
votes

While @GPBeta solution works, I wanted more flexibility for Qml supported pairs. I tried to work with templates, but Q_GADGET doesn't support it. There might be a smart wrapper (Template + QVariant) solution, I guess... Nonetheless, here is my approach to the problem:

class PairQml {
    Q_GADGET

    Q_PROPERTY(QVariant first MEMBER first CONSTANT FINAL)
    Q_PROPERTY(QVariant second MEMBER second CONSTANT FINAL)

public:
    PairQml() = default;
    PairQml(QVariant f, QVariant s): first(f), second(s) {}

    QVariant first;
    QVariant second;
};

Don't forget to register: qRegisterMetaType<PairQml>("PairQml");