1 /*
2 QRoundProgressBar - a circular progress bar Qt widget.
3
4 Sintegrial Technologies (c) 2015-now
5
6 The software is freeware and is distributed "as is" with the complete source codes.
7 Anybody is free to use it in any software projects, either commercial or non-commercial.
8 Please do not remove this copyright message and remain the name of the author unchanged.
9
10 It is very appreciated if you produce some feedback to us case you are going to use
11 the software.
12
13 Please send your questions, suggestions, and information about found issues to the
14
15 sintegrial@gmail.com
16
17 */
18
19 #include "QRoundProgressBar.h"
20
21 #include <QPainter>
22 #include <QPainterPath>
23
QRoundProgressBar(QWidget * parent)24 QRoundProgressBar::QRoundProgressBar(QWidget *parent)
25 : QWidget(parent), m_min(0), m_max(100), m_value(25), m_nullPosition(PositionTop), m_barStyle(StyleDonut),
26 m_outlinePenWidth(1), m_dataPenWidth(1), m_rebuildBrush(false), m_format("%p%"), m_decimals(1),
27 m_updateFlags(UF_PERCENT)
28 {
29 }
30
setRange(double min,double max)31 void QRoundProgressBar::setRange(double min, double max)
32 {
33 m_min = min;
34 m_max = max;
35
36 if (m_max < m_min)
37 qSwap(m_max, m_min);
38
39 if (m_value < m_min)
40 m_value = m_min;
41 else if (m_value > m_max)
42 m_value = m_max;
43
44 if (!m_gradientData.isEmpty())
45 m_rebuildBrush = true;
46
47 update();
48 }
49
setMinimum(double min)50 void QRoundProgressBar::setMinimum(double min)
51 {
52 setRange(min, m_max);
53 }
54
setMaximum(double max)55 void QRoundProgressBar::setMaximum(double max)
56 {
57 setRange(m_min, max);
58 }
59
setValue(double val)60 void QRoundProgressBar::setValue(double val)
61 {
62 if (m_value != val)
63 {
64 if (val < m_min)
65 m_value = m_min;
66 else if (val > m_max)
67 m_value = m_max;
68 else
69 m_value = val;
70
71 update();
72 }
73 }
74
setValue(int val)75 void QRoundProgressBar::setValue(int val)
76 {
77 setValue(double(val));
78 }
79
setNullPosition(double position)80 void QRoundProgressBar::setNullPosition(double position)
81 {
82 if (position != m_nullPosition)
83 {
84 m_nullPosition = position;
85
86 if (!m_gradientData.isEmpty())
87 m_rebuildBrush = true;
88
89 update();
90 }
91 }
92
setBarStyle(QRoundProgressBar::BarStyle style)93 void QRoundProgressBar::setBarStyle(QRoundProgressBar::BarStyle style)
94 {
95 if (style != m_barStyle)
96 {
97 m_barStyle = style;
98
99 update();
100 }
101 }
102
setOutlinePenWidth(double penWidth)103 void QRoundProgressBar::setOutlinePenWidth(double penWidth)
104 {
105 if (penWidth != m_outlinePenWidth)
106 {
107 m_outlinePenWidth = penWidth;
108
109 update();
110 }
111 }
112
setDataPenWidth(double penWidth)113 void QRoundProgressBar::setDataPenWidth(double penWidth)
114 {
115 if (penWidth != m_dataPenWidth)
116 {
117 m_dataPenWidth = penWidth;
118
119 update();
120 }
121 }
122
setDataColors(const QGradientStops & stopPoints)123 void QRoundProgressBar::setDataColors(const QGradientStops &stopPoints)
124 {
125 if (stopPoints != m_gradientData)
126 {
127 m_gradientData = stopPoints;
128 m_rebuildBrush = true;
129
130 update();
131 }
132 }
133
setFormat(const QString & format)134 void QRoundProgressBar::setFormat(const QString &format)
135 {
136 if (format != m_format)
137 {
138 m_format = format;
139
140 valueFormatChanged();
141 }
142 }
143
resetFormat()144 void QRoundProgressBar::resetFormat()
145 {
146 m_format.clear();
147 valueFormatChanged();
148 }
149
setDecimals(int count)150 void QRoundProgressBar::setDecimals(int count)
151 {
152 if (count >= 0 && count != m_decimals)
153 {
154 m_decimals = count;
155
156 valueFormatChanged();
157 }
158 }
159
paintEvent(QPaintEvent *)160 void QRoundProgressBar::paintEvent(QPaintEvent * /*event*/)
161 {
162 #if 0
163 double outerRadius = qMin(width(), height());
164 QRectF baseRect(1, 1, outerRadius-2, outerRadius-2);
165
166 QImage buffer(outerRadius, outerRadius, QImage::Format_ARGB32_Premultiplied);
167
168 QPainter p(&buffer);
169 p.setRenderHint(QPainter::Antialiasing);
170
171 // data brush
172 rebuildDataBrushIfNeeded();
173
174 // background
175 drawBackground(p, buffer.rect());
176
177 // base circle
178 drawBase(p, baseRect);
179
180 // data circle
181 double arcStep = 360.0 / (m_max - m_min) * m_value;
182 drawValue(p, baseRect, m_value, arcStep);
183
184 // center circle
185 double innerRadius(0);
186 QRectF innerRect;
187 calculateInnerRect(baseRect, outerRadius, innerRect, innerRadius);
188 drawInnerBackground(p, innerRect);
189
190 // text
191 drawText(p, innerRect, innerRadius, m_value);
192
193 // finally draw the bar
194 p.end();
195
196 QPainter painter(this);
197 painter.fillRect(baseRect, palette().background());
198 painter.drawImage(0,0, buffer);
199 #endif
200 double outerRadius = qMin(width(), height());
201 QRectF baseRect(1, 1, outerRadius - 2, outerRadius - 2);
202 QPainter p(this);
203 //painter.fillRect(baseRect, palette().window());
204 p.setRenderHint(QPainter::Antialiasing);
205 p.fillRect(baseRect, Qt::NoBrush);
206 drawBase(p, baseRect);
207 // data circle
208 double arcStep = 360.0 / (m_max - m_min) * m_value;
209 drawValue(p, baseRect, m_value, arcStep);
210
211 // center circle
212 double innerRadius(0);
213 QRectF innerRect;
214 calculateInnerRect(baseRect, outerRadius, innerRect, innerRadius);
215 drawInnerBackground(p, innerRect);
216
217 // text
218 drawText(p, innerRect, innerRadius, m_value);
219 }
220
drawBackground(QPainter & p,const QRectF & baseRect)221 void QRoundProgressBar::drawBackground(QPainter &p, const QRectF &baseRect)
222 {
223 p.fillRect(baseRect, palette().window());
224 }
225
drawBase(QPainter & p,const QRectF & baseRect)226 void QRoundProgressBar::drawBase(QPainter &p, const QRectF &baseRect)
227 {
228 switch (m_barStyle)
229 {
230 case StyleDonut:
231 p.setPen(QPen(palette().shadow().color(), m_outlinePenWidth));
232 p.setBrush(palette().base());
233 p.drawEllipse(baseRect);
234 break;
235
236 case StylePie:
237 p.setPen(QPen(palette().base().color(), m_outlinePenWidth));
238 p.setBrush(palette().base());
239 p.drawEllipse(baseRect);
240 break;
241
242 case StyleLine:
243 p.setPen(QPen(palette().base().color(), m_outlinePenWidth));
244 p.setBrush(Qt::NoBrush);
245 p.drawEllipse(baseRect.adjusted(m_outlinePenWidth / 2, m_outlinePenWidth / 2, -m_outlinePenWidth / 2,
246 -m_outlinePenWidth / 2));
247 break;
248
249 default:;
250 }
251 }
252
drawValue(QPainter & p,const QRectF & baseRect,double value,double arcLength)253 void QRoundProgressBar::drawValue(QPainter &p, const QRectF &baseRect, double value, double arcLength)
254 {
255 // nothing to draw
256 if (value == m_min)
257 return;
258
259 // for Line style
260 if (m_barStyle == StyleLine)
261 {
262 p.setPen(QPen(palette().highlight().color(), m_dataPenWidth));
263 p.setBrush(Qt::NoBrush);
264 p.drawArc(baseRect.adjusted(m_outlinePenWidth / 2, m_outlinePenWidth / 2, -m_outlinePenWidth / 2,
265 -m_outlinePenWidth / 2),
266 m_nullPosition * 16, -arcLength * 16);
267 return;
268 }
269
270 // for Pie and Donut styles
271 QPainterPath dataPath;
272 dataPath.setFillRule(Qt::WindingFill);
273
274 // pie segment outer
275 dataPath.moveTo(baseRect.center());
276 dataPath.arcTo(baseRect, m_nullPosition, -arcLength);
277 dataPath.lineTo(baseRect.center());
278
279 p.setBrush(palette().highlight());
280 p.setPen(QPen(palette().shadow().color(), m_dataPenWidth));
281 p.drawPath(dataPath);
282 }
283
calculateInnerRect(const QRectF &,double outerRadius,QRectF & innerRect,double & innerRadius)284 void QRoundProgressBar::calculateInnerRect(const QRectF & /*baseRect*/, double outerRadius, QRectF &innerRect,
285 double &innerRadius)
286 {
287 // for Line style
288 if (m_barStyle == StyleLine)
289 {
290 innerRadius = outerRadius - m_outlinePenWidth;
291 }
292 else // for Pie and Donut styles
293 {
294 innerRadius = outerRadius * 0.75;
295 }
296
297 double delta = (outerRadius - innerRadius) / 2;
298 innerRect = QRectF(delta, delta, innerRadius, innerRadius);
299 }
300
drawInnerBackground(QPainter & p,const QRectF & innerRect)301 void QRoundProgressBar::drawInnerBackground(QPainter &p, const QRectF &innerRect)
302 {
303 if (m_barStyle == StyleDonut)
304 {
305 p.setBrush(palette().alternateBase());
306 p.drawEllipse(innerRect);
307 }
308 }
309
drawText(QPainter & p,const QRectF & innerRect,double innerRadius,double value)310 void QRoundProgressBar::drawText(QPainter &p, const QRectF &innerRect, double innerRadius, double value)
311 {
312 if (m_format.isEmpty())
313 return;
314
315 // !!! to revise
316 QFont f(font());
317 f.setPixelSize(innerRadius * qMax(0.05, (0.35 - (double)m_decimals * 0.08)));
318 p.setFont(f);
319
320 QRectF textRect(innerRect);
321 p.setPen(palette().text().color());
322 p.drawText(textRect, Qt::AlignCenter, valueToText(value));
323 }
324
valueToText(double value) const325 QString QRoundProgressBar::valueToText(double value) const
326 {
327 QString textToDraw(m_format);
328
329 if (m_updateFlags & UF_VALUE)
330 textToDraw.replace("%v", QString::number(value, 'f', m_decimals));
331
332 if (m_updateFlags & UF_PERCENT)
333 {
334 double procent = (value - m_min) / (m_max - m_min) * 100.0;
335 textToDraw.replace("%p", QString::number(procent, 'f', m_decimals));
336 }
337
338 if (m_updateFlags & UF_MAX)
339 textToDraw.replace("%m", QString::number(m_max - m_min + 1, 'f', m_decimals));
340
341 return textToDraw;
342 }
343
valueFormatChanged()344 void QRoundProgressBar::valueFormatChanged()
345 {
346 m_updateFlags = 0;
347
348 if (m_format.contains("%v"))
349 m_updateFlags |= UF_VALUE;
350
351 if (m_format.contains("%p"))
352 m_updateFlags |= UF_PERCENT;
353
354 if (m_format.contains("%m"))
355 m_updateFlags |= UF_MAX;
356
357 update();
358 }
359
rebuildDataBrushIfNeeded()360 void QRoundProgressBar::rebuildDataBrushIfNeeded()
361 {
362 if (m_rebuildBrush)
363 {
364 m_rebuildBrush = false;
365
366 QConicalGradient dataBrush;
367 dataBrush.setCenter(0.5, 0.5);
368 dataBrush.setCoordinateMode(QGradient::StretchToDeviceMode);
369
370 // invert colors
371 for (int i = 0; i < m_gradientData.count(); i++)
372 {
373 dataBrush.setColorAt(1.0 - m_gradientData.at(i).first, m_gradientData.at(i).second);
374 }
375
376 // angle
377 dataBrush.setAngle(m_nullPosition);
378
379 QPalette p(palette());
380 p.setBrush(QPalette::Highlight, dataBrush);
381 setPalette(p);
382 }
383 }
384