Qt QProgressBar美化,水波纹进度条
首先上实例
要模拟波浪,就要首先画出一条波浪线,正弦余弦曲线就很适合。
y=A*sin(ω*x+φ)+k
y=A*cos(ω*x+φ)+k
这是正弦余弦曲线的公式,要想实现水波效果,那需要两条曲线,一条曲线的波峰对着另外一条曲线的波谷,要实现这样的曲线效果,只有让正弦曲线前移π/2个单位。所以我们最后对两个水波使用的公式就变成了下面两个
y=A*sin(ω*x-π/2+φ)+k
y=A*cos(ω*x+φ)+k
在我们的代码中A就是水波的高度,ω是水波的周期,φ是水波的偏移量,用于实现动画效果,k是水波的高度,注意由于我们的电脑坐标系是以左上角为原点,这和我们高中坐标系理解的不同,所以这里水波的高度是要进行一段处理的。
下面直接上代码
#include <QProgressBar> #include <QTimer> #include <QPainter> #include <QColor> #include <QtMath> class WaterProgressBar : public QProgressBar { Q_OBJECT public: WaterProgressBar(QWidget *parent); ~WaterProgressBar(); protected: //页面重绘事件 void paintEvent(QPaintEvent *event); private: void drawBackGround(QPainter* painter); void drawWaterWave(QPainter* painter); void drawText(QPainter* painter); private: int m_iBorderWidth;//边框厚度 int m_iValue;//当前进度条进度 double m_dOffset;//水波偏移量 QColor m_waterColor;//水波颜色 QColor m_backgroundColor;//背景颜色 QColor m_borderColor;//边框颜色 QColor m_textColor;//文本颜色 QTimer *m_timer;//控制水波移动的定时器 };
#include "stdafx.h" #include "WaterProgressBar.h" WaterProgressBar::WaterProgressBar(QWidget *parent) { m_iBorderWidth = 0; m_waterColor.setRgb(43, 123, 234); m_backgroundColor.setRgb(255, 255, 255); m_borderColor.setRgb(120, 120, 120); m_textColor.setRgb(0, 0, 0); m_dOffset = 0; //利用定时器固定时间内刷新页面,使得水浪动起来 m_timer = new QTimer(this); m_timer->setSingleShot(false); connect(m_timer, &QTimer::timeout, this, [=](){ if (this->isVisible()) { //偏移量控制,每次绘制自加0.3,当超过一个正弦余弦2π周期时,就回退为0,加上定时器每50ms绘制一下,就触发了曲线的动态效果 m_dOffset += 0.3; if (m_dOffset > 2 * M_PI) { m_dOffset = 0; } this->update(); } }); m_timer->start(50); } WaterProgressBar::~WaterProgressBar() { } void WaterProgressBar::paintEvent(QPaintEvent *event) { //进度条不可见的情况下就不重绘了 if (!this->isVisible()) { return; } m_iValue = this->value() < 0 ? 0 : this->value();//初始化时QT进度条的进度为-1,避免负进度的出现 QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);//开启图和字体抗锯齿 drawBackGround(&painter);//绘制背景 drawWaterWave(&painter);//绘制水波 drawText(&painter);//绘制进度文本 } void WaterProgressBar::drawBackGround(QPainter* painter) { int width = this->width(); int height = this->height(); if (m_iBorderWidth > 0) { //根据窗口的长宽最小值得到外部背景直径 int max_diameter = qMin(width, height); painter->save(); painter->setBrush(QBrush(m_borderColor)); painter->setPen(Qt::NoPen); //绘制边框背景,然后用内部背景覆盖,即可得到边框 painter->drawEllipse(0, 0, max_diameter, max_diameter); painter->restore(); } painter->save(); //根据窗口的长宽最小值减去边框厚度得到内部背景直径 int min_diameter = qMin(width, height) - (2 * m_iBorderWidth); painter->setBrush(QBrush(m_backgroundColor)); painter->setPen(Qt::NoPen); painter->drawEllipse(m_iBorderWidth, m_iBorderWidth, min_diameter, min_diameter); painter->restore(); } void WaterProgressBar::drawWaterWave(QPainter* painter) { int width = this->width(); int height = this->height(); //根据窗口的长宽最小值减去边框厚度得到直径 int diameter = qMin(width, height) - (2 * m_iBorderWidth); //sincos曲线的波峰,波峰越大,水浪越高 double waveHeight = 0.04*diameter; //sincos曲线的周期,周期越大,水浪越密 double cycle = 2 * M_PI / diameter; //水的高度,可以理解为进度,注意由于我们的电脑坐标系是以左上角为原点,这和我们高中坐标系理解的不同 double percent = (double)m_iValue / 100; double waterHeight = (1 - percent)*diameter + m_iBorderWidth; painter->save(); QPainterPath totalPath; //加入圆形路径 totalPath.addEllipse(m_iBorderWidth, m_iBorderWidth, diameter, diameter); //水波路径,先画浅色,再画深色 QPainterPath water1; QPainterPath water2; water1.moveTo(m_iBorderWidth, m_iBorderWidth + diameter); water2.moveTo(m_iBorderWidth, m_iBorderWidth + diameter); //从左边起始点到右边结束点绘制两条波浪曲线 for (int i = m_iBorderWidth; i <= m_iBorderWidth + diameter; i++) { double waterY1 = 0; double waterY2 = 0; if (m_iValue == 0 || m_iValue == 100) { waterY1 = waterY2 = waterHeight; } else { //第一条波浪Y轴 waterY1 = (double)(waveHeight * qSin(cycle * (i - m_iBorderWidth) - M_PI / 2 + m_dOffset)) + waterHeight;//当正弦曲线前进π/2,sin的波峰和cos的波谷就对上了 //第二条波浪Y轴 waterY2 = (double)(waveHeight * qCos(cycle * (i - m_iBorderWidth) + m_dOffset)) + waterHeight; } water1.lineTo(i, waterY1); water2.lineTo(i, waterY2); } //封闭 water1.lineTo(m_iBorderWidth + diameter, m_iBorderWidth + diameter); water2.lineTo(m_iBorderWidth + diameter, m_iBorderWidth + diameter); QPainterPath path; QColor waterColor1 = m_waterColor; waterColor1.setAlpha(100); QColor waterColor2 = m_waterColor; waterColor2.setAlpha(200); //第一条波浪 path = totalPath.intersected(water1); painter->setBrush(waterColor1); painter->setPen(Qt::NoPen); painter->drawPath(path); painter->restore(); painter->save(); //第二条波浪挖去后的路径 path = totalPath.intersected(water2); painter->setBrush(waterColor2); painter->setPen(Qt::NoPen); painter->drawPath(path); painter->restore(); } void WaterProgressBar::drawText(QPainter* painter) { painter->save(); int width = this->width(); int height = this->height(); //根据窗口的长宽最小值减去边框厚度得到直径 int diameter = qMin(width, height) - (2 * m_iBorderWidth); int fontSize = diameter / 5; //设置文本字体 QFont font; font.setFamily("微软雅黑"); font.setPixelSize(fontSize); font.setBold(true); //绘制文本 painter->setFont(font); painter->setPen(m_textColor); painter->drawText(QRectF(m_iBorderWidth, m_iBorderWidth, diameter, diameter), Qt::AlignCenter, QString("%1%").arg(m_iValue)); painter->restore(); }