Qwt提供了强大的图形绘制的功能,Qwt自带的refreshtest例子可以比较全面的展示了采用Qwt绘制动态曲线的功能。Qwt可以用来实现示波器的实时波形绘制,但示波器的余晖模式无法用Qwt的原生库来实现,因此,作者研究了Qwt绘图源码,通过重写QwtPlotCanvas类实现了示波器的余晖绘图功能,由于采用的是基于位图的实现方式,可以达到非常好的性能,和普通波形绘制没有差异。下图是余晖模式的效果。
下面,详细讲解采用重写QwtPlotCanvas的方式实现余晖绘图功能,并附上完整代码。要实现余晖功能,还需要对QwtPlot类和QwtPlotCanvas类的绘图实现方式有所了解,这些都是Qwt的基础功能,就不详细叙述了。
在QwtPlotCanvas类的私有成员变量中,定义了一个位图对象QPixmap *backingStore,位图对象存储的是采用BackingStore模式绘图时的位图缓存,大概用意就是绘制图形的时候先在位图对象缓存中绘制,然后再将位图缓存一次性显示到窗体中,以提高绘图效率,在这种模式下,每次在重新绘图时,会先将位图缓存清除,因此,无法保留历史的绘图数据。因此,利用位图缓存的机制,我们只要重写QwtPlotCanvas,自定义一种绘图模式(余晖模式),在余晖模式下不进行绘图缓存的清除,便可以达到余晖绘图的效果。并且,通过开发接口,可以实现从余晖模式和普通模式之间的动态切换。下面是详细的代码实现。
1、实现方式:MyPlotCanvas即为重写的QwtPlotCanvas类,需要将MyPlotCanvas对象与QwtPlot类进行关联,调用代码如下:
// 设置背景面板风格
MyPlotCanvas *pCanvas = new MyPlotCanvas();
pCanvas->setFrameStyle( QFrame::Box | QFrame::Plain );
pCanvas->setLineWidth( 1 );
pCanvas->setPalette( Qt::black );
pPlot->setCanvas( m_pCanvas );
// 设置普通绘图模式(每次重绘前擦除)
pCanvas->setPaintMode( MyPlotCanvas::PaintNormal );
// 设置余晖绘图模式(每次重绘不擦除)
pCanvas->setPaintMode( MyPlotCanvas::PaintEmbers );
2、MyPlotCanvas头文件:
/************************************************************************
* 文件名称 : // my_plot_canvas.h
* 内容摘要 : // 基于QwtPlotCanvas改造,以支持无尽余晖绘图功能。
* 创建人员 : // YuJ([email protected])
* 创建日期 : // 2021年12月15日
************************************************************************/
#ifndef AMC_PLOT_CANVAS_H
#define AMC_PLOT_CANVAS_H
#include "qwt_global.h"
#include <qframe.h>
#include <qpainterpath.h>
class QwtPlot;
class QPixmap;
/*!
\brief Canvas of a QwtPlot.
Canvas is the widget where all plot items are displayed
\sa QwtPlot::setCanvas(), QwtPlotGLCanvas
*/
class MyPlotCanvas : public QFrame
{
Q_OBJECT
Q_PROPERTY( double borderRadius READ borderRadius WRITE setBorderRadius )
public:
/*!
\brief Paint attributes
The default setting enables BackingStore and Opaque.
\sa setPaintAttribute(), testPaintAttribute()
*/
enum PaintAttribute
{
/*!
\brief Paint double buffered reusing the content
of the pixmap buffer when possible.
Using a backing store might improve the performance
significantly, when working with widget overlays ( like rubber bands ).
Disabling the cache might improve the performance for
incremental paints (using QwtPlotDirectPainter ).
\sa backingStore(), invalidateBackingStore()
*/
BackingStore = 1,
/*!
\brief Try to fill the complete contents rectangle
of the plot canvas
When using styled backgrounds Qt assumes, that the
canvas doesn't fill its area completely
( f.e because of rounded borders ) and fills the area
below the canvas. When this is done with gradients it might
result in a serious performance bottleneck - depending on the size.
When the Opaque attribute is enabled the canvas tries to
identify the gaps with some heuristics and to fill those only.
\warning Will not work for semitransparent backgrounds
*/
Opaque = 2,
/*!
\brief Try to improve painting of styled backgrounds
QwtPlotCanvas supports the box model attributes for
customizing the layout with style sheets. Unfortunately
the design of Qt style sheets has no concept how to
handle backgrounds with rounded corners - beside of padding.
When HackStyledBackground is enabled the plot canvas tries
to separate the background from the background border
by reverse engineering to paint the background before and
the border after the plot items. In this order the border
gets perfectly antialiased and you can avoid some pixel
artifacts in the corners.
*/
HackStyledBackground = 4,
/*!
When ImmediatePaint is set replot() calls repaint()
instead of update().
\sa replot(), QWidget::repaint(), QWidget::update()
*/
ImmediatePaint = 8
};
//! Paint attributes
typedef QFlags<PaintAttribute> PaintAttributes;
/*!
\brief Focus indicator
The default setting is NoFocusIndicator
\sa setFocusIndicator(), focusIndicator(), drawFocusIndicator()
*/
enum FocusIndicator
{
//! Don't paint a focus indicator
NoFocusIndicator,
/*!
The focus is related to the complete canvas.
Paint the focus indicator using drawFocusIndicator()
*/
CanvasFocusIndicator,
/*!
The focus is related to an item (curve, point, ...) on
the canvas. It is up to the application to display a
focus indication using f.e. highlighting.
*/
ItemFocusIndicator
};
enum PaintMode
{
PaintNormal, // 常规模式
PaintEmbers, // 余烬模式
};
explicit MyPlotCanvas( QwtPlot * = NULL );
virtual ~MyPlotCanvas();
QwtPlot *plot();
const QwtPlot *plot() const;
void setFocusIndicator( FocusIndicator );
FocusIndicator focusIndicator() const;
void setBorderRadius( double );
double borderRadius() const;
void setPaintAttribute( PaintAttribute, bool on = true );
bool testPaintAttribute( PaintAttribute ) const;
const QPixmap *backingStore() const;
void invalidateBackingStore();
virtual bool event( QEvent * );
Q_INVOKABLE QPainterPath borderPath( const QRect & ) const;
void setPaintMode( PaintMode mode );
public Q_SLOTS:
void replot();
protected:
virtual void paintEvent( QPaintEvent * );
virtual void resizeEvent( QResizeEvent * );
virtual void drawFocusIndicator( QPainter * );
virtual void drawBorder( QPainter * );
void updateStyleSheetInfo();
private:
void drawCanvas( QPainter *, bool withBackground );
PaintMode m_ePaintMode;
class PrivateData;
PrivateData *d_data;
};
Q_DECLARE_OPERATORS_FOR_FLAGS( MyPlotCanvas::PaintAttributes )
#endif
MyPlotCanvas类实现:
#include "my_plot_canvas.h"
#include "qwt_painter.h"
#include "qwt_null_paintdevice.h"
#include "qwt_math.h"
#include "qwt_plot.h"
#include <qpainter.h>
#include <qstyle.h>
#include <qstyleoption.h>
#include <qpaintengine.h>
#include <qevent.h>
class QwtStyleSheetRecorder: public QwtNullPaintDevice
{
public:
QwtStyleSheetRecorder( const QSize &size ):
d_size( size )
{
}
virtual void updateState( const QPaintEngineState &state )
{
if ( state.state() & QPaintEngine::DirtyPen )
{
d_pen = state.pen();
}
if (