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.
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):
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: