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