qt第三方圆形进度条-及其改进

朝闻道 朝闻道     2022-08-20     525

关键词:

Qt第三方圆形进度条的改进

要实现一个圆形的进度条功能,在网上找到一个比较出名的第三方封装类:QRoundProgressBar,地址:sourceforge 的 QRoundProgressBar 
功能封装的还是不错,提供了3种模式,线形、圆环、饼状。使用过程中发现圆环进度条对背景透明支持不够完善,内圆的背景无法实现透明,为了解决此问题,下面对此控件进行了一些修订,实现完整的圆形进度条。

QRoundProgressBar目前存在的不足

技术分享

QRoundProgressBar在带背景图片widget下使用StyleDonut样式时,内环背景无法透明

技术分享

代码如下: 
头文件:

class DRoundProgressBar;
class QTimer;
namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();
public slots:
    void onTimeOut();
private:
    Ui::Widget *ui;
    QTimer* mTimer;
    DRoundProgressBar* mRoundBar;
    int mPresent;
};

cpp:

#include "Widget.h"
#include "ui_Widget.h"
#include "DRoundProgressBar.h"
#include <QTimer>
Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    setAutoFillBackground(true);
    QPixmap img(":/img/BlueDialog_BK.png");
    QPalette bkPalette;
    bkPalette.setBrush(QPalette::Window,QBrush(img));
    setPalette(bkPalette);
    mRoundBar = new DRoundProgressBar(this);
    mRoundBar->setGeometry(150,100,500,500);
    mRoundBar->setBarStyle(DRoundProgressBar::StyleDonut);
    mRoundBar->setRange(0,100);
    QPalette palette;
    palette.setBrush(QPalette::Window,Qt::NoBrush);
    palette.setBrush(QPalette::AlternateBase,Qt::NoBrush);
    palette.setBrush(QPalette::Highlight,QBrush(QColor(0,140,255)));
    palette.setColor(QPalette::Text,QColor(0,0,0));
    //palette.setBrush(QPalette::Base,Qt::white);
    mRoundBar->setPalette(palette);
    mTimer = new QTimer(this);
    mTimer->setInterval(200);
    connect(mTimer,SIGNAL(timeout()),this,SLOT(onTimeOut()));
    mPresent = 0;
    mTimer->start();
}

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

void Widget::onTimeOut()
{
    ++mPresent;
    if(mPresent >= 100)
    {
        mPresent = 0;
    }
    mRoundBar->setValue(mPresent);
}

这里把QPalette::Window和QPalette::AlternateBase设置为透明,发现无法绘制圆环

原因

查看代码,看看其绘制圆环的步骤

void QRoundProgressBar::drawValue(QPainter &p, const QRectF &baseRect, double value, double arcLength)
{
    // nothing to draw
    if (value == m_min)
        return;

    // for Line style
    if (m_barStyle == StyleLine)
    {
        p.setPen(QPen(palette().highlight().color(), m_dataPenWidth));
        p.setBrush(Qt::NoBrush);
        p.drawArc(baseRect.adjusted(m_outlinePenWidth/2, m_outlinePenWidth/2, -m_outlinePenWidth/2, -m_outlinePenWidth/2),
                  m_nullPosition * 16,
                  -arcLength * 16);
        return;
    }

    // for Pie and Donut styles

    QPainterPath dataPath;
    dataPath.setFillRule(Qt::WindingFill);

    // pie segment outer
    dataPath.moveTo(baseRect.center());
    dataPath.arcTo(baseRect, m_nullPosition, -arcLength);
    dataPath.lineTo(baseRect.center());

    p.setBrush(palette().highlight());
    p.setPen(QPen(palette().shadow().color(), m_dataPenWidth));
    p.drawPath(dataPath);
}

