QML application using QtChart performance issue
-
Our application should be able to show live data with max 4096 points of data. Our first test application was created with Qt Designer, where we used a litte trick to get the QtChart to perform way better: before updating the data on a Series, we call the "blockSignals" function. This way, the chart won't update on every datapoint being updated or added.
We're now on the move to a QML application, created with Qt Design Studio. We're trying to use the same QtChart in this application, and this basically works just fine.
But: the performance is bad. Very bad. And we cannot find any way to use the same "blockSignals" trick in this QML application as we did in the widgets application.Anyone any experience on QML / QtChart / performance?
-
We had a problem very simillar to this. Finally we wrote our own graph/chart to do the same.
-
@JonB
We already tried this to get the code as optimised as possible, using both the series->replace AND the blockSignals "trick". No luck however... It seems like the blockSignals function is not working at all in the QML application.screen01.qml:
Rectangle { id: rectangle width: Constants.width height: Constants.height color: Constants.backgroundColor states: [ State { name: "clicked" } ] Plot { the_plot_data: PlotData{} } }
plot.qml:
import QtQuick 6.5 import QtCharts 2.6 Rectangle { id: chartrect required property PlotData the_plot_data color: "lightblue" anchors.fill: parent Timer { property int m_x: 0 interval: 50 running: true repeat: true onTriggered: { chartrect.the_plot_data.updateData(the_chart.series(0)) } } ChartView { id: the_chart title: "Line" antialiasing: false anchors.fill: parent ValuesAxis { id: axisX min: 0 max: 2048 } ValuesAxis { id: axisY min: 0 max: 3000 } LineSeries { id: series name: "LineSeries" axisX: axisX; axisY: axisY; //useOpenGL: true // mooi man, zie je helemaal niets meer ;-) XYPoint { x: 0; y: 0 } XYPoint { x: 750; y: 200 } XYPoint { x: 1200; y: 250 } XYPoint { x: 1500; y: 2200 } XYPoint { x: 1750; y: 2000 } XYPoint { x: 2000; y: 1600 } XYPoint { x: 2047; y: 400 } } } }
and in plotdata.cpp:
void PlotData::updateData(QAbstractSeries *p_series) { QVector<QPointF> pointList; pointList.reserve(numPts); for(int count = 0; count < numPts; ++count) { pointList.append(QPointF(count * (2048 / numPts), 200+ (rand() % 2000))); } m_counter++; if(p_series) { p_series->useOpenGL(); p_series->blockSignals(true); QLineSeries *xySeries = static_cast<QLineSeries *>(p_series); xySeries->replace(pointList); p_series->blockSignals(false); emit xySeries->pointAdded(0); } }
-
@Avantes
Looks like you have tried the right stuff. I'm afraid I don't know anything about QML.blockSignals()
is my idea of Devil's spawn at the best of times.@dheerendra's post that he encountered something like this and gave up on QML charts is not encouraging.
-
@Avantes I am not sure exactly what the "blockSignals" approach was that you used to prevent the chart being updated on each point added, but I would have thought that you could do something along the lines of buffering incoming points and only adding them to the chart at certain intervals. You could use a timer for example to decide when to update the chart.
When I implemented a use of the QML
ChartView
, I used one of the examples (I think it was called something like "Oscilloscope") as a guide. It was very useful as it shows a way of organising the code that might not necessarily be obvious from the docs, but involves keeping all of the data handling and appending logic in the C++ backend. If you are using theXYSeries
for example, having created it in the QML layer, you pass into the C++ backend to be updated. This sees it as aQXYSeries
and you use the C++ API.Edit: sorry, I see you added more details in a new post while I was writing that and are already handling the data in C++.
-
@Bob64 said in QML application using QtChart performance issue:
that you could do something along the lines of buffering incoming points and only adding them to the chart at certain intervals.
That is exactly what we recommended by consulting https://forum.qt.io/topic/140576/qt-charts-extremely-slow-qlineseries. And then QML is bad because it does not have any call to add multiple points in one call (which
QChart
does). Hence the recommendation to usereplace()
. Which the OP says he is using, and is not good enough....@Avantes How large is
numPts
whenupdateData()
is called? How frequently in real/clock time is that function called? -
@JonB said in QML application using QtChart performance issue:
And then QML is bad because it does not have any call to add multiple points in one call (which QChart does). Hence the recommendation to use replace()
But the calls to add points are on the
QAbstractSeries
objects which are (or can be) used both withQChart
and QMLChartView
so there is no difference in the ability to add multiple points. -
The posted code seems very unoptimized (in OP's snippet or in other links). Replacing every points at each tick?
Passing a QLineSeries from QML to C++ also looks like a bad practice.I'd recommend using VXYModelMapper instead.
-
@Avantes
I don't know if this is helpful, but this is how I solved this for myself:class PointUpdater : public QObject { Q_OBJECT public: PointUpdater(QObject* parent=nullptr) : QObject(parent) { } public slots: void update(QAbstractSeries *series, QVariantList points){ if(!series){ return; } QVector<QPointF> newpoints; for(QVariant point: points){ auto list = point.toList(); newpoints.push_back(QPointF(list.at(0).toDouble(), list.at(1).toDouble())); } QXYSeries *xySeries = static_cast<QXYSeries *>(series); xySeries->replace(newpoints); } };
It looks like you already tried something similar. This only notifies the chart after every point is replaced. So it does not notify per point.
-
@fcarney I don't have access to my code at the moment, but this looks very familiar to me. I am not sure how realistic @Avantes' example was because it seemed to be randomly generating a whole new set of points on every update.
@GrecKo "Passing a QLineSeries from QML to C++ also looks like a bad practice."
I think this is OK. My understanding is that typically one will create the series in QML using
ChartView.createSeries
but it is more efficient to pass that object to C++ to be updated because, for example, thereplace
overload that accepts multiple points is available there but not in the QML exposure of the series object. Also (at least in my system), new data comes in directly to the C++ backend anyway and is never explicitly passed through the QML layer. As I understand it, one is essentially just passing a pointer to the series object from QML to C++ so there should not be much overhead introduced. -