QCustomPlot Discussion and Comments

Qt increasing accumulated GUI response slowness when implementing RT plotReturn to overview

Hi,

I'm using QCustomPlot as part of a Qt application (c++) running on Raspberry Pi 4.
It is used for implementing an oscilloscope like display of RT signal waveforms using a periodic timer.
It all works perfect, but after few minutes (actual time depends on the replot repetition rate - 0.1-0.5sec) all Qt GUI response becomes very slow.
Slow means delay of few seconds between, for example, moving a knob and until it is actually move.
When disabling the plot itself but leaving all the data transfer working, no delay is observed, so I'm assuming its the plotting that cause the slowness.
Bellow is the plot code.
Any idea what can be the cause for this?
Thanks,
Nahum.

Osciloscope::Osciloscope(QCustomPlot *plt)
{
	plot = plt;
	
	sensitivity = 8.0;
}

void Osciloscope::setupPlot()
{
	if ((signalPlotDataSize > 0) && (sigData != NULL) && (plot != NULL))
	{
		QPen pen;
		pen.setStyle(Qt::SolidLine);
		pen.setWidth(1);
		pen.setColor(Qt::green);
		plot->addGraph();
		plot->graph(0)->setPen(pen);
		plot->addGraph();
		QVector<double> x(signalPlotDataSize + 1), y0(signalPlotDataSize + 1);
		for (int i = 0; i < signalPlotDataSize; ++i)
		{
			x[i] = i;
			y0[i] = *(sigData + i) * sensitivity;
		}
		plot->xAxis->setVisible(false);
		plot->xAxis->setTickLabels(true);
		plot->yAxis->setVisible(false);
		plot->yAxis->setTickLabels(true);
		// pass data points to graphs:
		plot->graph(0)->setData(x, y0);
		// let the ranges scale themselves so graph 0 fits perfectly in the visible area:
		plot->graph(0)->rescaleAxes();
		plot->yAxis->setRange(-1.0, 1.0);
	}
}

void Osciloscope::updatePlotParams(float *sig, int size)
{
	if ((sig != NULL) && (size >= 0))
	{
		sigData = sig;
		signalPlotDataSize = size;
	}
}

void Osciloscope::redrawPlot()
{
	setupPlot();

	if (plot != 0)
	{
		plot->replot();
	}
}

If you have lots of data or your data is coming in quickly, i wouldnt use the setData function, because it has to copy and redo everything.

I would hold a pointer to a QCPGraphDataContainer and add to that in your signal.

another thing you can do is keep track of the bounds as you get data coming in so you dont have to call rescaleAxes, because every time you call it, it will go through all the data to find the ranges. (obviously not necessary if you already know the min max with a single value coming in)

lastly, dont call replot on every sample coming in, call it on an update timer with a slower refresh rate (it will drastically reduce the number of calls to redraw).

I think you have a bug: in redrawPlot, you call setupPlot. That doesn't make sense. You should set up the plot once, and then repeatedly maybe call redrawPlot.

Right now you are creating thousands or more of graphs, because in every setupPlot, you call addGraph twice and do the whole configuration.

Also your vectors x and y0 are too large one element. Doesn't change much in performance, but is probably unintentional still (you always have a (0, 0) data point in there)

And if you set the yAxis range explicitly to (-1, 1) then you don't have to call rescaleAxes before. Only call rescaleKeyAxis. This avoids the rescaling operation of the yAxis. The key axis rescale is very fast because the data points are sorted by key internally.

Hi ian and Isso,

Thank you for your prompt and comprehensive feedback.

I have follow most of your advises, and its now working well.

1. The plot was (and is) not redraw every new single data point. New data is added as a full vector using an asynchronous callback function call from the DSP engine.
2. Plot was (and is) redraw using a timer (was 0.5sec now even 0.2sec).
3. All onetime plot settings were removed from the periodic drawing call and are performed just once.
4. setupPlot() now only refreshes the new data.
5. rescaleAxes() was removed. yAxis->setRange() is called only once, and only xAxis->setRange() is called for every new set of data (not sure why it is needed).

Please see the code below.

Thank you,

Nahum.

Osciloscope::Osciloscope(QCustomPlot *plt)
{
	if (plt != NULL)
	{
		plot = plt;
		
		QPen pen;
		pen.setStyle(Qt::SolidLine);
		pen.setWidth(1);
		pen.setColor(Qt::blue);
		plot->addGraph();
		plot->graph(0)->setPen(pen);
		plot->addGraph();
		
		plot->xAxis->setVisible(false);
		plot->xAxis->setTickLabels(true);
		plot->yAxis->setVisible(false);
		plot->yAxis->setTickLabels(true);
		
		plot->yAxis->setRange(-1.0 , 1.0);	
		
		plot->setBackground(QBrush(QColor("#b3daff")));
		
		sensitivity = 0.5f + 50.0/4.0; // mid range
	}
}

void Osciloscope::setupPlot()
{
	if ((signalPlotDataSize > 0) && (sigData != NULL) && (plot != NULL))
	{
		QVector<double> x(signalPlotDataSize + 1), y0(signalPlotDataSize + 1);
		for (int i = 0; i < signalPlotDataSize; ++i)
		{
			x[i] = i;
			y0[i] = *(sigData + i) * sensitivity;
		}
		// pass data points to graphs:
		plot->graph(0)->setData(x, y0);
		plot->xAxis->setRange(0, signalPlotDataSize);
	}
}

void Osciloscope::setSensitivity(int sens)
{
	if ((sens >= 0) && (sens <= 100))
	{
		if ((sens >= 0) && (sens <= 100))
		{	
			// 0.5 - 25.5
			sensitivity = 0.5f + sens / 4.0f;
		}
	}
}

void Osciloscope::updatePlotParams(float *sig, int size)
{
	if ((sig != NULL) && (size >= 0))
	{
		sigData = sig;
		signalPlotDataSize = size;
	}
}

void Osciloscope::redrawPlot()
{
	setupPlot();

	if (plot != 0)
	{
		plot->replot();
	}
}