Qwt源码解读之标尺相关类

  Qwt中添加标尺是非常方便和漂亮的。除了有普通的直尺外,还有环形标尺。这里我们先学习一下直尺的相关类。直尺的相关类共有下面几个:

1、QwtScaleMap:标尺值映射类。

      QwtScaleMap类用于提供标尺坐标系与绘制设备坐标系之间的映射关系。其主要的功能依托 QwtScaleTransformation 类来实现。

2、QwtScaleDiv:标尺刻度划分类。

      QwtScaleDiv类表征刻度划分。一个标尺的刻度划分包括它的起始值和3个刻度列表(分别为主刻度列表,次刻度列表和最小刻度列表)。大部分情况下,刻度划分是由QwtScaleEngine(应该说是由它的派生类)自动计算的。

3、QwtScaleDraw:绘制标尺类。

      QwtScaleDraw类继承自抽象基类QwtAbstractScaleDraw,用于绘制普通标尺。一个标尺也由多个部分组成(如下列表),其由变量QwtAbstractScaleDraw::ScaleComponent 标识。

1)Backbone 标尺沿线。
2)Ticks 刻度线。
3)Labels 值标签。

4、QwtScaleWidget:标尺部件。

      QwtScaleWidget类是呈现或者说被用于绘制标尺的部件,继承自QWidget。 在实际的开发中,这个类用得比较多。

代码分析:

1、QwtScaleTransformation 类

除了构造函数和习惯函数外,QwtScaleTransformation 类提供了以下接口:标尺坐标值与绘制设备坐标值之间的转化。
    virtual double xForm( double s, double s1, double s2,
        double p1, double p2 ) const;
    virtual double invXForm( double p, double p1, double p2,
        double s1, double s2 ) const;

由于禁用了拷贝构造函数和拷贝赋值操作符函数,因此还提供了克隆函数:

//! Create a clone of the transformation
QwtScaleTransformation *QwtScaleTransformation::copy() const
{
    return new QwtScaleTransformation( d_type );
}
2、QwtScaleMap类
这里看一下QwtScaleMap的拷贝构造函数和拷贝赋值操作符函数,帮助理解上面 QwtScaleTransformation类的copy()函数。

//! Copy constructor
QwtScaleMap::QwtScaleMap( const QwtScaleMap& other ):
    d_s1( other.d_s1 ),
    d_s2( other.d_s2 ),
    d_p1( other.d_p1 ),
    d_p2( other.d_p2 ),
    d_cnv( other.d_cnv )
{
    d_transformation = other.d_transformation->copy();
}
//! Assignment operator
QwtScaleMap &QwtScaleMap::operator=( const QwtScaleMap & other )
{
    d_s1 = other.d_s1;
    d_s2 = other.d_s2;
    d_p1 = other.d_p1;
    d_p2 = other.d_p2;
    d_cnv = other.d_cnv;

    delete d_transformation;
    d_transformation = other.d_transformation->copy();

    return *this;
}

//! Copy constructor
QwtScaleMap::QwtScaleMap( const QwtScaleMap& other ):
    d_s1( other.d_s1 ),
    d_s2( other.d_s2 ),
    d_p1( other.d_p1 ),
    d_p2( other.d_p2 ),
    d_cnv( other.d_cnv )
{
    d_transformation = other.d_transformation->copy();
}
//! Assignment operator
QwtScaleMap &QwtScaleMap::operator=( const QwtScaleMap & other )
{
    d_s1 = other.d_s1;
    d_s2 = other.d_s2;
    d_p1 = other.d_p1;
    d_p2 = other.d_p2;
    d_cnv = other.d_cnv;

    delete d_transformation;
    d_transformation = other.d_transformation->copy();

    return *this;
}
然后,QwtScaleMap的参数设置接口和完成坐标系统之间值转化的功能函数:
    void setPaintInterval( double p1, double p2 );
    void setScaleInterval( double s1, double s2 );

    double transform( double s ) const;
    double invTransform( double p ) const;
3、QwtScaleDiv类
      QwtScaleDiv类共有3个构造函数,不带参数的默认构造函数创建一个“无效的”刻度划分实例。我们看具有两个参数的构造函数,就能明白了QwtScaleDiv的基本属性特征。

/*!
  Construct QwtScaleDiv instance.

  \param interval Interval
  \param ticks List of major, medium and minor ticks
*/
QwtScaleDiv::QwtScaleDiv( const QwtInterval &interval,
        QList<double> ticks[NTickTypes] ):
    d_lowerBound( interval.minValue() ),
    d_upperBound( interval.maxValue() ),
    d_isValid( true )
{
    for ( int i = 0; i < NTickTypes; i++ )
        d_ticks[i] = ticks[i];
}
再看一下其实现值得借鉴的函数接口:

/*!
   Return a list of ticks

   \param type MinorTick, MediumTick or MajorTick
*/
const QList<double> &QwtScaleDiv::ticks( int type ) const
{
    if ( type >= 0 || type < NTickTypes ) // 防错性判断,OK!
        return d_ticks[type];

    static QList<double> noTicks; 
    return noTicks;
}
static QList<double> noTicks;  // 定义一个局部的静态变量,只初始化一次即可。如果在一个循环中调用ticks(),恰好输入的参数有误,则能大幅度提高效率。

