基于QWT实现示波器余晖绘图模式


下面,详细讲解采用重写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(yj517066295@126.com)
* 创建日期  : // 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;

/*!
  rief 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:

    /*!
      rief Paint attributes

      The default setting enables BackingStore and Opaque.

      sa setPaintAttribute(), testPaintAttribute()
     */
    enum PaintAttribute
    {
        /*!
          rief 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,

        /*!
          rief Try to fill the complete contents rectangle
                 of the plot canvas

          When using styled backgrounds Qt assumes, that the
          canvas doesnt 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,

        /*!
          rief 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;

    /*!
      rief Focus indicator
      The default setting is NoFocusIndicator
      sa setFocusIndicator(), focusIndicator(), drawFocusIndicator()
    */

    enum FocusIndicator
    {
        //! Dont 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 ( state.state() & QPaintEngine::DirtyBrush )
{
d_brush = state.brush();
}
if ( state.state() & QPaintEngine::DirtyBrushOrigin )
{
d_origin = state.brushOrigin();
}
}
virtual void drawRects(const QRectF *rects, int count )
{
for ( int i = 0; i < count; i++ )
border.rectList += rects[i];
}
virtual void drawRects(const QRect *rects, int count )
{
// to silence -Woverloaded-virtual
QwtNullPaintDevice::drawRects( rects, count );
}
virtual void drawPath( const QPainterPath &path )
{
const QRectF rect( QPointF( 0.0, 0.0 ), d_size );
if ( path.controlPointRect().contains( rect.center() ) )
{
setCornerRects( path );
alignCornerRects( rect );
background.path = path;
background.brush = d_brush;
background.origin = d_origin;
}
else
{
border.pathList += path;
}
}
void setCornerRects( const QPainterPath &path )
{
QPointF pos( 0.0, 0.0 );
for ( int i = 0; i < path.elementCount(); i++ )
{
QPainterPath::Element el = path.elementAt(i);
switch( el.type )
{
case QPainterPath::MoveToElement:
case QPainterPath::LineToElement:
{
pos.setX( el.x );
pos.setY( el.y );
break;
}
case QPainterPath::CurveToElement:
{
QRectF r( pos, QPointF( el.x, el.y ) );
clipRects += r.normalized();
pos.setX( el.x );
pos.setY( el.y );
break;
}
case QPainterPath::CurveToDataElement:
{
if ( clipRects.size() > 0 )
{
QRectF r = clipRects.last();
r.setCoords(
qMin( r.left(), el.x ),
qMin( r.top(), el.y ),
qMax( r.right(), el.x ),
qMax( r.bottom(), el.y )
);
clipRects.last() = r.normalized();
}
break;
}
}
}
}
protected:
virtual QSize sizeMetrics() const
{
return d_size;
}
private:
void alignCornerRects( const QRectF &rect )
{
for ( int i = 0; i < clipRects.size(); i++ )
{
QRectF &r = clipRects[i];
if ( r.center().x() < rect.center().x() )
r.setLeft( rect.left() );
else
r.setRight( rect.right() );
if ( r.center().y() < rect.center().y() )
r.setTop( rect.top() );
else
r.setBottom( rect.bottom() );
}
}
public:
QVector<QRectF> clipRects;
struct Border
{
QList<QPainterPath> pathList;
QList<QRectF> rectList;
QRegion clipRegion;
} border;
struct Background
{
QPainterPath path;
QBrush brush;
QPointF origin;
} background;
private:
const QSize d_size;
QPen d_pen;
QBrush d_brush;
QPointF d_origin;
};
static void qwtDrawBackground( QPainter *painter, MyPlotCanvas *canvas )
{
painter->save();
const QPainterPath borderClip = canvas->borderPath( canvas->rect() );
if ( !borderClip.isEmpty() )
painter->setClipPath( borderClip, Qt::IntersectClip );
const QBrush &brush =
canvas->palette().brush( canvas->backgroundRole() );
if ( brush.style() == Qt::TexturePattern )
{
QPixmap pm( canvas->size() );
QwtPainter::fillPixmap( canvas, pm );
painter->drawPixmap( 0, 0, pm );
}
else if ( brush.gradient() )
{
QVector<QRect> rects;
if ( brush.gradient()->coordinateMode() == QGradient::ObjectBoundingMode )
{
rects += canvas->rect();
}
else
{
rects = painter->clipRegion().rects();
}
#if 1
bool useRaster = false;
if ( painter->paintEngine()->type() == QPaintEngine::X11 )
{
// Qt 4.7.1: gradients on X11 are broken ( subrects +
// QGradient::StretchToDeviceMode ) and horrible slow.
// As workaround we have to use the raster paintengine.
// Even if the QImage -> QPixmap translation is slow
// it is three times faster, than using X11 directly
useRaster = true;
}
#endif
if ( useRaster )
{
QImage::Format format = QImage::Format_RGB32;
const QGradientStops stops = brush.gradient()->stops();
for ( int i = 0; i < stops.size(); i++ )
{
if ( stops[i].second.alpha() != 255 )
{
// dont use Format_ARGB32_Premultiplied. Its
// recommended by the Qt docs, but QPainter::drawImage()
// is horrible slow on X11.
format = QImage::Format_ARGB32;
break;
}
}
QImage image( canvas->size(), format );
QPainter p( &image );
p.setPen( Qt::NoPen );
p.setBrush( brush );
p.drawRects( rects );
p.end();
painter->drawImage( 0, 0, image );
}
else
{
painter->setPen( Qt::NoPen );
painter->setBrush( brush );
painter->drawRects( rects );
}
}
else
{
painter->setPen( Qt::NoPen );
painter->setBrush( brush );
painter->drawRects( painter->clipRegion().rects() );
}
painter->restore();
}
static inline void qwtRevertPath( QPainterPath &path )
{
if ( path.elementCount() == 4 )
{
QPainterPath::Element el0 = path.elementAt(0);
QPainterPath::Element el3 = path.elementAt(3);
path.setElementPositionAt( 0, el3.x, el3.y );
path.setElementPositionAt( 3, el0.x, el0.y );
}
}
static QPainterPath qwtCombinePathList( const QRectF &rect,
const QList<QPainterPath> &pathList )
{
if ( pathList.isEmpty() )
return QPainterPath();
QPainterPath ordered[8]; // starting top left
for ( int i = 0; i < pathList.size(); i++ )
{
int index = -1;
QPainterPath subPath = pathList[i];
const QRectF br = pathList[i].controlPointRect();
if ( br.center().x() < rect.center().x() )
{
if ( br.center().y() < rect.center().y() )
{
if ( qAbs( br.top() - rect.top() ) <
qAbs( br.left() - rect.left() ) )
{
index = 1;
}
else
{
index = 0;
}
}
else
{
if ( qAbs( br.bottom() - rect.bottom() ) <
qAbs( br.left() - rect.left() ) )
{
index = 6;
}
else
{
index = 7;
}
}
if ( subPath.currentPosition().y() > br.center().y() )
qwtRevertPath( subPath );
}
else
{
if ( br.center().y() < rect.center().y() )
{
if ( qAbs( br.top() - rect.top() ) <
qAbs( br.right() - rect.right() ) )
{
index = 2;
}
else
{
index = 3;
}
}
else
{
if ( qAbs( br.bottom() - rect.bottom() ) <
qAbs( br.right() - rect.right() ) )
{
index = 5;
}
else
{
index = 4;
}
}
if ( subPath.currentPosition().y() < br.center().y() )
qwtRevertPath( subPath );
}
ordered[index] = subPath;
}
for ( int i = 0; i < 4; i++ )
{
if ( ordered[ 2 * i].isEmpty() != ordered[2 * i + 1].isEmpty() )
{
// we dont accept incomplete rounded borders
return QPainterPath();
}
}
const QPolygonF corners( rect );
QPainterPath path;
//path.moveTo( rect.topLeft() );
for ( int i = 0; i < 4; i++ )
{
if ( ordered[2 * i].isEmpty() )
{
path.lineTo( corners[i] );
}
else
{
path.connectPath( ordered[2 * i] );
path.connectPath( ordered[2 * i + 1] );
}
}
path.closeSubpath();
#if 0
return path.simplified();
#else
return path;
#endif
}
static inline void qwtDrawStyledBackground(
QWidget *w, QPainter *painter )
{
QStyleOption opt;
opt.initFrom(w);
w->style()->drawPrimitive( QStyle::PE_Widget, &opt, painter, w);
}
static QWidget *qwtBackgroundWidget( QWidget *w )
{
if ( w->parentWidget() == NULL )
return w;
if ( w->autoFillBackground() )
{
const QBrush brush = w->palette().brush( w->backgroundRole() );
if ( brush.color().alpha() > 0 )
return w;
}
if ( w->testAttribute( Qt::WA_StyledBackground ) )
{
QImage image( 1, 1, QImage::Format_ARGB32 );
image.fill( Qt::transparent );
QPainter painter( &image );
painter.translate( -w->rect().center() );
qwtDrawStyledBackground( w, &painter );
painter.end();
if ( qAlpha( image.pixel( 0, 0 ) ) != 0 )
return w;
}
return qwtBackgroundWidget( w->parentWidget() );
}
static void qwtFillBackground( QPainter *painter,
QWidget *widget, const QVector<QRectF> &fillRects )
{
if ( fillRects.isEmpty() )
return;
QRegion clipRegion;
if ( painter->hasClipping() )
clipRegion = painter->transform().map( painter->clipRegion() );
else
clipRegion = widget->contentsRect();
// Try to find out which widget fills
// the unfilled areas of the styled background
QWidget *bgWidget = qwtBackgroundWidget( widget->parentWidget() );
for ( int i = 0; i < fillRects.size(); i++ )
{
const QRect rect = fillRects[i].toAlignedRect();
if ( clipRegion.intersects( rect ) )
{
QPixmap pm( rect.size() );
QwtPainter::fillPixmap( bgWidget, pm, widget->mapTo( bgWidget, rect.topLeft() ) );
painter->drawPixmap( rect, pm );
}
}
}
static void qwtFillBackground( QPainter *painter, MyPlotCanvas *canvas )
{
QVector<QRectF> rects;
if ( canvas->testAttribute( Qt::WA_StyledBackground ) )
{
QwtStyleSheetRecorder recorder( canvas->size() );
QPainter p( &recorder );
qwtDrawStyledBackground( canvas, &p );
p.end();
if ( recorder.background.brush.isOpaque() )
rects = recorder.clipRects;
else
rects += canvas->rect();
}
else
{
const QRectF r = canvas->rect();
const double radius = canvas->borderRadius();
if ( radius > 0.0 )
{
QSizeF sz( radius, radius );
rects += QRectF( r.topLeft(), sz );
rects += QRectF( r.topRight() - QPointF( radius, 0 ), sz );
rects += QRectF( r.bottomRight() - QPointF( radius, radius ), sz );
rects += QRectF( r.bottomLeft() - QPointF( 0, radius ), sz );
}
}
qwtFillBackground( painter, canvas, rects);
}
class MyPlotCanvas::PrivateData
{
public:
PrivateData():
focusIndicator( NoFocusIndicator ),
borderRadius( 0 ),
paintAttributes( 0 ),
backingStore( NULL )
{
styleSheet.hasBorder = false;
}
~PrivateData()
{
delete backingStore;
}
FocusIndicator focusIndicator;
double borderRadius;
MyPlotCanvas::PaintAttributes paintAttributes;
QPixmap *backingStore;
struct StyleSheet
{
bool hasBorder;
QPainterPath borderPath;
QVector<QRectF> cornerRects;
struct StyleSheetBackground
{
QBrush brush;
QPointF origin;
} background;
} styleSheet;
};
/*!
rief Constructor
param plot Parent plot widget
sa QwtPlot::setCanvas()
*/
MyPlotCanvas::MyPlotCanvas( QwtPlot *plot ):
QFrame( plot )
{
setFrameStyle( QFrame::Panel | QFrame::Sunken );
setLineWidth( 2 );
d_data = new PrivateData;
m_ePaintMode = MyPlotCanvas::PaintNormal;
#ifndef QT_NO_CURSOR
setCursor( Qt::CrossCursor );
#endif
setAutoFillBackground( true );
setPaintAttribute( MyPlotCanvas::BackingStore, true );
setPaintAttribute( MyPlotCanvas::Opaque, true );
setPaintAttribute( MyPlotCanvas::HackStyledBackground, true );
}
//! Destructor
MyPlotCanvas::~MyPlotCanvas()
{
delete d_data;
}
//! Return parent plot widget
QwtPlot *MyPlotCanvas::plot()
{
return qobject_cast<QwtPlot *>( parent() );
}
//! Return parent plot widget
const QwtPlot *MyPlotCanvas::plot() const
{
return qobject_cast<const QwtPlot *>( parent() );
}
/*!
rief Changing the paint attributes
param attribute Paint attribute
param on On/Off
sa testPaintAttribute(), backingStore()
*/
void MyPlotCanvas::setPaintAttribute( PaintAttribute attribute, bool on )
{
if ( bool( d_data->paintAttributes & attribute ) == on )
return;
if ( on )
d_data->paintAttributes |= attribute;
else
d_data->paintAttributes &= ~attribute;
switch ( attribute )
{
case BackingStore:
{
if ( on )
{
if ( d_data->backingStore == NULL )
d_data->backingStore = new QPixmap();
if ( isVisible() )
{
#if QT_VERSION >= 0x050000
*d_data->backingStore = grab( rect() );
#else
*d_data->backingStore =
QPixmap::grabWidget( this, rect() );
#endif
}
}
else
{
delete d_data->backingStore;
d_data->backingStore = NULL;
}
break;
}
case Opaque:
{
if ( on )
setAttribute( Qt::WA_OpaquePaintEvent, true );
break;
}
case HackStyledBackground:
case ImmediatePaint:
{
break;
}
}
}
/*!
Test whether a paint attribute is enabled
param attribute Paint attribute
eturn true, when attribute is enabled
sa setPaintAttribute()
*/
bool MyPlotCanvas::testPaintAttribute( PaintAttribute attribute ) const
{
return d_data->paintAttributes & attribute;
}
//! 
eturn Backing store, might be null
const QPixmap *MyPlotCanvas::backingStore() const
{
return d_data->backingStore;
}
//! Invalidate the internal backing store
void MyPlotCanvas::invalidateBackingStore()
{
if ( d_data->backingStore )
*d_data->backingStore = QPixmap();
}
/*!
Set the focus indicator
sa FocusIndicator, focusIndicator()
*/
void MyPlotCanvas::setFocusIndicator( FocusIndicator focusIndicator )
{
d_data->focusIndicator = focusIndicator;
}
/*!
eturn Focus indicator
sa FocusIndicator, setFocusIndicator()
*/
MyPlotCanvas::FocusIndicator MyPlotCanvas::focusIndicator() const
{
return d_data->focusIndicator;
}
/*!
Set the radius for the corners of the border frame
param radius Radius of a rounded corner
sa borderRadius()
*/
void MyPlotCanvas::setBorderRadius( double radius )
{
d_data->borderRadius = qMax( 0.0, radius );
}
/*!
eturn Radius for the corners of the border frame
sa setBorderRadius()
*/
double MyPlotCanvas::borderRadius() const
{
return d_data->borderRadius;
}
/*!
Qt event handler for QEvent::PolishRequest and QEvent::StyleChange
param event Qt Event
eturn See QFrame::event()
*/
bool MyPlotCanvas::event( QEvent *event )
{
if ( event->type() == QEvent::PolishRequest )
{
if ( testPaintAttribute( MyPlotCanvas::Opaque ) )
{
// Setting a style sheet changes the
// Qt::WA_OpaquePaintEvent attribute, but we insist
// on painting the background.
setAttribute( Qt::WA_OpaquePaintEvent, true );
}
}
if ( event->type() == QEvent::PolishRequest ||
event->type() == QEvent::StyleChange )
{
updateStyleSheetInfo();
}
return QFrame::event( event );
}
/*!
Paint event
param event Paint event
*/
void MyPlotCanvas::paintEvent( QPaintEvent *event )
{
QPainter painter( this );
painter.setClipRegion( event->region() );
if ( testPaintAttribute( MyPlotCanvas::BackingStore ) &&
d_data->backingStore != NULL )
{
QPixmap &bs = *d_data->backingStore;
qreal pixelRatio = 1.0;
#if QT_VERSION >= 0x050000
pixelRatio = bs.devicePixelRatio();
#endif
if ( m_ePaintMode == PaintNormal )
{
// 常规模式
if ( bs.size() != size() * pixelRatio )
{
bs = QwtPainter::backingStore( this, size() );
if ( testAttribute(Qt::WA_StyledBackground) )
{
QPainter p( &bs );
qwtFillBackground( &p, this );
drawCanvas( &p, true );
}
else
{
QPainter p;
if ( d_data->borderRadius <= 0.0 )
{
QwtPainter::fillPixmap( this, bs );
p.begin( &bs );
drawCanvas( &p, false );
}
else
{
p.begin( &bs );
qwtFillBackground( &p, this );
drawCanvas( &p, true );
}
if ( frameWidth() > 0 )
drawBorder( &p );
}
}
}
else
{
// 余烬模式
QPainter p( &bs );
p.begin( &bs );
drawCanvas( &p, false );
if ( frameWidth() > 0 )
drawBorder( &p );
}
painter.drawPixmap( 0, 0, *d_data->backingStore );
}
else
{
if ( testAttribute(Qt::WA_StyledBackground ) )
{
if ( testAttribute( Qt::WA_OpaquePaintEvent ) )
{
qwtFillBackground( &painter, this );
drawCanvas( &painter, true );
}
else
{
drawCanvas( &painter, false );
}
}
else
{
if ( testAttribute( Qt::WA_OpaquePaintEvent ) )
{
if ( autoFillBackground() )
{
qwtFillBackground( &painter, this );
qwtDrawBackground( &painter, this );
}
}
else
{
if ( borderRadius() > 0.0 )
{
QPainterPath clipPath;
clipPath.addRect( rect() );
clipPath = clipPath.subtracted( borderPath( rect() ) );
painter.save();
painter.setClipPath( clipPath, Qt::IntersectClip );
qwtFillBackground( &painter, this );
qwtDrawBackground( &painter, this );
painter.restore();
}
}
drawCanvas( &painter, false );
if ( frameWidth() > 0 )
drawBorder( &painter );
}
}
if ( hasFocus() && focusIndicator() == CanvasFocusIndicator )
drawFocusIndicator( &painter );
}
void MyPlotCanvas::drawCanvas( QPainter *painter, bool withBackground )
{
bool hackStyledBackground = false;
if ( withBackground && testAttribute( Qt::WA_StyledBackground )
&& testPaintAttribute( HackStyledBackground ) )
{
// Antialiasing rounded borders is done by
// inserting pixels with colors between the
// border color and the color on the canvas,
// When the border is painted before the plot items
// these colors are interpolated for the canvas
// and the plot items need to be clipped excluding
// the anialiased pixels. In situations, where
// the plot items fill the area at the rounded
// borders this is noticeable.
// The only way to avoid these annoying "artefacts"
// is to paint the border on top of the plot items.
if ( d_data->styleSheet.hasBorder &&
!d_data->styleSheet.borderPath.isEmpty() )
{
// We have a border with at least one rounded corner
hackStyledBackground = true;
}
}
if ( withBackground )
{
painter->save();
if ( testAttribute( Qt::WA_StyledBackground ) )
{
if ( hackStyledBackground )
{
// paint background without border
painter->setPen( Qt::NoPen );
painter->setBrush( d_data->styleSheet.background.brush );
painter->setBrushOrigin( d_data->styleSheet.background.origin );
painter->setClipPath( d_data->styleSheet.borderPath );
painter->drawRect( contentsRect() );
}
else
{
qwtDrawStyledBackground( this, painter );
}
}
else if ( autoFillBackground() )
{
painter->setPen( Qt::NoPen );
painter->setBrush( palette().brush( backgroundRole() ) );
if ( d_data->borderRadius > 0.0 && ( rect() == frameRect() ) )
{
if ( frameWidth() > 0 )
{
painter->setClipPath( borderPath( rect() ) );
painter->drawRect( rect() );
}
else
{
painter->setRenderHint( QPainter::Antialiasing, true );
painter->drawPath( borderPath( rect() ) );
}
}
else
{
painter->drawRect( rect() );
}
}
painter->restore();
}
painter->save();
if ( !d_data->styleSheet.borderPath.isEmpty() )
{
painter->setClipPath(
d_data->styleSheet.borderPath, Qt::IntersectClip );
}
else
{
if ( d_data->borderRadius > 0.0 )
painter->setClipPath( borderPath( frameRect() ), Qt::IntersectClip );
else
painter->setClipRect( contentsRect(), Qt::IntersectClip );
}
plot()->drawCanvas( painter );
painter->restore();
if ( withBackground && hackStyledBackground )
{
// Now paint the border on top
QStyleOptionFrame opt;
opt.initFrom(this);
style()->drawPrimitive( QStyle::PE_Frame, &opt, painter, this);
}
}
/*!
Draw the border of the plot canvas
param painter Painter
sa setBorderRadius()
*/
void MyPlotCanvas::drawBorder( QPainter *painter )
{
if ( d_data->borderRadius > 0 )
{
if ( frameWidth() > 0 )
{
QwtPainter::drawRoundedFrame( painter, QRectF( frameRect() ),
d_data->borderRadius, d_data->borderRadius,
palette(), frameWidth(), frameStyle() );
}
}
else
{
#if QT_VERSION >= 0x040500
#if QT_VERSION < 0x050000
QStyleOptionFrameV3 opt;
#else
QStyleOptionFrame opt;
#endif
opt.init(this);
int frameShape  = frameStyle() & QFrame::Shape_Mask;
int frameShadow = frameStyle() & QFrame::Shadow_Mask;
opt.frameShape = QFrame::Shape( int( opt.frameShape ) | frameShape );
#if 0
opt.rect = frameRect();
#endif
switch (frameShape)
{
case QFrame::Box:
case QFrame::HLine:
case QFrame::VLine:
case QFrame::StyledPanel:
case QFrame::Panel:
{
opt.lineWidth = lineWidth();
opt.midLineWidth = midLineWidth();
break;
}
default:
{
opt.lineWidth = frameWidth();
break;
}
}
if ( frameShadow == Sunken )
opt.state |= QStyle::State_Sunken;
else if ( frameShadow == Raised )
opt.state |= QStyle::State_Raised;
style()->drawControl(QStyle::CE_ShapedFrame, &opt, painter, this);
#else
drawFrame( painter );
#endif
}
}
/*!
Resize event
param event Resize event
*/
void MyPlotCanvas::resizeEvent( QResizeEvent *event )
{
QFrame::resizeEvent( event );
updateStyleSheetInfo();
}
/*!
Draw the focus indication
param painter Painter
*/
void MyPlotCanvas::drawFocusIndicator( QPainter *painter )
{
const int margin = 1;
QRect focusRect = contentsRect();
focusRect.setRect( focusRect.x() + margin, focusRect.y() + margin,
focusRect.width() - 2 * margin, focusRect.height() - 2 * margin );
QwtPainter::drawFocusRect( painter, this, focusRect );
}
/*!
Invalidate the paint cache and repaint the canvas
sa invalidatePaintCache()
*/
void MyPlotCanvas::replot()
{
if ( m_ePaintMode == PaintNormal )
{
invalidateBackingStore();
}
if ( testPaintAttribute( MyPlotCanvas::ImmediatePaint ) )
repaint( contentsRect() );
else
update( contentsRect() );
}
//! Update the cached information about the current style sheet
void MyPlotCanvas::updateStyleSheetInfo()
{
if ( !testAttribute(Qt::WA_StyledBackground ) )
return;
QwtStyleSheetRecorder recorder( size() );
QPainter painter( &recorder );
QStyleOption opt;
opt.initFrom(this);
style()->drawPrimitive( QStyle::PE_Widget, &opt, &painter, this);
painter.end();
d_data->styleSheet.hasBorder = !recorder.border.rectList.isEmpty();
d_data->styleSheet.cornerRects = recorder.clipRects;
if ( recorder.background.path.isEmpty() )
{
if ( !recorder.border.rectList.isEmpty() )
{
d_data->styleSheet.borderPath =
qwtCombinePathList( rect(), recorder.border.pathList );
}
}
else
{
d_data->styleSheet.borderPath = recorder.background.path;
d_data->styleSheet.background.brush = recorder.background.brush;
d_data->styleSheet.background.origin = recorder.background.origin;
}
}
/*!
Calculate the painter path for a styled or rounded border
When the canvas has no styled background or rounded borders
the painter path is empty.
param rect Bounding rectangle of the canvas
eturn Painter path, that can be used for clipping
*/
QPainterPath MyPlotCanvas::borderPath( const QRect &rect ) const
{
if ( testAttribute(Qt::WA_StyledBackground ) )
{
QwtStyleSheetRecorder recorder( rect.size() );
QPainter painter( &recorder );
QStyleOption opt;
opt.initFrom(this);
opt.rect = rect;
style()->drawPrimitive( QStyle::PE_Widget, &opt, &painter, this);
painter.end();
if ( !recorder.background.path.isEmpty() )
return recorder.background.path;
if ( !recorder.border.rectList.isEmpty() )
return qwtCombinePathList( rect, recorder.border.pathList );
}
else if ( d_data->borderRadius > 0.0 )
{
double fw2 = frameWidth() * 0.5;
QRectF r = QRectF(rect).adjusted( fw2, fw2, -fw2, -fw2 );
QPainterPath path;
path.addRoundedRect( r, d_data->borderRadius, d_data->borderRadius );
return path;
}
return QPainterPath();
}
void MyPlotCanvas::setPaintMode(MyPlotCanvas::PaintMode mode)
{
m_ePaintMode = mode;
}

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/290195.html

(0)
上一篇 2022年9月29日
下一篇 2022年9月29日

相关推荐

发表回复

登录后才能评论