波浪进度条
如下:
绘制步骤如下:
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);
作者:姆路