ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

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

2022-09-29 14:31:45  阅读:69  来源: 互联网

标签:


下面,详细讲解采用重写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;
}

标签:
来源:

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有