Accessing Qt Charts with QDateTimeAxis in QML from C++
-
I have a problem with Qt charts and with QDateTimeAxis. Inserted data won't display properly, when series is edited from C++ class. In main.qml I have only ChartView which is edited from C++ class DataHandler. DataHandler generates random data every second with current time stamp and edits the series in chart correspondigly. Also chart x-axis is edited dynamically. Changing the QDateTimeAxis to ValueAxis fixes the problem, but then the data time won't be in readable format in chart.
Here is main.cpp
#include <QGuiApplication> #include <QApplication> #include <QQmlApplicationEngine> #include <QQmlEngine> #include <QQmlContext> #include <QQmlComponent> #include "datahandler.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication app(argc, argv); QQmlApplicationEngine engine; QQmlComponent component(&engine, QUrl(QLatin1String("qrc:/main.qml"))); QObject *object = component.create(); QScopedPointer<DataHandler> datahandler(new DataHandler(object)); engine.rootContext()->setContextProperty("datahandler", datahandler.data()); return app.exec(); }
Here is datahandler.h
#ifndef DATAHANDLER_H #define DATAHANDLER_H #include <QObject> #include <QtCharts> QT_CHARTS_USE_NAMESPACE class DataHandler : public QObject { Q_OBJECT public: DataHandler(QObject* object, QObject *parent = 0); ~DataHandler(); private: QTimer* m_timer; QObject* m_object; QObject* m_chart; QVector<QPointF> m_data1, m_data2; public slots: void addValues(); }; #endif // DATAHANDLER_H
...and datahandler.cpp
#include "datahandler.h" #include <QObject> #include <QTimer> #include <QDateTime> #include <QDebug> DataHandler::DataHandler(QObject* object, QObject *parent) { m_timer = new QTimer(); m_object = object; connect(m_timer, &QTimer::timeout, this, &DataHandler::addValues); m_chart = m_object->findChild<QObject*>("chart"); m_timer->start(1000); } DataHandler::~DataHandler(){ } void DataHandler::addValues(){ if(m_chart){ //Generate the data m_data1.append(QPointF(QDateTime::currentMSecsSinceEpoch(), qrand() % 100)); m_data2.append(QPointF(QDateTime::currentMSecsSinceEpoch(), qrand() % 100)); QAbstractSeries *series1; QAbstractSeries *series2; QAbstractAxis *xAxis; //Fetch the series and x-axis from QML QMetaObject::invokeMethod(m_chart, "series", Qt::AutoConnection, Q_RETURN_ARG(QAbstractSeries*, series1), Q_ARG(int, 0)); QMetaObject::invokeMethod(m_chart, "series", Qt::AutoConnection, Q_RETURN_ARG(QAbstractSeries*, series2), Q_ARG(int, 1)); QMetaObject::invokeMethod(m_chart, "axisX", Qt::AutoConnection, Q_RETURN_ARG(QAbstractAxis*, xAxis)); //Cast the series and axis to proper form QXYSeries* xyseries1 = static_cast<QXYSeries *>(series1); QXYSeries* xyseries2 = static_cast<QXYSeries *>(series2); QDateTimeAxis* dateTimeAxis = static_cast<QDateTimeAxis *>(xAxis); //set the new values to series and axises xyseries1->replace(m_data1); xyseries2->replace(m_data2); dateTimeAxis->setMax(QDateTime::fromMSecsSinceEpoch(m_data1.last().x())); dateTimeAxis->setMin(QDateTime::fromMSecsSinceEpoch(m_data1.first().x())); } }
Here is main.qml
import QtQuick 2.7 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.3 import QtCharts 2.0 ApplicationWindow { visible: true width: 640 height: 480 title: qsTr("Hello World") ChartView{ id: chart objectName: "chart" antialiasing: true legend.visible: false width: parent.width height: parent.height anchors.fill: parent theme: ChartView.ChartThemeBlueCerulean ValueAxis{ id:axisY min:0 max:100 } DateTimeAxis{ id:dateTime format: "hh:mm:ss" tickCount: 5 } LineSeries{ id:series1 objectName: "series1" axisX: dateTime axisY: axisY useOpenGL: true } LineSeries{ id: series2 objectName: "series2" axisX: dateTime axisY: axisY useOpenGL: true } } }
and ChartTest.pro file
QT += qml quick widgets charts CONFIG += c++11 SOURCES += main.cpp \ datahandler.cpp RESOURCES += qml.qrc # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH = # Additional import path used to resolve QML modules just for Qt Quick Designer QML_DESIGNER_IMPORT_PATH = # The following define makes your compiler emit warnings if you use # any feature of Qt which as been marked deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if you use deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target HEADERS += \ datahandler.h
-
I got the program working, by deleting and generating new series every time the chart update is required. First I delete and generate new series in QML and then call the addValues() function in C++ side. This solves the problem, but does not change the fact that there seems to be something wrong with the QDateTimeSeries update.
-
I noticed that the actual cause of the problem is in LineSeries useOpenGL property. When OpenGL is on, the series won't update properly. After turnign OpenGL off, everything works just fine.