Reputation: 157
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?
Upvotes: 3
Views: 5766
Reputation: 157
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])
}
}
...
}
Upvotes: 2
Reputation: 12854
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.
Upvotes: 0