4 Attachment(s)
How to plot the real time data in qwtplot while erasing fixed-width old data?
This video is the effect I want,I referenced that oscilloscope code,but I did not use that QwtPlotDirectPainter class,because I can't achieve the effect I want?there are 12 curves refreshed at the same time,but high CPU usage,average over 90% on my pi 3b.
https://www.youtube.com/watch?v=x3RGt6QpPn8
Attachment 13434
Attachment 13433
Attachment 13432
This is my code compression package.Attachment 13435
I use below method to update the graph in real time.
Code:
//class MPlotWaveCurve inherit from QwtPlotCurve
void MPlotWaveCurve
::drawCurve(QPainter * painter,
int style,
const QRectF & canvasRect,
int from,
int to
) const {
switch (style)
{
case Lines:
if (testCurveAttribute(Fitted))
{
// we always need the complete
// curve for fitting
from = 0;
to = dataSize() - 1;
}
/*
below 10 lines code achieved my effect,
variable m_iEaserCount represents the erased width,
variable m_iCurrentPoint represents the latest data index
*/
if ((from < m_iCurrentPoint) && (m_iCurrentPoint < to - m_iEaserCount))
{
drawLines(painter, xMap, yMap, canvasRect, from, m_iCurrentPoint);
drawLines(painter, xMap, yMap, canvasRect, m_iCurrentPoint + m_iEaserCount, to);
}
else
{
drawLines(painter, xMap, yMap, canvasRect, from, m_iCurrentPoint);
}
break;
case Sticks:
drawSticks(painter, xMap, yMap, canvasRect, from, to);
break;
case Steps:
drawSteps(painter, xMap, yMap, canvasRect, from, to);
break;
case Dots:
drawDots(painter, xMap, yMap, canvasRect, from, to);
break;
case NoCurve:
default:
break;
}
}
Calculate the current refreshed area first, then draw the points that need to be drawn currently.The data container size is 1000.
Code:
void Plot::updateCurve(int index)
{
CurveData *curveData = static_cast<CurveData *>( d_curve[index]->data() );
curveData->lock();
int iCurrentPoint = curveData->getCurrentPointIndex();//Get the index number of the latest data in the container, starting from 0
int iMaxPointCount = curveData->getMaxPointCount();//Get the capacity of a container
int size = curveData->size();//Get the current size of the container
if(iCurrentPoint == m_iLastPaintedPoint)
{
curveData->unlock();
return;
}
QRect canvasRect
= canvas
()->contentsRect
();
QRegion region;
//Save the area to be redrawn const QwtScaleMap xMap
= canvasMap
( d_curve
[index
]->xAxis
() );
const QwtScaleMap yMap
= canvasMap
( d_curve
[index
]->yAxis
() );
//Get the last starting X coordinate
int calc_start = (m_iLastPaintedPoint > 0)?m_iLastPaintedPoint -1:m_iLastPaintedPoint;
double start_x = xMap.transform( d_curve[index]->sample(calc_start).x() );
if ( iCurrentPoint > m_iLastPaintedPoint )//The data does not appear to flip, new data is added from the back
{
/*
Depending on the platform setting a clip might be an important
performance issue. F.e. for Qt Embedded this reduces the
part of the backing store that has to be copied out - maybe
to an unaccelerated frame buffer device.
*/
if(size < iMaxPointCount)//The initial container is not full
{
double stop_x = xMap.transform( d_curve[index]->sample(iCurrentPoint).x());
br
= QRect( start_x, canvasRect.
top(), stop_x
- start_x, canvasRect.
height());
}
else//After the container is full
{
if(iCurrentPoint + d_iEaserCount <= iMaxPointCount - 1)//If the erased point does not exceed the boundary
{
double stop_x = xMap.transform( d_curve[index]->sample(iCurrentPoint + d_iEaserCount).x());
br
= QRect( start_x, canvasRect.
top(), stop_x
- start_x, canvasRect.
height());
}
else//If the boundary is exceeded, only the remaining part is erased
{
br
= QRect( start_x, canvasRect.
top(), canvasRect.
right() - start_x, canvasRect.
height());
}
}
region += br;
}
else //Data flipped
{
//Tail
double stop_x = xMap.transform( d_curve[index]->sample(iCurrentPoint + d_iEaserCount).x());
double stop2_x = xMap.transform( d_curve[index]->sample(iMaxPointCount - 1).x());
QRect r1
( start_x , canvasRect.
top(), stop2_x
- start_x , canvasRect.
height());
region += r1;
//Head
double start2_x = xMap.transform( d_curve[index]->sample(0).x());
QRect r2
( start2_x, canvasRect.
top(), stop_x
- start2_x, canvasRect.
height());
region += r2;
}
//Set the latest data index of 12 curves
for(int i = 0;i < 12;i++)
{
d_curve[i]->SetCurrentPoint(iCurrentPoint);
}
m_iLastPaintedPoint = iCurrentPoint;
curveData->unlock();
//Partial refresh,replot 12 curves
canvas()->update(region);
}
In order to achieve the effect I want, how to reduce CPU consumption ?
Re: How to plot the real time data in qwtplot while erasing fixed-width old data?
QWidget does not offer to "erase" something - all you can do is to redraw from scratch. This is why QwtPlotDirectPainter is useful - it at least allows to draw on top of something existing.
Uwe
Re: How to plot the real time data in qwtplot while erasing fixed-width old data?
Quote:
Originally Posted by
Uwe
QWidget does not offer to "erase" something - all you can do is to redraw from scratch. This is why QwtPlotDirectPainter is useful - it at least allows to draw on top of something existing.
Uwe
Another way to achieve the effect I want,and in this way, the CPU usage is also very small,but need to write a lot of code,use the most primitive QPainter , not as concise as the qwt.The code snippet is as follows:
Code:
{
painter.drawPixmap(0,0,m_gridPixmap);//paint grid backgroup pixmap
painter.drawPixmap(0,0,m_curvePixmap);//paint curve pixmap on the top, and set QPainter::CompositionMode_Source mode when paint m_curvePixmap
painter.end();
}
Code:
void Mywidget::updateCurvePixmap()
{
painter.begin(&m_curvePixmap);
painter.
setCompositionMode(QPainter::CompositionMode_Source);
//Calculate the current 12 refreshed area first,and fill transparent
for(int i = 0; i < 12;i++)
{
QRect rect;
//The calculation process is omitted here!!! painter.fillRect(rect,Qt::transparent);
//Plot newly added data points
QPolygonF points;
//The process of filling points is omitted!!! painter.drawPoints(points);
}
painter.end();
//At this point, the new data point is successfully partial updated on the m_curvePixmap,Partial refresh widget
QRegion region;
//save all need updated area for(int i = 0; i < 12;i++)
{
QRect rect;
//The calculation process is omitted here!!! region += rect;
}
update(region );
}
Refer to this example:https://doc.qt.io/qt-5/qtwidgets-pai...n-example.html,
Used this class method:QPainter::setCompositionMode.
In order to achieve the effect I want, is this the only way?
Re: How to plot the real time data in qwtplot while erasing fixed-width old data?
Guess what you mean by this code snippet is that you draw each curve to its own pixmap first and the plot is done finally by drawing the pixmaps.
Then you would have only one drawPolyline call, when only one curve has changed.
Of course the code as being posted is not even close to what needs to be done in reality - but the idea is understood.
So your code will have a positive effect, when having several curves, that are updated on different intervals - but it won't help for a situation with one curve and many points.
Uwe
Re: How to plot the real time data in qwtplot while erasing fixed-width old data?
Quote:
Originally Posted by
Uwe
Guess what you mean by this code snippet is that you draw each curve to its own pixmap first and the plot is done finally by drawing the pixmaps.
Then you would have only one drawPolyline call, when only one curve has changed.
Of course the code as being posted is not even close to what needs to be done in reality - but the idea is understood.
So your code will have a positive effect, when having several curves, that are updated on different intervals - but it won't help for a situation with one curve and many points.
Uwe
All curves plot in the only m_curvePixmap,I update the code snippet in the previous reply. All curves partial update at the same time ,update every 30ms.
The original code looks too complicated,I just want to try to modify it as simple as the qwt.
Re: How to plot the real time data in qwtplot while erasing fixed-width old data?
Quote:
Originally Posted by
lockdown
All curves plot in the only m_curvePixmap,....
Then your code does not make any sense.
Uwe
Re: How to plot the real time data in qwtplot while erasing fixed-width old data?
Quote:
Originally Posted by
Uwe
Then your code does not make any sense.
Uwe
:) I have renewed.Thanks for the quick reply!
What I don’t understand is why these my two methods are so different, why the CPU consumption is so different? Looks like they are doing the same partial refresh.
Maybe as you said:QWidget does not offer to "erase" something - all you can do is to redraw from scratch.
Re: How to plot the real time data in qwtplot while erasing fixed-width old data?
Quote:
Originally Posted by
lockdown
What I don’t understand is why these my two methods are so different, why the CPU consumption is so different?
Hard to say what your code is doing as the code snippet does not show the real code.
But the plot canvas is actually using a backing store ( a QPixmap ), that gets updated on certain operations like when the size of the canvas is changing or replot has been called.
Beside that the content of the widget is always restored from this backing store only.
So when calling QWidget::update( region ) only all what happens is, that the rectangles from region will be copied from the backing store of the canvas to another backing store - the default one every widgets has.
Uwe
Re: How to plot the real time data in qwtplot while erasing fixed-width old data?
@lockdown Have you found a way to achieve this? because I am running in circles about the same issue and that would be really great help.