发现绘制圆环和绘制pie是一个代码,而实现圆环只不过是用一个背景覆盖上去了,这样,如果让中间区域透明就会显示画的那个扇形的那一部分(原来作者是得多懒-_-#)。 
因此。在绘制圆环时需要特殊对待,QPainterPath在画圆环时把圆环填充。

改进

这里需要把原来的drawValue函数进行修正,原来drawValue函数对绘制pie和绘制Donut styles是一样处理,这里修正为pie就画pie,画Donut styles就画圆环: 
为了绘制圆环,drawValue函数需添加两个变量,是内环的对应的矩形和半径

virtual void drawValue(QPainter& p, const QRectF& baseRect, double value, double arcLength, const QRectF & innerRect, double innerRadius);

改动后的cpp

void QRoundProgressBar::drawValue(QPainter &p
                                  , const QRectF &baseRect
                                  , double value
                                  , double arcLength
                                  , const QRectF & innerRect
                                  , double innerRadius)
{
    // nothing to draw
    if (value == m_min)
        return;

    // for Line style
    if (m_barStyle == StyleLine)
    {
        p.setPen(QPen(palette().highlight().color(), m_dataPenWidth));
        p.setBrush(Qt::NoBrush);
        p.drawArc(baseRect.adjusted(m_outlinePenWidth/2, m_outlinePenWidth/2, -m_outlinePenWidth/2, -m_outlinePenWidth/2),
                  m_nullPosition * 16,
                  -arcLength * 16);
        return;
    }

    // for Pie and Donut styles
    QPainterPath dataPath;
    dataPath.setFillRule(Qt::WindingFill);
    dataPath.moveTo(baseRect.center());
    dataPath.arcTo(baseRect, m_nullPosition, -arcLength);//大家都是先绘制外圆的弧长
    if(m_barStyle == StylePie)
    {

        // pie segment outer
        dataPath.lineTo(baseRect.center());

        p.setPen(QPen(palette().shadow().color(), m_dataPenWidth));
    }
    if(m_barStyle == StyleDonut)
    {
        // draw dount outer
        QPointF currentPoint = dataPath.currentPosition();//绘制完外圆弧长后,获取绘制完的位置绘制一个直线到达内圆
        currentPoint = baseRect.center() + ((currentPoint - baseRect.center()) * m_innerOuterRate;//计算内圆的坐标点,m_innerOuterRate替代了原作者写的0.75,代表内圆是外圆的0.75倍
        dataPath.lineTo(currentPoint);//绘制外圆到内圆的直线
        dataPath.moveTo(baseRect.center());//坐标点回到中心准备绘制内圆弧形
        dataPath.arcTo(innerRect, m_nullPosition-arcLength, arcLength);//绘制内圆的弧形
        currentPoint = dataPath.currentPosition();//准备绘制内圆到外圆的直线,形成封闭区域
        currentPoint = baseRect.center() + ((currentPoint - baseRect.center()) * (2-m_innerOuterRate));//绘制内圆到外圆的直线,这里2-m_innerOuterRate其实是对应(1 + (1 -m_innerOuterRate))的
        dataPath.lineTo(currentPoint);
        p.setPen(Qt::NoPen);//这个很重要不然就会有绘制过程的一些轮廓了
    }
    p.setBrush(palette().highlight());
    p.drawPath(dataPath);
}

具体过程见代码的注释。

这里作者把内圆直径定死为外圆的0.75倍,我觉得这样失去了灵活性,因此加入了一个float变量,m_innerOuterRate,默认为0.75,替代原来的0.75常数,并加入方法float innerOuterRate() constvoid setInnerOuterRate(float r)进行设置

原来的paintEvent函数的函数顺序也需要改变:

下面是原来的paintEvent函数:

void QRoundProgressBar::paintEvent(QPaintEvent* /*event*/)
{
    double outerRadius = qMin(width(), height());
    QRectF baseRect(1, 1, outerRadius-2, outerRadius-2);

    QImage buffer(outerRadius, outerRadius, QImage::Format_ARGB32_Premultiplied);

    QPainter p(&buffer);
    p.setRenderHint(QPainter::Antialiasing);

    // data brush
    rebuildDataBrushIfNeeded();

    // background
    drawBackground(p, buffer.rect());

    // base circle
    drawBase(p, baseRect);

    // data circle
    double arcStep = 360.0 / (m_max - m_min) * m_value;
    drawValue(p, baseRect, m_value, arcStep);

    // center circle
    double innerRadius(0);
    QRectF innerRect;
    calculateInnerRect(baseRect, outerRadius, innerRect, innerRadius);
    drawInnerBackground(p, innerRect);

    // text
    drawText(p, innerRect, innerRadius, m_value);

    // finally draw the bar
    p.end();

    QPainter painter(this);
    painter.fillRect(baseRect, palette().background());
    painter.drawImage(0,0, buffer);
}

原来作者使用了QImage作为缓存,但qt自带双缓冲,这一步没有必要(用QImage时在ubuntu还有小问题,在控件比较小时(200*200以下)会出现花屏现象,原因未知),修改为:


void QRoundProgressBar::paintEvent(QPaintEvent* /*event*/)
{
    double outerRadius = qMin(width(), height());
    QRectF baseRect(1, 1, outerRadius-2, outerRadius-2);

    QPainter p(this);
    p.setRenderHint(QPainter::Antialiasing);

    // data brush
    rebuildDataBrushIfNeeded();

    // background
    drawBackground(p, rect());
    double innerRadius(0);
    QRectF innerRect;
    calculateInnerRect(baseRect, outerRadius, innerRect, innerRadius);
    double arcStep = 360.0 / (m_max - m_min) * m_value;
    // base circle
    drawBase(p, baseRect,innerRect);

    // data circle

    drawValue(p, baseRect, m_value, arcStep,innerRect, innerRadius);

    // center circle

    drawInnerBackground(p, innerRect);

    // text
    drawText(p, innerRect, innerRadius, m_value);

    // finally draw the bar
    p.end();
}

主要是把calculateInnerRect(baseRect, outerRadius, innerRect, innerRadius);函数提前计算出内圆对应的参数。并传入给新修改的drawValue函数。把多余的双缓冲机制去掉.

此时效果还未达到需求的效果,发现drawBase函数还需要修改,原来的drawBase函数如下:

void QRoundProgressBar::drawBase(QPainter &p, const QRectF &baseRect)
{
    switch (m_barStyle)
    {
    case StyleDonut:
        p.setPen(QPen(palette().shadow().color(), m_outlinePenWidth));
        p.setBrush(palette().base());
        p.drawEllipse(baseRect);
        break;

    case StylePie:
        p.setPen(QPen(palette().base().color(), m_outlinePenWidth));
        p.setBrush(palette().base());
        p.drawEllipse(baseRect);
        break;

    case StyleLine:
        p.setPen(QPen(palette().base().color(), m_outlinePenWidth));
        p.setBrush(Qt::NoBrush);
        p.drawEllipse(baseRect.adjusted(m_outlinePenWidth/2, m_outlinePenWidth/2, -m_outlinePenWidth/2, -m_outlinePenWidth/2));
        break;

    default:;
    }
}

上面的drawBase函数可见,由于原作者比较懒,对于Donut styles模式就直接画了一个外圆,并不是一个空心圆环,改进如下:

void QRoundProgressBar::drawBase(QPainter &p, const QRectF &baseRect,const QRectF &innerRect)
{
    switch (m_barStyle)
    {
    case StyleDonut:
    {
        QPainterPath dataPath;
        dataPath.setFillRule(Qt::OddEvenFill);
        dataPath.moveTo(baseRect.center());
        dataPath.addEllipse(baseRect);
        dataPath.addEllipse(innerRect);
        p.setPen(QPen(palette().shadow().color(), m_outlinePenWidth));
        p.setBrush(palette().base());
        p.drawPath(dataPath);
        break;
    }
    case StylePie:
        p.setPen(QPen(palette().base().color(), m_outlinePenWidth));
        p.setBrush(palette().base());
        p.drawEllipse(baseRect);
        break;

    case StyleLine:
        p.setPen(QPen(palette().base().color(), m_outlinePenWidth));
        p.setBrush(Qt::NoBrush);
        p.drawEllipse(baseRect.adjusted(m_outlinePenWidth/2, m_outlinePenWidth/2, -m_outlinePenWidth/2, -m_outlinePenWidth/2));
        break;
    default:;
    }
}

最后运行效果:

技术分享

代码见:czyt1988的github

 

http://blog.csdn.net/czyt1988/article/details/53422274

如何在kivy中制作圆形进度条?

】如何在kivy中制作圆形进度条?【英文标题】:Howtomakecircularprogressbarinkivy?【发布时间】:2018-11-0516:04:11【问题描述】:我想用kivy和python制作一个简单的圆形进度条。我搜索了在线文档和GitHub曲目,但没有找到解释循环进度条... 查看详情

异步下载圆形进度条显示进度

圆形进度条参考链接即可:使用css3实现圆形进度条需求点击下载后遮罩层显示下载进度:1.圆形进度条参考以上链接,有点小瑕疵,可更改定位距离实现重合。 2.遮罩层:.lbOverlay{ display:none; position:fixed; left:0; ... 查看详情

qt浅谈之四十二钟表摆动显示百分比

...以适用于显示百分比或进度条的进度或时间的刻度值(在圆形进度条上的一种改进)。效果如下:二、详解1、代码(1)DashboardDisplay.h#ifndefDASHBOARDPROCESS_H#defineDASHBOARDPROCESS_H#in 查看详情

ios制作个圆形进度条

参考技术A1.需要做个这样的圆形进度条 自定义弹窗view就不说了,主要是这个圆形进度view,底色是灰色然后有进度灰色被黑色覆盖或者说变成黑色首先我们自定一个view,加个uilabel做进度lbael@property(nonatomic,strong)UILabel*percentLabel;/... 查看详情

圆形进度条 Android

】圆形进度条Android【英文标题】:CircularProgressBarAndroid【发布时间】:2015-06-0320:06:35【问题描述】:我想创建一个分段的圆形进度条,其行为类似于倒数计时器,这样每个分段都有自己的时间限制。有一个iOS应用pTimer+,我想在An... 查看详情

带有图像和圆形进度条的圆形按钮

】带有图像和圆形进度条的圆形按钮【英文标题】:Circularbuttonwithimageandcircularprogressbar【发布时间】:2019-12-2819:29:53【问题描述】:我正在尝试制作一个按钮,里面有一个图像,外面有一个圆形进度条,按下按钮后是彩色的。我... 查看详情

带圆角的 Android 圆形进度条

】带圆角的Android圆形进度条【英文标题】:Androidcircularprogressbarwithroundedcorners【发布时间】:2016-08-0623:37:54【问题描述】:我正在尝试获得一个带有圆角的圆形进度条,如下所示。但到目前为止,我无法获得圆角,我能够获得圆... 查看详情

googlechrome圆形进度条

Conmajia?2012UpdatedonFeb.21,2018GoogleChrome的圆形进度条。Demo功能显示百分比(0-100)。如果进度值达到100%,则将闪烁指定次数。属性BlinkCount,结束后闪烁几次。0为不闪烁,默认2.BlinkSpeed,闪烁速度,0-255,默认10.Image,中央图标SpokeCol... 查看详情

css3做圆形进度条

...天就重点讲解这个效果,首先,当有人说你能不能做一个圆形进度条效果出来时,如果是静态完整圆形进度条,那么就很简单了:.circleprogress{  width:160px;  height:160px;  border:20pxsolidred;  border-radius:50%;}... 查看详情

Visual Basic 圆形进度条

】VisualBasic圆形进度条【英文标题】:Visualbasiccircularprogressbar【发布时间】:2015-01-1118:11:15【问题描述】:我正在尝试制作一个具有良好UI的软件,但我在VB方面并不专业......如何制作圆形进度条?举例【问题讨论】:见codeproject.... 查看详情

快速创建围绕图像的圆形进度条

】快速创建围绕图像的圆形进度条【英文标题】:Creatingacircularprogressbararoundanimageinswift【发布时间】:2016-07-2712:05:09【问题描述】:我正在尝试在图像周围创建一个圆形进度条,如下面的屏幕截图所示。到目前为止,我已经设法... 查看详情

纯css3实现圆形进度条动画

悄悄地,GIF格式的进度条已经越来越少,CSS进度条如雨后春笋般涌现。今天要介绍的这个CSS3进度条,效果和FlymeOS4上的加载动画一样。首先,来看下最终的效果:  查看详情

如何更改圆形进度条的颜色?

】如何更改圆形进度条的颜色?【英文标题】:Howtochangecolorincircularprogressbar?【发布时间】:2011-07-1707:03:54【问题描述】:我在Android上使用循环进度条。我想改变它的颜色。我正在使用"?android:attr/progressBarStyleLargeInverse"风格。那... 查看详情

javaswing圆形加载进度条(代码片段)

查看详情

android中的圆形进度条

】android中的圆形进度条【英文标题】:Circularprogressbarinandroid【发布时间】:2015-09-1318:26:05【问题描述】:我想实现一些圆形进度条,中间有一个按钮,就像在whatsapp中一样。有谁知道一个好的图书馆来实现这一点?我想要完成... 查看详情

如何重新加载圆形进度条

】如何重新加载圆形进度条【英文标题】:howtoreloadacircularprogressbar【发布时间】:2015-12-0921:59:58【问题描述】:我对画布以及如何创建圆形进度条进行了一些网络搜索,我得到了这个。我对在网上找到的代码片段进行了一些更... 查看详情

如何创建圆形样式进度条

】如何创建圆形样式进度条【英文标题】:HowtocreateaCircularStyleProgressBar【发布时间】:2011-06-1918:24:09【问题描述】:我需要帮助来实现这样的循环进度条:我应该如何通过增加Value属性来实现Circle来填充?【问题讨论】:相关链... 查看详情

尝试在 Swift 中创建圆形进度条

】尝试在Swift中创建圆形进度条【英文标题】:TryingtocreatecircularprogressbarinSwift【发布时间】:2014-09-2722:45:58【问题描述】:我正在尝试在Swift中为我的应用程序添加一个圆形进度条,但是我无法找到任何Swift特定示例。因此,我... 查看详情