QWT基础教程之curvdemo1

2020-1-30

最终运行效果如下所示

在本次教程里,为了方便我们掌握工程的整体架构,我们需要先了解类图制作:类图制作,我们后面会经常用到类图

下面我们就进入今天的案例介绍。

打开工程文件,我们发现只有一个curvdemo1.cpp文件,但是这个文件要比上一个案例simpleplot大一点,一眼望去不太好掌握整体,所以下面先给出它的架构图

工程的顶层类是MainWin,它继承自QFrame,有一个private属性d_curves,一个public方法MainWin,两个protected方法paintEvent和drawEvent方法,一个private方法shiftDown。下面我们逐个来介绍这些属性与方法的实现。

首先是包含必要的头文件

#include <qwt_scale_map.h>
#include <qwt_plot_curve.h>
#include <qwt_symbol.h>
#include <qwt_math.h>
#include <qcolor.h>
#include <qpainter.h>
#include <qapplication.h>
#include <qframe.h>

然后是声明一些重要的全局变量

//
//   Array Sizes
//
const int Size = 27;
//数据点个数
const int CurvCnt = 6;
//将来绘制在QFrame中的曲线条数

//
//   Arrays holding the values
//
double xval[Size];
double yval[Size];
//用来存放曲线数据点
QwtScaleMap xMap;
QwtScaleMap yMap;
//用来标识曲线坐标轴范围

定义MainWin类

class MainWin : public QFrame
{
public:
    MainWin();

protected:
    virtual void paintEvent( QPaintEvent * );
    void drawContents( QPainter *p );

private:
    void shiftDown( QRect &rect, int offset ) const;

    QwtPlotCurve d_curves[CurvCnt];
};

实现构造函数

MainWin::MainWin()
{
    int i;

    xMap.setScaleInterval( -0.5, 10.5 );
    yMap.setScaleInterval( -1.1, 1.1 );
    //xMap是QwtScaleMap类,关于这个类,官方文档里是这么解释的:QwtScaleMap提供了从范围的坐标系统到绘图设备的线性坐标系统,反之亦然。
    //说白了就是从相对坐标到绝对坐标的转换。举个例子:我在家门口说,我要往南走100m,天上的卫星看到了就会想到,这个人从某个纬度经度出发,移动了多少纬度经度。在这里,我们设置了一个范围,这个范围将来会被映射成绝对坐标绘制到QFrame中。

    //
    //  Frame style
    //
    setFrameStyle( QFrame::Box | QFrame::Raised );
    setLineWidth( 2 );
    setMidLineWidth( 3 );
    //设置框架属性,QFrame是是Qt中的一个框架类,它绘制框架并且调用一个虚函数drawContents()来填充这个框架。这个函数是被子类重新实现的。这里至少还有两个有用的函数:drawFrame()和frameChanged()。

    //
    // Calculate values
    //
    for( i = 0; i < Size; i++ )
    {
        xval[i] = double( i ) * 10.0 / double( Size - 1 );
        yval[i] = qSin( xval[i] ) * qCos( 2.0 * xval[i] );
    }
    //生成数据点

    //
    //  define curve styles
    //
    i = 0;

    d_curves[i].setSymbol( new QwtSymbol( QwtSymbol::Cross, Qt::NoBrush,
        QPen( Qt::black ), QSize( 5, 5 ) ) );
    d_curves[i].setPen( Qt::darkGreen );
    d_curves[i].setStyle( QwtPlotCurve::Lines );
    d_curves[i].setCurveAttribute( QwtPlotCurve::Fitted );
    i++;

    d_curves[i].setSymbol( new QwtSymbol( QwtSymbol::Ellipse, Qt::yellow,
        QPen( Qt::blue ), QSize( 5, 5 ) ) );
    d_curves[i].setPen( Qt::red );
    d_curves[i].setStyle( QwtPlotCurve::Sticks );
    i++;

    d_curves[i].setPen( Qt::darkBlue );
    d_curves[i].setStyle( QwtPlotCurve::Lines );
    i++;

    d_curves[i].setPen( Qt::darkBlue );
    d_curves[i].setStyle( QwtPlotCurve::Lines );
    d_curves[i].setRenderHint( QwtPlotItem::RenderAntialiased );
    i++;

    d_curves[i].setPen( Qt::darkCyan );
    d_curves[i].setStyle( QwtPlotCurve::Steps );
    i++;

    d_curves[i].setSymbol( new QwtSymbol( QwtSymbol::XCross, Qt::NoBrush,
        QPen( Qt::darkMagenta ), QSize( 5, 5 ) ) );
    d_curves[i].setStyle( QwtPlotCurve::NoCurve );
    i++;
    //定义曲线


    //
    // attach data
    //
    for( i = 0; i < CurvCnt; i++ )
        d_curves[i].setRawSamples( xval, yval, Size );
    //为曲线设置数据
}

