水波纹进度条——QT利用QPainter绘制

        水波纹进度条是一种很有趣的进度显示效果,水波纹进度条具有独特的动态效果,能够吸引用户的注意力,让界面更加生动有趣。水波纹进度条通过波纹的扩散和逐渐填充的方式来表示进度,让用户一目了然地知道任务的进度状态。

一、简述

         本示例介绍一个Qt利用QPainter实现水波纹进度条的方法。自定义一个继承自QWidget的类,并在其中重写paintEvent方法进行绘制操作。通过使用QPainter的绘制方法和一些算法,可以实现动态的水波纹效果。

二、 设计思路             
  1. 创建一个继承自QWidget的自定义控件,命名为WaterProcess。

  2. 在WaterProcess的构造函数中,初始化相关变量和属性,例如进度值、波浪高度、波浪速度等。

  3. 重写WaterProcess的绘制事件函数paintEvent(QPaintEvent *event)。

  4. 在paintEvent函数中,使用QPainter进行绘制。首先绘制外圈的背景框,然后根据当前进度值计算波浪的高度,并绘制波浪效果。

  5. 绘制波浪效果可以使用QPainter的drawPath函数,根据当前进度值计算波浪的高度和位置。可以使用QTimer来定时更新波浪的位置,实现波浪的动态效果。

  6. 在WaterProcess中提供一个公有函数setValue,用于设置进度值。在setValue函数中更新进度值,并调用update函数请求重绘。

三、效果 

四、核心代码  
1、头文件
#ifndef WATERPROCESS_H
#define WATERPROCESS_H

#include <QWidget>
#include <QtWidgets>

class WaterProcess : public QWidget
{
    Q_OBJECT
public:
    explicit WaterProcess(QWidget *parent = nullptr);

    ~WaterProcess(){}

    void setUsedColor (const QColor color);

protected:
    void paintEvent(QPaintEvent *ev);

private:
    //画背景
    void drawBg(QPainter *painter);

    //画水波
    void drawProcess(QPainter *painter);

    //画文字
    void drawValue(QPainter *painter);

public slots:
    void setMinValue(int value);
    void setMaxValue(int value);
    void setValue(int v);

    void setBorderWidth(int width);
    void setWaterHeight(int height);

    void start();
    void stop();

    void updateWave();

private:
    int     m_minValue;       //最小值
    int     m_maxValue;       //最大值
    int     m_value;          //当前值

    double  m_borderWidth;    //边框宽度
    double  m_waterHeight;    //水波高度
    double  m_offset;         //水波偏移量

    QColor  m_bgColor;        //背景颜色
    QColor  m_borderColor;    //边框颜色
    QColor  m_usedColor;      //进度颜色
    QColor  m_textColor;      //文字颜色

    QTimer  *m_timer;         //水波定时器
};

#endif // WATERPROCESS_H
2、实现代码
#include "waterprocess.h"
#include <QDebug>

WaterProcess::WaterProcess(QWidget *parent)
    : QWidget(parent)
{
    m_minValue = 0;
    m_maxValue = 100;
    m_value = 30;

    m_borderWidth = 10.0;
    m_waterHeight = 0.05;
    m_offset = 0.6;

    m_bgColor = QColor(120, 120, 120);
    m_borderColor = QColor(80, 80, 80);
    m_usedColor = QColor(50, 205, 50);
    m_textColor = QColor(255, 255, 255);

    //波浪
    m_timer = new QTimer(this);
    m_timer->setInterval(100);

    connect(m_timer, &QTimer::timeout, this, &WaterProcess::updateWave);

    m_timer->start();

}

void WaterProcess::setUsedColor(const QColor color)
{
    m_usedColor = color;
}

void WaterProcess::paintEvent(QPaintEvent *ev)
{
    Q_UNUSED(ev)

    QPainter painter(this);
    painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);

    //背景
    drawBg(&painter);

    //进度、水波
    drawProcess(&painter);

    //进度数字
    drawValue(&painter);
}

void WaterProcess::drawBg(QPainter *painter)
{
    int width = this->width();
    int height = this->height();
    int side = qMin(width, height) - m_borderWidth;

    int startX = (width - side) * 0.5;
    int startY = (height - side) * 0.5;

    painter->save();
    painter->setBrush(QBrush(m_bgColor));
    if (m_borderWidth == 0) {
        painter->setPen(Qt::NoPen);
    } else {
        QBrush brush(m_borderColor);
        painter->setPen(QPen(brush, m_borderWidth, Qt::SolidLine));
    }

    painter->drawEllipse(startX, startY, side, side);
    painter->restore();
}

void WaterProcess::drawProcess(QPainter *painter)
{
    int width = this->width();
    int height = this->height();
    int side = qMin(width, height) - (2 * m_borderWidth); //直径

    int startX = (width - side) * 0.5;
    int startY = (height - side) *0.5;
    int endX = startX + side;
    int endY = startY + side;

    double percent = (m_value * 1.0) / (m_maxValue - m_minValue);

    double w = 2 * M_PI / endX;
    double A = endY * m_waterHeight;
    double k = endY * (1.0 - percent);

    painter->save();
    painter->setPen(Qt::NoPen);
    painter->setBrush(m_usedColor);

    QPainterPath totalPath;
    //加入圆形路径
    totalPath.addEllipse(startX, startY, side, side);

    //水波路径
    QPainterPath water1;
    QPainterPath water2;

    water1.moveTo(startX, endY);
    water2.moveTo(startX, endY);

    m_offset += 0.6;
    if (m_offset > (endX / 2)) {
        m_offset = 0;
    }

    for(int i = startX; i < endX; i++) {
        //第一条波浪Y轴
        double waterY1 = (double)(A * qSin(w * i + m_offset)) + k;
        //第二条波浪Y轴
        double waterY2;
        waterY2 = (double)(A * qSin(w * i + m_offset + (endX / 2 * w))) + k;

        water1.lineTo(i, waterY1);
        water2.lineTo(i, waterY2);

        if (m_value == m_minValue) {
            waterY1 = endY;
        }

        if (m_value == m_maxValue) {
            waterY1 = startY;
        }
    }
    //封闭
    water1.lineTo(endX, endY);
    water2.lineTo(endX, endY);

    QPainterPath path;
    QColor waterColor1 = m_usedColor;
    waterColor1.setAlpha(100);
    QColor waterColor2 = m_usedColor;
    waterColor2.setAlpha(200);

    //第一条波浪
    path = totalPath.intersected(water1);
    painter->setBrush(waterColor1);
    painter->drawPath(path);

    //第二条波浪挖去后的路径
    path = totalPath.intersected(water2);
    painter->setBrush(waterColor2);
    painter->drawPath(path);


    painter->restore();
}

