Search code examples
qtqmlqtcharts

y-Axis Maximum in QML BarSeries Not Updating


I'm using a QML BarSeries to display some data and encountered a problem: the y-axis of the BarSeries doesn't update automatically.

Below is an mcve copied and modified from the BarSeries docs. It updates the bar series when the user clicks on the background.

//  main.qml

import QtQuick 2.6
import QtQuick.Window 2.2
import QtCharts 2.2

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    ChartView {
        id: chartView
        title: "Bar series"
        anchors.fill: parent
        legend.alignment: Qt.AlignBottom
        antialiasing: true

        BarSeries {
            id: mySeries
            axisX: BarCategoryAxis { categories: ["2007", "2008", "2009", "2010", "2011", "2012" ] }

            BarSet { label: "Bob"; values: [2, 2, 3, 4, 5, 6] }
            BarSet { label: "Susan"; values: [5, 1, 2, 4, 1, 7] }
            BarSet { label: "James"; values: [3, 5, 8, 13, 5, 8] }
        }

    }

    MouseArea {
        anchors.fill: parent
        onClicked: {
            mySeries.clear();  //  clear previous sets

            //  update with new sets
            mySeries.append("Bob", [3, 5, 8, 13, 5, 8]);
            mySeries.append("Susan", [2, 2, 3, 4, 5, 200]);
            mySeries.append("James", [5, 1, 2, 4, 1, 7]);

        }
    }
}

From the code, we could see that the click on the mouse area should update the series to have a y-axis of up to 200 (due to Susan's new set of values).

The screenshots below show the columns updating but not the y-axis. (Note that I'm expecting the y-axis maximum to update to 200.)

Before the mouse click:

After the mouse click:

What changes should I make to update the maximum of the chart's y-axis?

After the multiple mySeries.append statements in MouseArea::onClicked, I tried doing chartView.update() but this worked to no avail.

I searched and researched but found nothing. Most answers from the web concern only QtCharts run from C++ or describe a different issue (unless I searched with the wrong keywords?).


For completeness, here's the main.cpp file:

#include <QApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    QApplication app(argc, argv);  // needs QT += widgets in qmake

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

Solution

  • I was able to solve this issue by attaching a custom ValueAxis to the BarSeries and manually, programmatically updating the new maximum with the ValueAxis::max property.

    import QtQuick 2.6
    import QtQuick.Window 2.2
    import QtCharts 2.2
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
        
        ChartView {
            id: chartView
            title: "Bar series"
            anchors.fill: parent
            legend.alignment: Qt.AlignBottom
            antialiasing: true
        
            BarSeries {
                id: mySeries
                axisX: BarCategoryAxis { categories: ["2007", "2008", "2009", "2010", "2011", "2012" ] }
    
                axisY: ValueAxis {    //  <- custom ValueAxis attached to the y-axis
                    id: valueAxis
                }
    
                BarSet { label: "Bob"; values: [2, 2, 3, 4, 5, 6] }
                BarSet { label: "Susan"; values: [5, 1, 2, 4, 1, 7] }
                BarSet { label: "James"; values: [3, 5, 8, 13, 5, 8] }
            }
            
        }
        
        MouseArea {
            anchors.fill: parent
            onClicked: {
                mySeries.clear();
                mySeries.append("Bob", [3, 5, 8, 13, 5, 8]);
                mySeries.append("Susan", [2, 2, 3, 4, 5, 200]);
                mySeries.append("James", [5, 1, 2, 4, 1, 7]);
                
                valueAxis.max = 200;  //  hard-code a new maximum
            }
        }
    }
    

    This works splendidly. Here's what the chart now looks like after a click on the background:

    Here's a solution that dynamically calculates the new maximum (only the onClicked slot is shown, for brevity):

    onClicked: {
        mySeries.clear();
        mySeries.append("Bob", [3, 5, 8, 13, 5, 8]);
        mySeries.append("Susan", [2, 2, 3, 4, 5, 200]);
        mySeries.append("James", [5, 1, 2, 4, 1, 7]);
        
        //  deduce the new min and max
        var min = 1e8, max = -1e8;
        for (var i = 0; i < mySeries.count; i++) {
            //  min = Math.min(min, ...mySeries.at(i).values);  //  modern js not yet supported?
            //  max = Math.min(max, ...mySeries.at(i).values);
    
            min = Math.min(min, mySeries.at(i).values.reduce(function(a,b) {
                return Math.min(a, b);
            }));
            max = Math.max(max, mySeries.at(i).values.reduce(function(a,b) {
                return Math.max(a, b);
            }));
        }
    
        //  set the new min and max
        valueAxis.min = min;
        valueAxis.max = max;
    
        // valueAxis.max = max * 1.05; // or give a little margin?
        
    }
    

    Of course, the minimum could be left out of the picture, but that entirely depends on your data and situation.