如下:

绘制步骤如下:

1、先绘制波浪状的QPainterPath  ,即sin曲线

2、通过定时器不停的更新sin曲线的初始相位来呈现波动的效果

3、然后绘制中间的圆

4、然后绘制中间的文本

5、然后再在第一步之前给QPainter设置可见区域,把这个圆的范围设置为可见区域,这样多余的蓝色就不显示了

.h文件

#ifndef WAVEPROGRESSBAR_H
#define WAVEPROGRESSBAR_H

#include <QWidget>
#include<QPainter>
#include<QTimer>

class WaveProgressBar : public QWidget
{
    Q_OBJECT
    Q_PROPERTY(int max READ max WRITE setMax NOTIFY maxChanged FINAL)
    Q_PROPERTY(int min READ min WRITE setMin NOTIFY minChanged FINAL)
    Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged FINAL)
public:
    explicit WaveProgressBar(QWidget *parent = nullptr);

    int max() const;
    void setMax(int newMax);

    int min() const;
    void setMin(int newMin);

    int value() const;
    void setValue(int newValue);

protected:
    void paintEvent(QPaintEvent* ev) override;


signals:

    void maxChanged();

    void minChanged();

    void valueChanged();

private:
    QTimer* timer;
    int offset=0;//sin曲线的初始相位


    int mMax=100;
    int mMin=0;
    int mValue=50;

};

#endif // WAVEPROGRESSBAR_H

.cpp文件

#include "waveprogressbar.h"
#include<QPainterPath>
#include<QtMath>

WaveProgressBar::WaveProgressBar(QWidget *parent)
    : QWidget{parent}
{
    timer=new QTimer(this);
    connect(timer,&QTimer::timeout,this,[=](){

        //通过不停的修改初始相位,来实现sin曲线动起来的效果
        offset+=5;
        if(offset>std::min(width(),height())/2)
        {
            offset=-std::min(width(),height())/2;
        }
        update();
    });

    timer->start(100);

}

void WaveProgressBar::paintEvent(QPaintEvent *ev)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    //painter移到中心
    painter.translate(this->rect().center());

    //确定圆的半径
    int r=std::min(width(),height())/2;
    //确定圆的矩形
    QRect rect(QPoint(-r,-r),QPoint(r,r));

    //画圆形的笔的宽度
    int penWidth=5;


    //1、设置中间的可见区域,就是中间的那个圆,
    QPainterPath clipPath;
    clipPath.addEllipse(rect.adjusted(penWidth,penWidth,-penWidth,-penWidth));
    painter.setClipPath(clipPath);


    //2、先绘制波浪,就是sin曲线,正弦曲线可表示为y=Asin(ωx+φ)+k,
    painter.save();
    //使用QPainterPath来绘制平滑的曲线
    /*
     *这几个参数值都是慢慢调出来的
     *A:r/15.0
     *ω:2*M_PI/(rect.width()/2)
     *φ:offset
     *k:-r+r/15.0+2*r*(1-(mValue*1.0/(mMax-mMin)))
    */
    QPainterPath path;
    for(int i=rect.topLeft().x();i<rect.width()/2;++i)
    {
        if(i==rect.topLeft().x())
        {
            //起点
            //sin的取值是-1到1,需要*系数(r/10) 进行扩大   2*M_PI/(rect.width()/2)控制震荡频率,即绘制2个周期   k决定波浪的高度
            path.moveTo(i,(r/15.0)*qSin(2*M_PI/(rect.width()/2)*i+offset)-r+r/15.0+2*r*(1-(mValue*1.0/(mMax-mMin))));
        }
        else
        {
            path.lineTo(i,(r/15.0)*qSin(2*M_PI/(rect.width()/2)*i+offset)-r+r/15.0+2*r*(1-(mValue*1.0/(mMax-mMin))));
        }
    }
    path.lineTo(rect.bottomRight());
    path.lineTo(rect.bottomLeft());
    path.closeSubpath();//这个方法会自动将QPainterPath的第一个点和最后一个点连起来

    painter.setPen(Qt::NoPen);
    painter.setBrush(Qt::cyan);
    painter.drawPath(path);
    painter.restore();

    //3、再绘制中间的圆
    painter.save();

    painter.setPen(QPen(Qt::red,penWidth));
    painter.setBrush(Qt::NoBrush);
    painter.drawEllipse(rect.adjusted(penWidth,penWidth,-penWidth,-penWidth));
    painter.restore();

    //4、再绘制中间的文本
    painter.save();
    QFont font;
    font.setPixelSize(r/5);
    painter.setFont(font);
    painter.drawText(rect,Qt::AlignCenter,QString::number(mValue)+"%");
    painter.restore();
}

int WaveProgressBar::value() const
{
    return mValue;
}

void WaveProgressBar::setValue(int newValue)
{
    if (mValue == newValue)
        return;
    if(newValue>mMax)
    {
        newValue=mMax;
    }
    if(newValue<mMin)
    {
        newValue=mMin;
    }

    mValue = newValue;
    emit valueChanged();
    update();
}

int WaveProgressBar::min() const
{
    return mMin;
}

void WaveProgressBar::setMin(int newMin)
{
    if (mMin == newMin)
        return;
    mMin = newMin;
    emit minChanged();
    update();
}

int WaveProgressBar::max() const
{
    return mMax;
}

void WaveProgressBar::setMax(int newMax)
{
    if (mMax == newMax)
        return;
    mMax = newMax;
    emit maxChanged();
    update();
}

知识点:

给QPainter设置可见区域

调用QPainter的setClipPath()方法

需要指定可见区域的路径,这里可见区域是一个圆

QPainterPath clipPath;
clipPath.addEllipse(rect.adjusted(penWidth,penWidth,-penWidth,-penWidth));
painter.setClipPath(clipPath);

 

作者:姆路

物联沃分享整理
物联沃-IOTWORD物联网 » 波浪进度条

发表回复