void WaterProcess::drawValue(QPainter *painter)
{
    painter->save();
    int width = this->width();
    int height = this->height();
    int side = qMin(width, height) - m_borderWidth;

    int startX = (width - side) * 0.5;
    int startY = (height - side) * 0.5;

    int fontSize = side / 4;

    QFont font;
    font.setFamily("华文彩云");
    font.setPixelSize(fontSize);
    font.setBold(true);

    painter->setFont(font);
    painter->setPen(Qt::white);
    painter->drawText(QRectF(startX, startY, side, side), Qt::AlignCenter, QString("%1%").arg(m_value));

    painter->restore();
}

void WaterProcess::setMinValue(int value)
{
    m_minValue = value;
    update();
}

void WaterProcess::setMaxValue(int value)
{
    m_maxValue = value;
    update();
}

void WaterProcess::setValue(int v)
{
    m_value = v;
    update();
}

void WaterProcess::setBorderWidth(int width)
{
    m_borderWidth = width;
    update();
}

void WaterProcess::setWaterHeight(int height)
{
    m_waterHeight = height;
    update();
}

void WaterProcess::start()
{
    if (m_timer) {
        m_timer->start();
    }
}

void WaterProcess::stop()
{
    if (m_timer) {
        m_timer->stop();
    }
}

void WaterProcess::updateWave()
{
    update();
}

        这样就可以实现一个简单的水波纹进度条效果。可以通过调整绘制波浪效果的参数和动画效果,来实现不同的水波纹进度条样式。根据需要可以添加其他功能,例如添加气泡动画效果等。

五、使用示例

以下是一个简单的示例代码,演示了如何在Qt中使用此控件:

ui设计

界面交互代码 

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    ui->waterProcess2->setUsedColor(QColor(79, 175, 243));
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_borderWidthSlider_valueChanged(int value)
{
    ui->waterProcess1->setBorderWidth(value);
    ui->waterProcess2->setBorderWidth(value);
}

void Widget::on_processSlider_valueChanged(int value)
{
    ui->waterProcess1->setValue(value);
    ui->waterProcess2->setValue(value);
}

        总结一下,水波纹进度条是一种常见的进度条效果,它通过模拟水波纹的效果来表示进度。在Qt中,可以使用QPainter类来实现水波纹进度条。通过Qt的QPainter类,可以方便地实现水波纹进度条效果。关键是要理解绘制水波纹的原理,然后使用QPainter的相关函数来进行绘制。同时,使用Qt的定时器可以实现进度条的动态显示。通过良好的封装和设计,可以使得水波纹进度条的实现更加灵活和易于扩展。

        谢谢您的阅读,希望本文能为您带来一些帮助和启发。如果您有任何问题或意见,请随时与我联系。祝您度过美好的一天!

六、源代码下载
Qt中,QPainter是一个用于绘图的强大工具,特别是在处理图形用户界面(GUI)时。如果你想在Qt应用中绘制弯曲的管路,你可以利用QPainter的一些特性,如贝塞尔曲线(Bezier Curves)。贝塞尔曲线是一种通过控制点精确控制线条形状的数学函数,非常适合创建平滑的曲线。 以下是基本步骤: 1. **准备画布**:首先,你需要创建一个QPaintDevice或QWidget作为你的绘画容器。 ```cpp QGraphicsView view; QGraphicsScene scene(&view); view.setScene(&scene); QGraphicsPixmapItem pixmap(itemToDrawOn); // itemToDrawOn是你想要绘制管道的背景或其他内容 scene.addItem(pixmap); ``` 2. **获取QPainter**:然后从场景或窗口上获取QPainter实例。 ```cpp QPainter painter(&pixmap); ``` 3. **设置颜色和样式**:选择你要使用的笔刷颜色和宽度。 ```cpp painter.setPen(QPen(Qt::blue, 2)); // 设置蓝色线条,线宽为2像素 ``` 4. **绘制贝塞尔曲线**:使用`drawPath()`方法,传递一个包含贝塞尔曲线路径的QPainterPath对象。例如,可以先计算出一系列的控制点,然后构造路径。 ```cpp QPointF start = ...; // 弯曲起点 QPointF controlPoint1 = ...; // 第一个控制点 QPointF controlPoint2 = ...; // 第二个控制点 QPointF end = ...; // 结束点 // 构造贝塞尔曲线路径 QPainterPath path; path.moveTo(start); path.cubicTo(controlPoint1, controlPoint2, end); // 绘制路径 painter.drawPath(path); ``` 5. **循环绘制**:如果你需要绘制一段连续的弯曲管路,你可以将上述过程放在一个循环里,改变控制点的位置来创建曲线段。 6. **保存图像**:完成绘制后,如果需要,可以用`save()`方法保存图片到文件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值