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.

    0_1533551579411_movinggrid.gif

    (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! :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.