4、QwtAbstractScaleDraw类
数据:

class QwtAbstractScaleDraw::PrivateData
{
public:
    PrivateData():
        spacing( 4.0 ),
        penWidth( 0 ),
        minExtent( 0.0 )
    {
        components = QwtAbstractScaleDraw::Backbone 
            | QwtAbstractScaleDraw::Ticks 
            | QwtAbstractScaleDraw::Labels;

        tickLength[QwtScaleDiv::MinorTick] = 4.0;
        tickLength[QwtScaleDiv::MediumTick] = 6.0;
        tickLength[QwtScaleDiv::MajorTick] = 8.0;
    }

    ScaleComponents components; // 标尺包含哪些部分

    QwtScaleMap map;
    QwtScaleDiv scldiv;

    double spacing;
    double tickLength[QwtScaleDiv::NTickTypes]; // 刻度线的长度
    int penWidth;

    double minExtent;

    QMap<double, QwtText> labelCache;// 标签缓存
};
实现:
QwtAbstractScaleDraw类使用了模板方法模式【Template Method】,draw()方法定义了绘制标尺的基本框架:

/*!
  \brief Draw the scale

  \param painter    The painter

  \param palette    Palette, text color is used for the labels,
                    foreground color for ticks and backbone
*/
void QwtAbstractScaleDraw::draw( QPainter *painter,
    const QPalette& palette ) const
{
    painter->save();

    QPen pen = painter->pen();
    pen.setWidth( d_data->penWidth );
    pen.setCosmetic( false );
    painter->setPen( pen );

    if ( hasComponent( QwtAbstractScaleDraw::Labels ) )
    {
        painter->save();
        painter->setPen( palette.color( QPalette::Text ) ); // ignore pen style

        const QList<double> &majorTicks =
            d_data->scldiv.ticks( QwtScaleDiv::MajorTick );

        for ( int i = 0; i < majorTicks.count(); i++ )
        {
            const double v = majorTicks[i];
            if ( d_data->scldiv.contains( v ) )
                drawLabel( painter, majorTicks[i] ); // 绘制标签,纯虚函数,具体的实现延迟到子类中
        }

        painter->restore();
    }

    if ( hasComponent( QwtAbstractScaleDraw::Ticks ) )
    {
        painter->save();

        QPen pen = painter->pen();
        pen.setColor( palette.color( QPalette::WindowText ) );
        pen.setCapStyle( Qt::FlatCap );

        painter->setPen( pen );

        for ( int tickType = QwtScaleDiv::MinorTick;
            tickType < QwtScaleDiv::NTickTypes; tickType++ )
        {
            const QList<double> &ticks = d_data->scldiv.ticks( tickType );
            for ( int i = 0; i < ticks.count(); i++ )
            {
                const double v = ticks[i];
                if ( d_data->scldiv.contains( v ) )
                    drawTick( painter, v, d_data->tickLength[tickType] ); // 绘制刻度,纯虚函数,具体的实现延迟至子类中
            }
        }

        painter->restore();
    }

    if ( hasComponent( QwtAbstractScaleDraw::Backbone ) )
    {
        painter->save();

        QPen pen = painter->pen();
        pen.setColor( palette.color( QPalette::WindowText ) );
        pen.setCapStyle( Qt::FlatCap );

        painter->setPen( pen );

        drawBackbone( painter );  // 绘制标尺底线,纯虚函数,具体的实现延迟至子类中

        painter->restore();
    }

    painter->restore();
}

参数设置接口:
    void setScaleDiv( const QwtScaleDiv &s );
    const QwtScaleDiv& scaleDiv() const;

    void setTransformation( QwtScaleTransformation * );
    const QwtScaleMap &scaleMap() const;
    QwtScaleMap &scaleMap();
当然,也可以设置笔的宽度,刻度线的长度等属性显示不同风格的刻度。
5、QwtScaleDraw类
主要实现了QwtAbstractScaleDraw的四个纯虚函数,以及标尺的位置(原点)移动;标签旋转;刻度对齐;等功能。
6、QwtScaleWidget类
一个较好的接口命名示例代码:
    void getMinBorderDist( int &start, int &end ) const; // 通过引用返回两个值
    void setMinBorderDist( int start, int end );
设置坐标转化和刻度划分:
    void setScaleDiv( QwtScaleTransformation *, const QwtScaleDiv &sd );

    void setScaleDraw( QwtScaleDraw * );
    const QwtScaleDraw *scaleDraw() const;
    QwtScaleDraw *scaleDraw();
色标的使用:
    void setColorMap( const QwtInterval &, QwtColorMap * );

    QwtInterval colorBarInterval() const;
    const QwtColorMap *colorMap() const;
7、最后再看标尺用到的一个“静态工具类”QwtScaleArithmetic:

/*!
  \brief Arithmetic including a tolerance
*/
class QWT_EXPORT QwtScaleArithmetic
{
public:
    static double ceilEps( double value, double intervalSize );
    static double floorEps( double value, double intervalSize );

    static double divideEps( double interval, double steps );

    static double ceil125( double x );
    static double floor125( double x );
};
这样分类定义的静态函数明显优于全局函数。


  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值