File indexing completed on 2024-04-21 03:42:34

0001 /*
0002     QRoundProgressBar - a circular progress bar Qt widget.
0003 
0004     Sintegrial Technologies (c) 2015-now
0005 
0006     The software is freeware and is distributed "as is" with the complete source codes.
0007     Anybody is free to use it in any software projects, either commercial or non-commercial.
0008     Please do not remove this copyright message and remain the name of the author unchanged.
0009 
0010     It is very appreciated if you produce some feedback to us case you are going to use
0011     the software.
0012 
0013     Please send your questions, suggestions, and information about found issues to the
0014 
0015     sintegrial@gmail.com
0016 
0017 */
0018 
0019 #include "QRoundProgressBar.h"
0020 
0021 #include <QPainter>
0022 #include <QPainterPath>
0023 
0024 QRoundProgressBar::QRoundProgressBar(QWidget *parent)
0025     : QWidget(parent), m_min(0), m_max(100), m_value(25), m_nullPosition(PositionTop), m_barStyle(StyleDonut),
0026       m_outlinePenWidth(1), m_dataPenWidth(1), m_rebuildBrush(false), m_format("%p%"), m_decimals(1),
0027       m_updateFlags(UF_PERCENT)
0028 {
0029 }
0030 
0031 void QRoundProgressBar::setRange(double min, double max)
0032 {
0033     m_min = min;
0034     m_max = max;
0035 
0036     if (m_max < m_min)
0037         qSwap(m_max, m_min);
0038 
0039     if (m_value < m_min)
0040         m_value = m_min;
0041     else if (m_value > m_max)
0042         m_value = m_max;
0043 
0044     if (!m_gradientData.isEmpty())
0045         m_rebuildBrush = true;
0046 
0047     update();
0048 }
0049 
0050 void QRoundProgressBar::setMinimum(double min)
0051 {
0052     setRange(min, m_max);
0053 }
0054 
0055 void QRoundProgressBar::setMaximum(double max)
0056 {
0057     setRange(m_min, max);
0058 }
0059 
0060 void QRoundProgressBar::setValue(double val)
0061 {
0062     if (m_value != val)
0063     {
0064         if (val < m_min)
0065             m_value = m_min;
0066         else if (val > m_max)
0067             m_value = m_max;
0068         else
0069             m_value = val;
0070 
0071         update();
0072     }
0073 }
0074 
0075 void QRoundProgressBar::setValue(int val)
0076 {
0077     setValue(double(val));
0078 }
0079 
0080 void QRoundProgressBar::setNullPosition(double position)
0081 {
0082     if (position != m_nullPosition)
0083     {
0084         m_nullPosition = position;
0085 
0086         if (!m_gradientData.isEmpty())
0087             m_rebuildBrush = true;
0088 
0089         update();
0090     }
0091 }
0092 
0093 void QRoundProgressBar::setBarStyle(QRoundProgressBar::BarStyle style)
0094 {
0095     if (style != m_barStyle)
0096     {
0097         m_barStyle = style;
0098 
0099         update();
0100     }
0101 }
0102 
0103 void QRoundProgressBar::setOutlinePenWidth(double penWidth)
0104 {
0105     if (penWidth != m_outlinePenWidth)
0106     {
0107         m_outlinePenWidth = penWidth;
0108 
0109         update();
0110     }
0111 }
0112 
0113 void QRoundProgressBar::setDataPenWidth(double penWidth)
0114 {
0115     if (penWidth != m_dataPenWidth)
0116     {
0117         m_dataPenWidth = penWidth;
0118 
0119         update();
0120     }
0121 }
0122 
0123 void QRoundProgressBar::setDataColors(const QGradientStops &stopPoints)
0124 {
0125     if (stopPoints != m_gradientData)
0126     {
0127         m_gradientData = stopPoints;
0128         m_rebuildBrush = true;
0129 
0130         update();
0131     }
0132 }
0133 
0134 void QRoundProgressBar::setFormat(const QString &format)
0135 {
0136     if (format != m_format)
0137     {
0138         m_format = format;
0139 
0140         valueFormatChanged();
0141     }
0142 }
0143 
0144 void QRoundProgressBar::resetFormat()
0145 {
0146     m_format.clear();
0147     valueFormatChanged();
0148 }
0149 
0150 void QRoundProgressBar::setDecimals(int count)
0151 {
0152     if (count >= 0 && count != m_decimals)
0153     {
0154         m_decimals = count;
0155 
0156         valueFormatChanged();
0157     }
0158 }
0159 
0160 void QRoundProgressBar::paintEvent(QPaintEvent * /*event*/)
0161 {
0162 #if 0
0163     double outerRadius = qMin(width(), height());
0164     QRectF baseRect(1, 1, outerRadius-2, outerRadius-2);
0165 
0166     QImage buffer(outerRadius, outerRadius, QImage::Format_ARGB32_Premultiplied);
0167 
0168     QPainter p(&buffer);
0169     p.setRenderHint(QPainter::Antialiasing);
0170 
0171     // data brush
0172     rebuildDataBrushIfNeeded();
0173 
0174     // background
0175     drawBackground(p, buffer.rect());
0176 
0177     // base circle
0178     drawBase(p, baseRect);
0179 
0180     // data circle
0181     double arcStep = 360.0 / (m_max - m_min) * m_value;
0182     drawValue(p, baseRect, m_value, arcStep);
0183 
0184     // center circle
0185     double innerRadius(0);
0186     QRectF innerRect;
0187     calculateInnerRect(baseRect, outerRadius, innerRect, innerRadius);
0188     drawInnerBackground(p, innerRect);
0189 
0190     // text
0191     drawText(p, innerRect, innerRadius, m_value);
0192 
0193     // finally draw the bar
0194     p.end();
0195 
0196     QPainter painter(this);
0197     painter.fillRect(baseRect, palette().background());
0198     painter.drawImage(0,0, buffer);
0199 #endif
0200     double outerRadius = qMin(width(), height());
0201     QRectF baseRect(1, 1, outerRadius - 2, outerRadius - 2);
0202     QPainter p(this);
0203     //painter.fillRect(baseRect, palette().window());
0204     p.setRenderHint(QPainter::Antialiasing);
0205     p.fillRect(baseRect, Qt::NoBrush);
0206     drawBase(p, baseRect);
0207     // data circle
0208     double arcStep = 360.0 / (m_max - m_min) * m_value;
0209     drawValue(p, baseRect, m_value, arcStep);
0210 
0211     // center circle
0212     double innerRadius(0);
0213     QRectF innerRect;
0214     calculateInnerRect(baseRect, outerRadius, innerRect, innerRadius);
0215     drawInnerBackground(p, innerRect);
0216 
0217     // text
0218     drawText(p, innerRect, innerRadius, m_value);
0219 }
0220 
0221 void QRoundProgressBar::drawBackground(QPainter &p, const QRectF &baseRect)
0222 {
0223     p.fillRect(baseRect, palette().window());
0224 }
0225 
0226 void QRoundProgressBar::drawBase(QPainter &p, const QRectF &baseRect)
0227 {
0228     switch (m_barStyle)
0229     {
0230         case StyleDonut:
0231             p.setPen(QPen(palette().shadow().color(), m_outlinePenWidth));
0232             p.setBrush(palette().base());
0233             p.drawEllipse(baseRect);
0234             break;
0235 
0236         case StylePie:
0237             p.setPen(QPen(palette().base().color(), m_outlinePenWidth));
0238             p.setBrush(palette().base());
0239             p.drawEllipse(baseRect);
0240             break;
0241 
0242         case StyleLine:
0243             p.setPen(QPen(palette().base().color(), m_outlinePenWidth));
0244             p.setBrush(Qt::NoBrush);
0245             p.drawEllipse(baseRect.adjusted(m_outlinePenWidth / 2, m_outlinePenWidth / 2, -m_outlinePenWidth / 2,
0246                                             -m_outlinePenWidth / 2));
0247             break;
0248 
0249         default:;
0250     }
0251 }
0252 
0253 void QRoundProgressBar::drawValue(QPainter &p, const QRectF &baseRect, double value, double arcLength)
0254 {
0255     // nothing to draw
0256     if (value == m_min)
0257         return;
0258 
0259     // for Line style
0260     if (m_barStyle == StyleLine)
0261     {
0262         p.setPen(QPen(palette().highlight().color(), m_dataPenWidth));
0263         p.setBrush(Qt::NoBrush);
0264         p.drawArc(baseRect.adjusted(m_outlinePenWidth / 2, m_outlinePenWidth / 2, -m_outlinePenWidth / 2,
0265                                     -m_outlinePenWidth / 2),
0266                   m_nullPosition * 16, -arcLength * 16);
0267         return;
0268     }
0269 
0270     // for Pie and Donut styles
0271     QPainterPath dataPath;
0272     dataPath.setFillRule(Qt::WindingFill);
0273 
0274     // pie segment outer
0275     dataPath.moveTo(baseRect.center());
0276     dataPath.arcTo(baseRect, m_nullPosition, -arcLength);
0277     dataPath.lineTo(baseRect.center());
0278 
0279     p.setBrush(palette().highlight());
0280     p.setPen(QPen(palette().shadow().color(), m_dataPenWidth));
0281     p.drawPath(dataPath);
0282 }
0283 
0284 void QRoundProgressBar::calculateInnerRect(const QRectF & /*baseRect*/, double outerRadius, QRectF &innerRect,
0285                                            double &innerRadius)
0286 {
0287     // for Line style
0288     if (m_barStyle == StyleLine)
0289     {
0290         innerRadius = outerRadius - m_outlinePenWidth;
0291     }
0292     else // for Pie and Donut styles
0293     {
0294         innerRadius = outerRadius * 0.75;
0295     }
0296 
0297     double delta = (outerRadius - innerRadius) / 2;
0298     innerRect    = QRectF(delta, delta, innerRadius, innerRadius);
0299 }
0300 
0301 void QRoundProgressBar::drawInnerBackground(QPainter &p, const QRectF &innerRect)
0302 {
0303     if (m_barStyle == StyleDonut)
0304     {
0305         p.setBrush(palette().alternateBase());
0306         p.drawEllipse(innerRect);
0307     }
0308 }
0309 
0310 void QRoundProgressBar::drawText(QPainter &p, const QRectF &innerRect, double innerRadius, double value)
0311 {
0312     if (m_format.isEmpty())
0313         return;
0314 
0315     // !!! to revise
0316     QFont f(font());
0317     f.setPixelSize(innerRadius * qMax(0.05, (0.35 - (double)m_decimals * 0.08)));
0318     p.setFont(f);
0319 
0320     QRectF textRect(innerRect);
0321     p.setPen(palette().text().color());
0322     p.drawText(textRect, Qt::AlignCenter, valueToText(value));
0323 }
0324 
0325 QString QRoundProgressBar::valueToText(double value) const
0326 {
0327     QString textToDraw(m_format);
0328 
0329     if (m_updateFlags & UF_VALUE)
0330         textToDraw.replace("%v", QString::number(value, 'f', m_decimals));
0331 
0332     if (m_updateFlags & UF_PERCENT)
0333     {
0334         double procent = (value - m_min) / (m_max - m_min) * 100.0;
0335         textToDraw.replace("%p", QString::number(procent, 'f', m_decimals));
0336     }
0337 
0338     if (m_updateFlags & UF_MAX)
0339         textToDraw.replace("%m", QString::number(m_max - m_min + 1, 'f', m_decimals));
0340 
0341     return textToDraw;
0342 }
0343 
0344 void QRoundProgressBar::valueFormatChanged()
0345 {
0346     m_updateFlags = 0;
0347 
0348     if (m_format.contains("%v"))
0349         m_updateFlags |= UF_VALUE;
0350 
0351     if (m_format.contains("%p"))
0352         m_updateFlags |= UF_PERCENT;
0353 
0354     if (m_format.contains("%m"))
0355         m_updateFlags |= UF_MAX;
0356 
0357     update();
0358 }
0359 
0360 void QRoundProgressBar::rebuildDataBrushIfNeeded()
0361 {
0362     if (m_rebuildBrush)
0363     {
0364         m_rebuildBrush = false;
0365 
0366         QConicalGradient dataBrush;
0367         dataBrush.setCenter(0.5, 0.5);
0368         dataBrush.setCoordinateMode(QGradient::StretchToDeviceMode);
0369 
0370         // invert colors
0371         for (int i = 0; i < m_gradientData.count(); i++)
0372         {
0373             dataBrush.setColorAt(1.0 - m_gradientData.at(i).first, m_gradientData.at(i).second);
0374         }
0375 
0376         // angle
0377         dataBrush.setAngle(m_nullPosition);
0378 
0379         QPalette p(palette());
0380         p.setBrush(QPalette::Highlight, dataBrush);
0381         setPalette(p);
0382     }
0383 }