Improvement of Qt third-party circular progress bar


      To implement a circular progress bar function, find a well-known third-party package class on the Internet: QRoundProgressBar, address: sourceforge The QRoundProgressBar 
       function is well packaged, providing 3 modes, linear, circular, and pie. During use, it is found that the circular progress bar has insufficient support for background transparency, and the background of the inner circle cannot be transparent. In order to solve this problem, some revisions have been made to this control below to realize a complete circular progress bar.

The current shortcomings of QRoundProgressBar

When QRoundProgressBar uses the StyleDonut style under the widget with a background image, the background of the inner ring cannot be transparent:

code show as below:

.h

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

    Here, QPalette::Window and QPalette::AlternateBase are set to transparent, and it is found that the ring cannot be drawn

reason

    Check out the code to see the steps it takes to draw a torus

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

      It turns out that drawing the torus and drawing the pie is the same code, and the realization of the torus is just covering it with a background, so that if the middle area is made transparent, the part of the drawn fan will be displayed. Therefore, special treatment is required when QPainterPathdrawing a ring, and the ring is filled when drawing a ring.

Improve

    The original function needs to drawValuebe revised here. The original drawValuefunction handles drawing pie and Donut styles in the same way. Here, when the correction is pie, it draws pie, and when drawing Donut styles, it draws a ring: 
    In order to draw a ring, the drawValuefunction needs to add two variables, is the corresponding rectangle and radius of the inner ring

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

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

     See the code comments for the specific process.

     Here the author fixed the diameter of the inner circle as 0.75 times the outer circle. I think this loses flexibility, so I added a float variable, m_innerOuterRate, which is 0.75 by default, replacing the original 0.75 constant, and added methods float innerOuterRate() constand void setInnerOuterRate(float r)settings.

paintEventThe function order of the      original function also needs to be changed:

    Here is the original paintEvent function:

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

      The original author used QImage as the cache, but qt comes with double buffering, this step is unnecessary (there is still a small problem in ubuntu when using QImage, when the control is relatively small (below 200*200), there will be a blurry screen phenomenon, the reason is unknown), modify for:

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

      The calculateInnerRect(baseRect, outerRadius, innerRect, innerRadius);function is to calculate the parameters corresponding to the inner circle in advance. and passed to the newly modified drawValuefunction. Remove the redundant double buffering mechanism.

      At this time, the effect has not yet reached the required effect, and it is found that drawBasethe function needs to be modified. The original drawBasefunction is as follows:

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:;
    }
}

     The above drawBasefunction can be seen, because the original author is lazy, he directly draws an outer circle for the Donut styles mode, not a hollow circle. The improvement is as follows:

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:;
    }
}

The final running effect:

Related


Improvement of Qt third-party circular progress bar

To implement a circular progress bar function, find a well-known third-party package class on the Internet: QRoundProgressBar, address: sourceforge The QRoundProgressBar function is well packaged, providing 3 modes, linear, circular, and pie. During use, it is

Circular progress bar problem

Martin Ball I've modified the code I found online for a circular progress bar and it works fine until the progress bar reaches 100%, then disappears... I can't figure out what's wrong, I think it has something to do with how ArcSegment works, but I don't under

WPF: Circular progress bar

Martyn Ball I've been poking around around this topic and found a lot of information, it all seems complicated. I found this article really good. But this doesn't really figure out how to calculate the values input into the ArcSegment to make a full circle, do

jQuery circular progress bar

Kelsen I need the following progress bar to run in a loop. So after counting down to zero, start over with the defined value. http://jsfiddle.net/zessx/4PKEB/1/ This is JS <script type='text/javascript'>//<![CDATA[ $(window).load(function(){ function progress(

jQuery circular progress bar

Kelsen I need the following progress bar to run in a loop. So after counting down to zero, start over with the defined value. http://jsfiddle.net/zessx/4PKEB/1/ This is JS <script type='text/javascript'>//<![CDATA[ $(window).load(function(){ function progress(

circular progress bar with arrows

Lucas I've been trying to create a circular progress bar with an arrow in front of it . This is what I have so far HTML <!-- Container --> <ul class="progress"> <!-- Item --> <li data-name="Item 1" data-percent="Item 1"> <svg viewBox="-10 -

WPF: Circular progress bar

Martyn Ball I've been poking around around this topic and found a lot of information, it all seems complicated. I found this article really good. But this doesn't really figure out how to calculate the values input into the ArcSegment to make a full circle, do

WPF: Circular progress bar

Martyn Ball I've been poking around around this topic and found a lot of information, it all seems complicated. I found this article really good. But this doesn't really figure out how to calculate the values input into the ArcSegment to make a full circle, do

jQuery circular progress bar

Kelsen I need the following progress bar to run in a loop. So after counting down to zero, start over with the defined value. http://jsfiddle.net/zessx/4PKEB/1/ This is JS <script type='text/javascript'>//<![CDATA[ $(window).load(function(){ function progress(

Countdown timer with circular progress bar

