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();
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM