c++qtcounterqtwidgets

Custom circular Countdown progressbar in Qt widget C++


I want a circular time counter to display the remaining time of a process that looks like the figure below I use Qty 6.6.3 and Qty widgets If anyone has an idea to do this, I would appreciate it if you could help me.

Custum circular time counter

I wrote the code similar to what I want, but this model is loading and the user cannot see the remaining time.


CircleLoadingWidget.cpp

#include <QtGui/qpainter.h>
#include <QtWidgets/qapplication.h>
#include "circle_loading_widget.h"


CircleLoadingWidget::CircleLoadingWidget(QWidget *parent)
    : QWidget(parent)
    , background(Qt::transparent)
    , timerId(0)
{
}


CircleLoadingWidget::~CircleLoadingWidget()
{
}


void CircleLoadingWidget::setColors(const QList<QColor> &colors)
{
    this->colors = colors;
    if (timerId) {
        update();
    }
}


QColor CircleLoadingWidget::getColor(int i)
{
    if (colors.size() > i) {
        return colors.at(i);
    } else {
        switch (i) {
        case 0:
            return QColor("#dc322f");
        case 1:
            return QColor("#268bd2");
        default:
            return QColor("#90CAF9");
        }
    }
}


void CircleLoadingWidget::setBackground(const QBrush &background)
{
    this->background = background;
    if (timerId) {
        update();
    }
}


void CircleLoadingWidget::start()
{
    if (!timerId) {
        timerId = startTimer(1000 / 60);
        timer.restart();
    }
}


void CircleLoadingWidget::stop()
{
    if (timerId) {
        killTimer(timerId);
        timerId = 0;
    }
}


void CircleLoadingWidget::paintEvent(QPaintEvent *event)
{
    QWidget::paintEvent(event);
    qint64 msecs = timer.elapsed();
    msecs %= 5000;
    bool inverted;
    if (msecs > 2500) {
        msecs = msecs - 2500;
        inverted = true;
    } else {
        inverted = false;
    }
    QEasingCurve headCurve(QEasingCurve::OutQuart);
    QEasingCurve tailCurve(QEasingCurve::InQuad);

    QRect r0 = rect();
    if (r0.width() > r0.height()) {
        r0.setWidth(r0.height());
    } else {
        r0.setHeight(r0.width());
    }
    r0.moveCenter(rect().center());
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);
    painter.setBrush(background);
    painter.setPen(background.color());
    painter.drawRect(rect());
    for (int i = 0; i < 1; ++i) {
        qreal k1 = qMin(msecs / (1000.0 + 1000.0 / 10 * i), 1.0);
        qreal k2 = qMin(msecs / (1500.0 + 1000.0 / 10 * i), 1.0);
        if (qFuzzyCompare(k1, k2)) {
            continue;
        }

        qreal headAngle, tailAngle, spanAngle;
        if (inverted) {
            headAngle = 90 - headCurve.valueForProgress(k1) * 360;
            tailAngle = 90 - tailCurve.valueForProgress(k2) * 360;
        } else {
            headAngle = headCurve.valueForProgress(k1) * 360 + 90;
            tailAngle = tailCurve.valueForProgress(k2) * 360 + 90;
        }
        spanAngle =  tailAngle - headAngle;

        QRect r1 = r0 ;
        r1.setSize(r0.size() * (0.1 * (10 - i)));
        r1.moveCenter(r0.center());
        QRect r2 = r0;
        r2.setSize(r0.size() * (0.1 * (9 - i) + 0.01));
        r2.moveCenter(r0.center());

        qreal l1 = 1.0 - headCurve.valueForProgress(k1);
        qreal l3 = tailCurve.valueForProgress(k1);
        qreal l2 = 1.0 - l1 - l3;

        painter.setBrush(getColor(0));
        painter.drawPie(r1, headAngle * 16, spanAngle * 16 * l1 * 0.9);
        painter.setBrush(background);
        painter.drawPie(r2, headAngle * 16, spanAngle * 16 * l1 * 0.9);

        painter.setBrush(getColor(1));
        painter.drawPie(r1, headAngle * 16 + spanAngle * 16 * l1, spanAngle * 16 * l2 * 0.9);
        painter.setBrush(background);
        painter.drawPie(r2, headAngle * 16 + spanAngle * 16 * l1, spanAngle * 16 * l2 * 0.9);

        painter.setBrush(getColor(2));
        painter.drawPie(r1, headAngle * 16 + spanAngle * 16 * (l1 + l2), spanAngle * 16 * l3 * 0.9);
        painter.setBrush(background);
        painter.drawPie(r2, headAngle * 16 + spanAngle * 16 * (l1 + l2), spanAngle * 16 * l3 * 0.9);
    }
}



void CircleLoadingWidget::timerEvent(QTimerEvent *event)
{
    if (event->timerId() != timerId) {
        QWidget::timerEvent(event);
        return;
    }
    update();
}

CircleLoadingWidget.h

#ifndef CIRCLELOADINGWIDGET_H
#define CIRCLELOADINGWIDGET_H

#include <QtCore/qtimer.h>
#include <QtCore/qelapsedtimer.h>
#include <QtCore/qeasingcurve.h>
#include <QtWidgets/qwidget.h>


class CircleLoadingWidget : public QWidget
{
    Q_OBJECT
public:
    explicit CircleLoadingWidget(QWidget *parent = nullptr);
    virtual ~CircleLoadingWidget() override;
public:
    void setColors(const QList<QColor> &colors);
    void setBackground(const QBrush &background);
    void start();
    void stop();
protected:
    virtual void paintEvent(QPaintEvent *event) override;
    virtual void timerEvent(QTimerEvent *event) override;
private:
    QColor getColor(int i);
private:
    QList<QColor> colors;
    QBrush background;
    QElapsedTimer timer;
    int timerId;
};


#endif // CIRCLELOADINGWIDGET_H

the result is like this (it has loading animation and user can not see the remaining time and proccess):

enter image description here enter image description here


Solution

  • The result you get is very different from the first image, so it's not entirely clear what goal you want to achieve. Actually for the first image I would do the following:

    class Gauge: public QWidget
    {
    public:
        Gauge(QWidget *parent = nullptr)
            : QWidget(parent)
        {}
        
        void setValue(int value)
        {
            if (value != m_value) {
                m_value = qBound(0, value, 100);
                update();
            }
        }
    
    protected:
        void paintEvent(QPaintEvent *event) override
        {
            QPainter painter(this);
            painter.setRenderHint(QPainter::Antialiasing);
            painter.fillRect(rect(), Qt::white);
            
            int size = qMin(width(), height()) - 40;
            QRectF rect(20, 20, size, size);
            
            QPen pen(QColor::fromString("#eaeaea"), 20, Qt::SolidLine, Qt::RoundCap);
            painter.setPen(pen);
            painter.drawEllipse(rect);
            
            pen.setColor(QColor::fromString("#4a62ad"));
            painter.setPen(pen);
            painter.drawArc(rect, 90 * 16, -m_value * 16 * 3.6);
            
            painter.setPen(QColor::fromString("#333333"));
            QFont font = painter.font();
            font.setBold(true);
            font.setPointSize(size / 5);
            painter.setFont(font);
            painter.drawText(rect, Qt::AlignCenter, QString::number(m_value) + "%");
        }
    
    private:
        int m_value{0};
    };
    

    That probably should look like this:

    enter image description here