Alaksandar Jesus Gene I created a countdown timer. I have a circular border. The color of the circular border should decrement in seconds as the timer approaches zero. I created JSFIDDLE HTML <div class="outer"> <button class="btn btn-default btn-timer">0.

Circular progress bar size alignment

Anil P Babu I've been stuck with this problem for a while. May be someone can help. The problem is that the background of my android circular progress bar has different sizes according to the phone screen size. Screenshots should illustrate the problem. The bl

Circular progress bar using Tkinter?

Atalanto I want to add a circular progress bar to my Python GUI using Tkinter, but I didn't find any documentation on Tkinter's circular progress bar. How to create a circular progress bar in Tkinter, or is this not possible? Martino There is no built-in funct

Android circular progress bar in guide

spy__ Is it possible to do a circular loader from material design guidelines? Guide to the circular loader Do you know of any components like this? I guess This is the same material button https://github.com/ckurtm/FabButton

circular progress bar in angular 2

Musher Ayman I am trying to create a percentage loader in angular 2. The loader should load the given percentage amount and it should be dynamic. HTML <div class="col-lg-3 col-md-3 bar"> <div class="c100 p50 small"> <span>50%</span> <div class="slice

Circular slider/progress bar in flutter

Muhammad I want to implement a circular slider for a music player. But packages like slipk_circular_slider ( https://github.com/matthewfx/sleek_circular_slider ) and others like that are not suitable. Means for example the package above is very good at changin

Circular progress bar with Swift UI

msnsk I am creating a countdown timer app using Swift UI. I tried to add a circular progress bar to it, but the appearance is always the same. I have prepared 2 files. One of them encodes the countdown process and the other file encodes the UI. I have data lin

Circular progress bar with inner stroke

Joel Azevedo I am trying to build a circular percentage graph like this: Currently, I have this : https://jsfiddle.net/pvtxgq21/1/ HTML: <svg viewBox="0 0 36 36" class="circular-chart"> <path class="circle-outer" d="M18 2.0845 a 15.9155 15.91

Circular progress bar size alignment

Anil P Babu I've been stuck with this problem for a while. May be someone can help. The problem is that the background of my android circular progress bar has different sizes according to the phone screen size. Screenshots should illustrate the problem. The bl

Countdown timer with circular progress bar

Alaksandar Jesus Gene I created a countdown timer. I have a circular border. The color of the circular border should decrement in seconds as the timer approaches zero. I created JSFIDDLE HTML <div class="outer"> <button class="btn btn-default btn-timer">0.

Circular progress bar using Tkinter?

Atalanto I want to add a circular progress bar to my Python GUI using Tkinter, but I didn't find any documentation on Tkinter's circular progress bar. How to create a circular progress bar in Tkinter, or is this not possible? Martino There is no built-in funct

Circular progress bar, how to implement

snake I'm trying to make a circular progress bar where the progress bar consists of a solid color and a red dot. See the image below. Basically, it's a timer in which the red dot "eats" the white. Here is what I am trying to achieve: Here is the code I used fr

Circular gradient in custom progress bar

number How would you make a circular gradient ProgressBaron screen ? What I have now: pb_shape.xml: <?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background"> <s

Circular Progress Bar - Arrow Type

Little Keynes I have a progress bar that I want to loop before ending or starting, with next and previous links. I don't know how to change my actual javascript to do this: jQuery( document ).ready(function() { var back =jQuery(".js-prev"); var next = j

Circular progress bar using Tkinter?

Atalanto I want to add a circular progress bar to my Python GUI using Tkinter, but I didn't find any documentation on Tkinter's circular progress bar. How to create a circular progress bar in Tkinter, or is this not possible? Martino There is no built-in funct

Circular progress bar size alignment

Anil P Babu I've been stuck with this problem for a while. Maybe someone can help. The problem is that the background of my android circular progress bar has different sizes according to the phone screen size. Screenshots should explain the problem. The blue i

Circular slider/progress bar in flutter

Muhammad I want to implement a circular slider for a music player. But packages like sleep_circular_slider ( https://github.com/matthewfx/sleek_circular_slider ) and others like that don't fit. Means for example the package above is very good at changing posit

Circular progress bar with inner stroke

Joel Azevedo I am trying to build a circular percentage graph like this: Currently, I have this : https://jsfiddle.net/pvtxgq21/1/ HTML: <svg viewBox="0 0 36 36" class="circular-chart"> <path class="circle-outer" d="M18 2.0845 a 15.9155 15.91

Circular progress bar (for countdown timer)

David Baez Ok, I have a 15 second countdown timer that works fine and I want to make a custom looping progress bar for that timer. I want to create a full circle that takes "slices of the pie (circle)" as the timer goes down until there are no more circles. I

Circular progress bar with Swift UI

msnsk I am creating a countdown timer app using Swift UI. I tried to add a circular progress bar to it, but the appearance is always the same. I have prepared 2 files. One of them encodes the countdown process and the other file encodes the UI. I have data lin