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.
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++;
}
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 QueuedConnection
ed 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With