实现绘图事件函数

void MainWin::paintEvent( QPaintEvent *event )
{
    QFrame::paintEvent( event );
    //先调用父类的绘图事件

    QPainter painter( this );
    painter.setClipRect( contentsRect() );
    //设置一支画笔用来绘制曲线
    drawContents( &painter );
    //绘制曲线
}

绘图事件函数是一个Qt中的虚函数,这个函数会自动被调用。
当发生以下情况时会产生绘制事件并调用paintEvent()函数:
1.在窗口部件第一次显示时,系统会自动产生一个绘图事件,从而强制绘制这个窗口部件。

2.当重新调整窗口部件的大小时,系统也会产生一个绘制事件。

3.当窗口部件被其他窗口部件遮挡,然后又再次显示出来的时候,就会对那些隐藏的区域产生一个绘制事件。

同时可以调用QWidget::update()或者QWidget::repaint()来强制产生一个绘制事件。二者的区别是:
repaint()函数会强制产生一个即时的重绘事件,而update()函数只是在Qt下一次处理事件时才调用一次绘制事件。
如果多次调用update(),Qt会把连续多次的绘制事件压缩成一个单一的绘制事件,这样可避免闪烁现象。

//
//  REDRAW CONTENTS
//
void MainWin::drawContents( QPainter *painter )
{
    int deltay, i;

    QRect r = contentsRect();
    //获取绘图矩形,关于这个QRect,在官方文档里是这么解释的:一个矩形通常是用一个左上角坐标和一个尺寸来表示。说白了就是,你可以通过这个对象在屏幕上唯一确定一个矩形

    deltay = r.height() / CurvCnt - 1;

    r.setHeight( deltay );
    //重新设置矩形高度

    //
    //  draw curves
    //
    for ( i = 0; i < CurvCnt; i++ )
    {
        xMap.setPaintInterval( r.left(), r.right() );
        yMap.setPaintInterval( r.top(), r.bottom() );
        //设置map(确定相对坐标)

        painter->setRenderHint( QPainter::Antialiasing,
            d_curves[i].testRenderHint( QwtPlotItem::RenderAntialiased ) );
        //设置绘图工具渲染属性
        d_curves[i].draw( painter, xMap, yMap, r );
        //将曲线用绘图工具绘制在r矩形内,横纵坐标由xMap和yMap确定

        shiftDown( r, deltay );
        //将绘图矩形区域下移一个高度,准备绘制下一个曲线
    }

    //
    // draw titles
    //
    r = contentsRect();     // reset r
    painter->setFont( QFont( "Helvetica", 8 ) );

    const int alignment = Qt::AlignTop | Qt::AlignHCenter;

    painter->setPen( Qt::black );

    painter->drawText( 0, r.top(), r.width(), painter->fontMetrics().height(),
        alignment, "Style: Line/Fitted, Symbol: Cross" );
    shiftDown( r, deltay );

    painter->drawText( 0, r.top(), r.width(), painter->fontMetrics().height(),
        alignment, "Style: Sticks, Symbol: Ellipse" );
    shiftDown( r, deltay );

    painter->drawText( 0 , r.top(), r.width(), painter->fontMetrics().height(),
        alignment, "Style: Lines, Symbol: None" );
    shiftDown( r, deltay );

    painter->drawText( 0 , r.top(), r.width(), painter->fontMetrics().height(),
        alignment, "Style: Lines, Symbol: None, Antialiased" );
    shiftDown( r, deltay );

    painter->drawText( 0, r.top(), r.width(), painter->fontMetrics().height(),
        alignment, "Style: Steps, Symbol: None" );
    shiftDown( r, deltay );

    painter->drawText( 0, r.top(), r.width(), painter->fontMetrics().height(),
        alignment, "Style: NoCurve, Symbol: XCross" );
    //绘制一些坐标信息
}

实现矩形下移成员函数shiftDown

void MainWin::shiftDown( QRect &rect, int offset ) const
{
    rect.translate( 0, offset );
}

要点总结

  • 了解QFrame类
  • 了解QwtScaleMap的概念与基本用法
  • 理解paintEvent事件在绘图中的作用机制
  • 学会通过setRawSamples方法来为曲线设置数据点
发布了147 篇原创文章 · 获赞 41 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/YinShiJiaW/article/details/104115659
今日推荐