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 }