简体   繁体   中英

Give data to a ChartView (LineSeries) - QML

I recently started with QML and I'm searching for a way to give data from c++ to my ChartView in QML. I would prefer a solution where I can send a QMap with something like Q_PROPERTY so it updates automatically.

I've searched and found that you can do a function where you then can use 'append()' to added values to the chart. But I seem to be unable to send some kind of list to the QML...

QML file:

ChartView {
    theme: ChartView.ChartThemeQt
    antialiasing: true

    DateTimeAxis {
        id: dateTimeAxisX
    }

    ValueAxis{
        id: valueAxisY
        min: 0
        max: 15
        titleText: "Voltage (V)"
    }

    LineSeries {
        id: voltageSeries
        axisX: dateTimeAxisX
        axisY: valueAxisY
        name: "Battery Voltage"
    }
}

robot.h:

class Robot: public QObject
{
    ...
    Q_PROPERTY(QMap<int, double> list_battery_voltages READ getList_battery_voltages NOTIFY listBatteryVoltagesChanged)
    ...
}

main.cpp:

int main(int argc, char *argv[])
{
#if defined(Q_OS_WIN)
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
#endif

    QApplication app(argc, argv);
    QQmlApplicationEngine engine;

    // Load custom class inside engine
    QScopedPointer<Robot> robot(new Robot(app.applicationDirPath() + "/robot_settings.ini"));
    engine.rootContext()->setContextProperty("robot", robot.data());

    QQmlComponent component(&engine, QUrl(QLatin1String("qrc:/main.qml")));
    component.create();

    return app.exec();
}

Are there elegant solutions that I'm overseeing?

I have solved it by using QVariantMap . It is not the most ellegant solution but it works :) I have a javascript function in my QML file that fills the lineseries. This function is connected to my robot signal that will be send when new data have arrived.

robot.h:

class Robot: public QObject
{
    ...
    Q_PROPERTY(QVariantMap list_battery_voltages READ getList_battery_voltages NOTIFY listBatteryVoltagesChanged)
    ...
}

QML:

ChartView {
    ...
    Connections {
        target: robot
        onMapBatteryVoltagesChanged: insertVoltages()
    }

    Component.onCompleted: {
        insertVoltages()
    }
    ...
    function insertVoltages() {
        var voltages_map = robot.map_battery_voltages
        for (var prop in voltages_map) {
            voltageSeries.append(prop, voltages_map[prop])
        }
    }
    ...
}

As I remember Javascript in QML operates with C++ array using QVariantList and QVariantMap only. They are both not good for you as I see. So I advice you to store your map in C++ in a way you like and provide some convenient way to access the values. For example, using singleton:

mysingleton.h

class MySingleton : public QObject
{
    Q_OBJECT
public:
    explicit MySingleton(QObject *parent = nullptr);
    Q_INVOKABLE int count();
    Q_INVOKABLE double getX(int index);
    Q_INVOKABLE double getY(int index);

private:
    QList<QPair<double, double>> m_map;
};

mysingleton.cpp

MySingleton::MySingleton(QObject *parent) : QObject(parent)
{
    QRandomGenerator *generator = QRandomGenerator::global();
    int count = generator->generate() % 10;
    double xAccumulated = 0;
    for(int i = 0;i < count;i ++)
    {
        double x = generator->generateDouble() / (double)count;
        xAccumulated += x;
        double y = generator->generateDouble();
        m_map.append(QPair<double, double>(xAccumulated, y));
    }
}

int MySingleton::count()
{
    return m_map.count();
}

double MySingleton::getX(int index)
{
    return m_map.at(index).first;
}

double MySingleton::getY(int index)
{
    return m_map.at(index).second;
}

registering (main.cpp):

static QObject *my_singleton_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
{   
    return new MySingleton(engine);
}

main() {
    qmlRegisterSingletonType<MySingleton>("Qt.MyTest", 1, 0, "MySingleton", my_singleton_provider);
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
}

And so now you can use this in QML:

import QtQuick 2.9
import QtCharts 2.1
import Qt.MyTest 1.0

ChartView {
    anchors.fill: parent
    LineSeries {
        id: series
        Component.onCompleted: {               
            for(var i = 0;i < MySingleton.count();i ++) {
                series.append(MySingleton.getX(i), MySingleton.getY(i));
            }
        }
    }
}

Using this way could give you some additional advantages like data change signal etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM