David Partridge
David Partridge

Reputation: 363

Dynamically udpating a QChart when the Line Series is updated

If have a QLineSeries and a QChart.

I expected that appending a new point would automatically update the Chart. But found that the only way to get it to update the chart was this horrible code:

//
// Add the score and update the score chart
//
if (nullptr != scoreSeries)
    scoreChart->removeSeries(scoreSeries);
else
{
    scoreSeries = new QLineSeries(this);
    scoreSeries->setName(tr("Score", "IDC_SCORE"));
    scoreSeries->setPointsVisible(true);
    connect(scoreSeries, &QLineSeries::hovered,
        this, &ChartTab::scoreHovered);
}

scoreSeries->append(x, fScore);
scoreMap.emplace(name, size - 1);
scoreChart->addSeries(scoreSeries);
scoreChart->createDefaultAxes();
axes = scoreChart->axes(Qt::Horizontal);
for (const auto& p : axes)
{
    QValueAxis* axis{ dynamic_cast<QValueAxis*>(p) };
    if (axis)
    {
        axis->setRange(1.0, size);
        axis->setTickAnchor(1.0);
        axis->setTickType(QValueAxis::TicksDynamic);
        axis->setTickInterval(interval);
    }
}

I'm convinced that there's a better way to get the chart updated when I append to the line series but so far I've failed to find anything that actually works.

Please put me out of my misery and tell me how this should be done.

FWIW I'm using Qt 6.5.1 but I doubt that makes a great difference.

Upvotes: 0

Views: 191

Answers (1)

David Partridge
David Partridge

Reputation: 363

It would appear that if the point being added is outside the range of the axes for the chart then you'll see nothing (as I did).

So you need create the line series and axes to start with and then adjust the axis ranges prior to appending the new point.

So with the following inline functions defined in my class header:

inline QValueAxis* axisX(QChart* chart)
{
    return qobject_cast<QValueAxis*>(chart->axes(Qt::Horizontal)[0]);
}
inline QValueAxis* axisY(QChart* chart)
{
    return qobject_cast<QValueAxis*>(chart->axes(Qt::Vertical)[0]);
}

and this in the ctor:

QValueAxis* xAxis;
QValueAxis* yAxis;

scoreChart = new QChart();
scoreSeries = new QLineSeries(this);
scoreSeries->setName(tr("Score", "IDC_SCORE"));
scoreSeries->setPointsVisible(true);
scoreChart->addSeries(scoreSeries);
scoreChart->createDefaultAxes();
xAxis = axisX(scoreChart);
yAxis = axisY(scoreChart);
xAxis->setLabelFormat("%d");
xAxis->setTickType(QValueAxis::TicksDynamic);
yAxis->setRange(0, 1000);

Then in the mf that adds points I needed:

QValueAxis* xAxis;
QValueAxis* yAxis;

// Establish a value for maximum x as rangeMax
// I did it based on the number of data points to plot as in my case
// the x value is just the data point number. So e.g. I set rangeMax
// to 20 if there were 20 or fewer points to plot
// 
// Code for that omitted
//

//
// Adjust score axes if necessary (data won't display if we don't)
//
xAxis = axisX(scoreChart);
xAxis->setRange(1.0, rangeMax);
xAxis->setTickAnchor(1.0);
xAxis->setTickInterval(interval);
yAxis = axisY(scoreChart);
if (fScore > yAxis->max())
    yAxis->setMax(fScore * 1.1);
if (fScore < yAxis->min())
    yAxis->setMin(fScore);

//
// Add the score and update the score chart
//
scoreSeries->append(x, fScore);
yAxis->applyNiceNumbers();

I hope this helps someone else who is struggling with QChart.

David

Upvotes: 0

Related Questions