6

I'd like to know how to repaint a QChart after I append new points to the QLineSeries added to it. The goal is to use this for displaying data being acquired at high rates (up to 400 000 pts/sec) and updating the plot as the points arrive in packets.

Here's the test program I've been working on:

MainWindow:

class MainWindow : public QMainWindow{
    Q_OBJECT

    QLineSeries *series;
    QChart *chart;
    QChartView *chartView;

    int cnt=0;


public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_pB_Start_clicked();

private:
    Ui::MainWindow *ui;
};

MainWindow constructor:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow){
    ui->setupUi(this);

    series = new QLineSeries();

    chart = new QChart();
    chart->setBackgroundRoundness(0);

    chart->addSeries(series);

 // A bunch of formatting
    chart->setBackgroundVisible(false);
    chart->setMargins(QMargins(0,0,0,0));
    chart->layout()->setContentsMargins(0,0,0,0);
    chart->legend()->hide();
    chart->setPlotAreaBackgroundBrush(QBrush(Qt::black));
    chart->setPlotAreaBackgroundVisible(true);
    chartView = new QChartView(chart);
    ui->gridLayout->addWidget(chartView);

}

And a pushButton clicked event to add points to the series:

void MainWindow::on_pB_Start_clicked(){
    series->append(cnt,qSin(cnt/10));
    cnt++;
    // Update plot here << ======== HOW?
}

The OpenGLSeries example does it somehow. I don't understand how. But that case it's a bit different as it replaces all points in the series with new ones, instead of appending them.

A. Vieira
  • 1,213
  • 2
  • 11
  • 27
  • What really happens when `on_pB_Start_clicked` being called? Does it either adds only one point to series or it starts adding points at high rates? – Sergey Aug 06 '16 at 13:59
  • In this example all it does is add another point. This is a testing program that I built to understand the functionalities of `QChart`. But the goal is to plot at high rates. – A. Vieira Aug 06 '16 at 14:07

2 Answers2

4

Apparently QCharts doesn't need repaint(). Appending new points to the series seems to be enough. I wasn't seeing the data because I hadn't set the axis for the char and also because values weren't properly calculated.

Corrected code:

Header:

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow){
    ui->setupUi(this);

    series = new QLineSeries();

    chart = new QChart();    
    chart->addSeries(series);

    chart->createDefaultAxes(); // Preparing the axis
    chart->axisX()->setRange(0,10); 
    chart->axisY()->setRange(0,10); 

    // Same formatting
    chart->setBackgroundVisible(false);
    chart->setMargins(QMargins(0,0,0,0));
    chart->layout()->setContentsMargins(0,0,0,0);
    chart->legend()->hide();
    chart->setPlotAreaBackgroundBrush(QBrush(Qt::black));
    chart->setPlotAreaBackgroundVisible(true);
    chartView = new QChartView(chart);
    ui->gridLayout->addWidget(chartView);
}

And the pushButton code, casting cnt to double before calculation.

void MainWindow::on_pB_Start_clicked(){
    double val = 3*(qSin((double)cnt*2)+2);
    series->append(cnt,val); // Enough to trigger repaint!
    cnt++;
}
A. Vieira
  • 1,213
  • 2
  • 11
  • 27
  • And if you're using `AreaSeries`, then you can update with `setUpperSeries(new_useries_here)` and `setLowerSeries(new_lseries_here)`. Your areaSeries must be stored as a global var. – lenooh May 16 '18 at 11:57
  • `easy_chart.cpp:72:12: warning: 'axisX' is deprecated qchart.h:109:5: note: 'axisX' has been explicitly marked deprecated here qcompilerdetection.h:227:45: note: expanded from macro 'Q_DECL_DEPRECATED'` – euraad Sep 08 '21 at 13:02
2

First, if your will receive and append points at 400000 pts/sec in GUI thread, your application will get absolutely freezed. So you need to dedicate another thread to data receiving and processing, and send processed graphics data to the GUI thread using (for example) signals/slots connected with QueuedConnection. By "processing" I mean at least some sort of decimation (averaging, dropping, decimation as those DSP guys understand it), because 400000 pts/sec seems to fast, you'll waste your memory and GUI performance. But if you do not want to decimate, it's up to you. In this case you may consider a more lightweight data delivery mechanism than QueuedConnectioned signals/slots.

The second question is when to plot? Not so long ago I implemented similar functionality with QCustomPlot at much lower rates. The main problem I faced was a huge (and varying) lag when I tried to replot after receiving each point, especially while plotting antialiased graph. In your case the solution is to subclass QChartView (I suppose you have already done it), override timerEvent in it and call startTimer()/killTimer() when you need to start/stop replotting. Alternatively, you may hold a timer in an object which owns the QChartView object and issue replotting from there, but it looks like abstraction leakage in comparison to subclassing QChartView. All in all, this approach allows you to achieve almost constant framerate and make it as smooth as you want without freezing application's interface.

And finally, how to replot? QChartView seems to have repaint() method inherited from QWidget which does what you need.

Sergey
  • 7,985
  • 4
  • 48
  • 80
  • That's it. `repaint()` seems to do it. I just need to predefine the scale or something similar. And yes 400 000 pts/s is a lot and I've never plotted so many in my main application. I've been using qwt to plot graphics up until now, with `QwtPlotDirectPainter`, that plots only a few points at a time, in conjunction with `QCoreApplication::processEvents()` to keep GUI going and works great. But as I see QCharts uses OpenGL so I plan on changing. – A. Vieira Aug 06 '16 at 14:56
  • @A.Vieira Qwt is often claimed to be much faster than QCustomPlot. Probably it's also faster than QCharts, I don't know. If to speak about scale, consider data-based rescaling before each replot. It may look confusing for the user in the first few seconds of plotting, but it solves possible problems with wrong predefined scale. – Sergey Aug 06 '16 at 15:09
  • From what I see in the OpenGLSeries, using OpenGL makes it faster than I can do with Qwt when plotting new arrays each time. I don't know how to use OpenGL in Qwt, it seems still in development but possible (don't quote me). I had success with Qwt, but QCharts may be more stable to develop in many pcs and pass my work to coworkers. – A. Vieira Aug 06 '16 at 16:23