Solved Sliding Grid for Chart
-
Hello everyone,
I am trying to implement a sliding-grid plot in QML. So far I managed to make my LineSeries slide, but I have no idea on how to move the underlying grid. I have checked the docs for ChartView, LIneSeries and ValueAxis, with no luck. Did I miss something?
I have seen people suggesting to use a CategoryAxis to control the position of ticks, but to me it does not look like an optimal solution, as I would need to implement the sliding logic myself.
I have also checked out QCustomPlot already. It offers exactly what I need, but it is not suitable for my requirements, because I have to use exclusively QML ( at most some JS ) to draw the charts.
Here is a minimal example of what I mean. As you can see, the grid is fixed, while the tick values update as the line series slides.
import QtQuick 2.9 import QtQuick.Layouts 1.0 import QtCharts 2.1 Item { id: root width: 640 height: 480 property int numpoints: 628; property double step: 0.01; Timer { id: timer; interval: 1; repeat: true; onTriggered: { draw( xAxis, yAxis, lineSeries, lineSeries.at(0).y ); } } function draw(xax, yax, lineSeries, dataPoint){ lineSeries.remove(0); xax.min = xax.min + step; xax.max = xax.max + step; var x = lineSeries.at(lineSeries.count-1).x + step ; lineSeries.append( x, dataPoint ); if(dataPoint > yax.max){ yax.max = dataPoint +1; } else if(dataPoint < yax.min){ yax.min = dataPoint -1; } } ColumnLayout { anchors.rightMargin: 20 anchors.leftMargin: 20 anchors.bottomMargin: 20 anchors.topMargin: 20 anchors.fill: parent ChartView { id: chartView width: 631 height: 400 Layout.fillHeight: true Layout.fillWidth: true title: "Sliding Grid" antialiasing: true legend { alignment: Qt.AlignBottom } ValueAxis { id: xAxis min: 0 max: numpoints * step + (numpoints*step)/6; tickCount: 5 minorTickCount: 5 } ValueAxis { id: yAxis min: 0 max: 10 tickCount: 5 minorTickCount: 5 } LineSeries { id: lineSeries name: "Line Series" axisX: xAxis axisY: yAxis } } Component.onCompleted: { for (var i = 0; i < numpoints; i++) { lineSeries.append(i*step, Math.sin(i*step) * 3 + 5); } timer.start() } } }
Do you have any suggestion? If you know any other good plotting libraries for QML, feel free to suggest them.
-
@zansara said in Sliding Grid for Chart:
it does not look like an optimal solution, as I would need to implement the sliding logic myself.
The suggested approach using
CategoryAxis
works, and is actually quite simple to implement, if you give it a try: it requires minimal implementation logic, of which you already did most of the work.There is no other solution, I'm afraid.
(GIF animation is frame-reduced because max. 1MB allowed, it seems)
-
Thank you for your feedback.
I got it working using CategoryAxis, as you said. Am I wrong, or there is no way to draw the minor ticks? I see no mention to them in the docs.
This is what I have now:
import QtQuick 2.9 import QtQuick.Layouts 1.0 import QtCharts 2.1 Item { id: root width: 640 height: 480 property int numpoints: 628; property double step: 0.01; Timer { id: timer; interval: 1; repeat: true; onTriggered: { draw( xAxis, yAxis, lineSeries, lineSeries.at(0).y ); if( xAxis.max - xAxis.categoriesLabels[xAxis.categoriesLabels.length -1] > 1 ){ xAxis.append(xAxis.max.toFixed(0), xAxis.max); xAxis.remove(xAxis.categoriesLabels[0]); } } } function draw(xax, yax, lineSeries, dataPoint){ lineSeries.remove(0); xax.min = xax.min + step; xax.max = xax.max + step; var x = lineSeries.at(lineSeries.count-1).x + step ; lineSeries.append( x, dataPoint ); if(dataPoint > yax.max){ yax.max = dataPoint +1; } else if(dataPoint < yax.min){ yax.min = dataPoint -1; } } ColumnLayout { anchors.rightMargin: 20 anchors.leftMargin: 20 anchors.bottomMargin: 20 anchors.topMargin: 20 anchors.fill: parent ChartView { id: chartView width: 631 height: 400 Layout.fillHeight: true Layout.fillWidth: true title: "Sliding Grid" antialiasing: true legend { alignment: Qt.AlignBottom } CategoryAxis { id: xAxis min: 0 max: numpoints * step + (numpoints*step)/6; labelsPosition: CategoryAxis.AxisLabelsPositionOnValue; Component.onCompleted: { for(var i=0; i<max+1; i++){ xAxis.append( i, i ); } } } ValueAxis { id: yAxis min: 0 max: 10 tickCount: 5 minorTickCount: 5 } LineSeries { id: lineSeries name: "Line Series" axisX: xAxis axisY: yAxis Component.onCompleted: { for (var i = 0; i < numpoints; i++) { lineSeries.append(i*step, Math.sin(i*step) * 3 + 5); } timer.start(); } } } } }
The only issue I see with this approach is relative to performance, but I am still investigating how fast I can push the updates and how many FPS I can achieve. If you have any suggestion from this point of view, that would be great.
-
@zansara said in Sliding Grid for Chart:
Am I wrong, or there is no way to draw the minor ticks?
I'm afraid that it looks that way :-(
I did basically the same as you to obtain the above animation:
property int numpoints: 126 property int numTicks: 10 property double step: 10 property double range: numpoints*step property double tickStep:range/numTicks property int tickCounter: 0 Timer { id: timer interval: 100 repeat: true onTriggered: { draw( xAxis, yAxis, lineSeries, lineSeries.at(0).y ); } } function draw(xax, yax, lineSeries, dataPoint){ lineSeries.remove(0); xax.min = xax.min + step; xax.max = xax.max + step; var x = lineSeries.at(lineSeries.count-1).x + step ; lineSeries.append( x, dataPoint ); if(dataPoint > yax.max){ yax.max = dataPoint +1; } else if(dataPoint < yax.min){ yax.min = dataPoint -1; } if (xax.min > parseFloat(xax.categoriesLabels[1])) { updateGrid(xax.categoriesLabels[0]) } } function updateGrid(minVal){ ++tickCounter xAxis.remove(minVal) var newTick = (tickCounter + numTicks-1)*tickStep xAxis.append(newTick.toString(), newTick) } function initGrid(minVal){ for (var j=0; j<numTicks; ++j){ var endValue = minVal + j*tickStep xAxis.append(endValue.toString(), endValue) } xAxis.min = minVal xAxis.max = minVal + (numTicks-1)*tickStep tickCounter = 0 } ColumnLayout { anchors.rightMargin: 20 anchors.leftMargin: 20 anchors.bottomMargin: 20 anchors.topMargin: 20 anchors.fill: parent ChartView { id: chartView width: 631 height: 400 Layout.fillHeight: true Layout.fillWidth: true title: "Sliding Grid" antialiasing: true legend { alignment: Qt.AlignBottom } CategoryAxis { id: xAxis labelsPosition: CategoryAxis.AxisLabelsPositionOnValue } ValueAxis { id: yAxis min: 0 max: 10 tickCount: 5 minorTickCount: 5 } LineSeries { id: lineSeries name: "Line Series" axisX: xAxis axisY: yAxis } } Component.onCompleted: { for (var i = 0; i < numpoints; i++) { lineSeries.append(i*step, Math.sin(i*step/100) * 3 + 5); } initGrid(0) timer.start() } }
-
@Diracsbracket said in Sliding Grid for Chart:
@zansara said in Sliding Grid for Chart:
Am I wrong, or there is no way to draw the minor ticks?
I'm afraid that it looks that way :-(
I see... However, many thanks for your time! :)