392 lines
12 KiB
C++
392 lines
12 KiB
C++
/*
|
||
* QRoundProgressBar - a circular progress bar Qt widget.
|
||
*
|
||
* Sintegrial Technologies (c) 2015-now
|
||
*
|
||
* The software is freeware and is distributed "as is" with the complete source codes.
|
||
* Anybody is free to use it in any software projects, either commercial or non-commercial.
|
||
* Please do not remove this copyright message and remain the name of the author unchanged.
|
||
*
|
||
* It is very appreciated if you produce some feedback to us case you are going to use
|
||
* the software.
|
||
*
|
||
* Please send your questions, suggestions, and information about found issues to the
|
||
*
|
||
* sintegrial@gmail.com
|
||
*
|
||
*/
|
||
|
||
|
||
#include "qroundProgressBar.h"
|
||
|
||
#include <QtGui/QPainter>
|
||
#include <QtGui/QPainterPath>
|
||
#include <QDebug>
|
||
#include <QStyleOption>
|
||
//#include <QGSettings>
|
||
|
||
|
||
QRoundProgressBar::QRoundProgressBar(QWidget *parent) :
|
||
QWidget(parent),
|
||
m_min(0), m_max(100),
|
||
m_value(25),
|
||
m_nullPosition(PositionTop),
|
||
m_barStyle(StyleDonut),
|
||
m_outlinePenWidth(0.96),
|
||
m_dataPenWidth(1),
|
||
m_rebuildBrush(false),
|
||
m_format("%p%"),
|
||
m_decimals(1),
|
||
m_updateFlags(UF_PERCENT)
|
||
{
|
||
settingsStyle();
|
||
}
|
||
|
||
void QRoundProgressBar::setRange(double min, double max)
|
||
{
|
||
m_min = min;
|
||
m_max = max;
|
||
|
||
if (m_max < m_min)
|
||
qSwap(m_max, m_min);
|
||
|
||
if (m_value < m_min)
|
||
m_value = m_min;
|
||
else if (m_value > m_max)
|
||
m_value = m_max;
|
||
|
||
if (!m_gradientData.isEmpty())
|
||
m_rebuildBrush = true;
|
||
|
||
update();
|
||
}
|
||
|
||
void QRoundProgressBar::setMinimum(double min)
|
||
{
|
||
setRange(min, m_max);
|
||
}
|
||
|
||
void QRoundProgressBar::setMaximum(double max)
|
||
{
|
||
setRange(m_min, max);
|
||
}
|
||
|
||
void QRoundProgressBar::setValue(double val)
|
||
{
|
||
if (m_value != val) {
|
||
if (val < m_min)
|
||
m_value = m_min;
|
||
else if (val > m_max)
|
||
m_value = m_max;
|
||
else
|
||
m_value = val;
|
||
update();
|
||
}
|
||
}
|
||
|
||
//定义最小值位置
|
||
void QRoundProgressBar::setNullPosition(double position)
|
||
{
|
||
if (position != m_nullPosition) {
|
||
m_nullPosition = position;
|
||
if (!m_gradientData.isEmpty())
|
||
m_rebuildBrush = true;
|
||
update();
|
||
}
|
||
}
|
||
//设置视觉样式圆环(甜甜圈)风格、饼(派)风格、线型
|
||
void QRoundProgressBar::setBarStyle(QRoundProgressBar::BarStyle style)
|
||
{
|
||
if (style != m_barStyle) {
|
||
m_barStyle = style;
|
||
update();
|
||
}
|
||
}
|
||
//设置轮廓圆笔的宽度。
|
||
void QRoundProgressBar::setOutlinePenWidth(double penWidth)
|
||
{
|
||
if (penWidth != m_outlinePenWidth) {
|
||
m_outlinePenWidth = penWidth;
|
||
update();
|
||
}
|
||
}
|
||
//设置数据圆笔的宽度。
|
||
void QRoundProgressBar::setDataPenWidth(double penWidth)
|
||
{
|
||
if (penWidth != m_dataPenWidth) {
|
||
m_dataPenWidth = penWidth;
|
||
update();
|
||
}
|
||
}
|
||
//设置可见数据的颜色,并使用它们制作渐变画笔。
|
||
//此函数将覆盖widget的`palette()`来设置动态创建的渐变笔刷
|
||
void QRoundProgressBar::setDataColors(const QGradientStops &stopPoints)
|
||
{
|
||
if (stopPoints != m_gradientData) {
|
||
m_gradientData = stopPoints;
|
||
m_rebuildBrush = true;
|
||
update();
|
||
}
|
||
}
|
||
//定义用于生成当前文本的字符串。
|
||
//%p-用完成的百分比代替。 %v-由当前值替换。 %m-由最大值代替。
|
||
//默认值为“%p%”
|
||
void QRoundProgressBar::setFormat(const QString &format)
|
||
{
|
||
if (format != m_format) {
|
||
m_format = format;
|
||
|
||
valueFormatChanged();
|
||
}
|
||
}
|
||
//将格式字符串设置为空字符串。 因此,将不会显示任何文本。
|
||
void QRoundProgressBar::resetFormat()
|
||
{
|
||
m_format = QString::null;
|
||
valueFormatChanged();
|
||
}
|
||
//设置要在逗号后显示的小数位数(默认为1)。
|
||
void QRoundProgressBar::setDecimals(int count)
|
||
{
|
||
if (count >= 0 && count != m_decimals) {
|
||
m_decimals = count;
|
||
valueFormatChanged();
|
||
}
|
||
}
|
||
|
||
void QRoundProgressBar::paintEvent(QPaintEvent* /*event*/)
|
||
{
|
||
//构造和自定义(例如,设置笔或画笔)painter。 然后画。 记住在绘制后销毁QPainter对象。
|
||
QPainter painter(this);
|
||
painter.save();
|
||
//mainColor 在settingsStyle配置
|
||
painter.setPen(mainColor);
|
||
painter.setBrush(mainColor);
|
||
//在指定的boundingRectangle中创建一个椭圆,并将其作为封闭的子路径添加到painter路径。
|
||
QPainterPath bigCircle;
|
||
bigCircle.addEllipse(65, 13, 266, 266);
|
||
QPainterPath path = bigCircle ;
|
||
painter.drawPath(path);
|
||
//保存当前的画家状态(将状态推送到堆栈上)。 在save()之后必须有一个相应的restore(); end()函数展开堆栈。
|
||
painter.restore();
|
||
|
||
double outerRadius = 133;
|
||
QRectF baseRect(198 - outerRadius, 146 - outerRadius, outerRadius * 2, outerRadius * 2);
|
||
QPainter p(this);
|
||
//表示引擎应尽可能对图元的边缘进行抗锯齿。
|
||
p.setRenderHint(QPainter::Antialiasing);
|
||
// data brush
|
||
rebuildDataBrushIfNeeded();
|
||
// background
|
||
drawBackground(p, rect());
|
||
double innerRadius = 133;
|
||
QRectF innerRect = QRectF((198 - outerRadius) + 5, (146 - outerRadius) + 5, (outerRadius - 5) * 2 , (outerRadius - 5) * 2 );
|
||
|
||
//calculateInnerRect(baseRect, outerRadius, innerRect, innerRadius);
|
||
double arcStep = 360.0 / (m_max - m_min) * m_value;
|
||
// base circle
|
||
drawBase(p, baseRect,innerRect);
|
||
// data circle
|
||
drawValue(p, baseRect, m_value, arcStep,innerRect, innerRadius);
|
||
// finally draw the bar
|
||
p.end();
|
||
}
|
||
|
||
//背景色
|
||
void QRoundProgressBar::drawBackground(QPainter &p, const QRectF &baseRect)
|
||
{
|
||
p.fillRect(baseRect, palette().background());
|
||
}
|
||
|
||
//画基础
|
||
void QRoundProgressBar::drawBase(QPainter &p, const QRectF &baseRect,const QRectF &innerRect)
|
||
{
|
||
|
||
QStyleOption opt;
|
||
opt.init(this);
|
||
switch (m_barStyle)
|
||
{
|
||
case StyleDonut:
|
||
// p.setPen(QPen(QColor(160, 160, 160), 4, Qt::DotLine, Qt::RoundCap, Qt::RoundJoin));
|
||
// p.setBrush(Qt::NoBrush);
|
||
// p.drawEllipse(QPointF(227, 180),155,155);
|
||
break;
|
||
case StylePie:
|
||
p.setPen(QPen(palette().base().color(), m_outlinePenWidth, Qt::DotLine, Qt::RoundCap, Qt::RoundJoin));
|
||
p.setBrush(palette().base());
|
||
p.drawEllipse(baseRect);
|
||
break;
|
||
case StyleLine:
|
||
p.setPen(QPen(palette().base().color(), m_outlinePenWidth ,Qt::DotLine, Qt::RoundCap, Qt::RoundJoin));
|
||
p.setBrush(Qt::NoBrush);
|
||
p.drawEllipse(baseRect.adjusted(m_outlinePenWidth/2, m_outlinePenWidth/2, -m_outlinePenWidth/2, -m_outlinePenWidth/2));
|
||
break;
|
||
default:;
|
||
}
|
||
}
|
||
//画数据
|
||
void QRoundProgressBar::drawValue(QPainter &p
|
||
, const QRectF &baseRect
|
||
, double value
|
||
, double arcLength
|
||
, const QRectF & innerRect
|
||
, double innerRadius)
|
||
{
|
||
// nothing to draw
|
||
if (value == m_min)
|
||
return;
|
||
|
||
// for Pie and Donut styles
|
||
QPainterPath dataPath;
|
||
dataPath.setFillRule(Qt::WindingFill);
|
||
dataPath.moveTo(baseRect.center());
|
||
dataPath.arcTo(baseRect, m_nullPosition, -arcLength);//大家都是先绘制外圆的弧长
|
||
|
||
if(m_barStyle == StyleDonut) {
|
||
// draw dount outer
|
||
QPointF currentPoint = dataPath.currentPosition();//绘制完外圆弧长后,获取绘制完的位置绘制一个直线到达内圆
|
||
currentPoint = baseRect.center() + (currentPoint - baseRect.center()) * m_innerOuterRate;//计算内圆的坐标点,m_innerOuterRate替代了原作者写的0.75,代表内圆是外圆的0.75倍
|
||
dataPath.lineTo(currentPoint);//绘制外圆到内圆的直线
|
||
dataPath.moveTo(baseRect.center());//坐标点回到中心准备绘制内圆弧形
|
||
dataPath.arcTo(innerRect, m_nullPosition-arcLength, arcLength);//绘制内圆的弧形
|
||
currentPoint = dataPath.currentPosition();//准备绘制内圆到外圆的直线,形成封闭区域
|
||
currentPoint = baseRect.center() + ((currentPoint - baseRect.center()) * (2-m_innerOuterRate));//绘制内圆到外圆的直线,这里2-m_innerOuterRate其实是对应(1 + (1 -m_innerOuterRate))的
|
||
dataPath.lineTo(currentPoint);
|
||
p.setPen(Qt::NoPen);//这个很重要不然就会有绘制过程的一些轮廓了
|
||
}
|
||
|
||
p.setBrush(palette().highlight());
|
||
p.setBrush(ringRunColor);
|
||
|
||
p.drawPath(dataPath);
|
||
}
|
||
|
||
void QRoundProgressBar::calculateInnerRect(const QRectF &/*baseRect*/, double outerRadius, QRectF &innerRect, double &innerRadius)
|
||
{
|
||
// for Line style
|
||
if (m_barStyle == StyleLine) {
|
||
innerRadius = outerRadius - m_outlinePenWidth;
|
||
} else {
|
||
innerRadius = outerRadius * m_outlinePenWidth;
|
||
}
|
||
|
||
double delta = (outerRadius - innerRadius) / 2;
|
||
innerRect = QRectF(delta, delta, innerRadius, innerRadius);
|
||
// innerRect = QRectF(227 - outerRadius, 220 - outerRadius, innerRadius * 2, innerRadius * 2);
|
||
}
|
||
|
||
void QRoundProgressBar::drawInnerBackground(QPainter &p, const QRectF &innerRect)
|
||
{
|
||
if (m_barStyle == StyleDonut) {
|
||
p.setBrush(palette().alternateBase());
|
||
p.drawEllipse(innerRect);
|
||
}
|
||
}
|
||
|
||
void QRoundProgressBar::drawText(QPainter &p, const QRectF &innerRect, double innerRadius, double value)
|
||
{
|
||
if (m_format.isEmpty())
|
||
return;
|
||
|
||
// !!! to revise
|
||
QFont f(font());
|
||
f.setPixelSize(innerRadius * qMax(0.05, (0.35 - (double)m_decimals * 0.08)));
|
||
p.setFont(f);
|
||
|
||
QRectF textRect(innerRect);
|
||
p.setPen(palette().text().color());
|
||
p.drawText(textRect, Qt::AlignCenter, valueToText(value));
|
||
}
|
||
|
||
QString QRoundProgressBar::valueToText(double value) const
|
||
{
|
||
QString textToDraw(m_format);
|
||
|
||
if (m_updateFlags & UF_VALUE)
|
||
textToDraw.replace("%v", QString::number(value, 'f', m_decimals));
|
||
|
||
if (m_updateFlags & UF_PERCENT) {
|
||
double procent = (value - m_min) / (m_max - m_min) * 100.0;
|
||
textToDraw.replace("%p", QString::number(procent, 'f', m_decimals));
|
||
}
|
||
|
||
if (m_updateFlags & UF_MAX)
|
||
textToDraw.replace("%m", QString::number(m_max - m_min + 1, 'f', m_decimals));
|
||
return textToDraw;
|
||
}
|
||
|
||
void QRoundProgressBar::valueFormatChanged()
|
||
{
|
||
m_updateFlags = 0;
|
||
|
||
if (m_format.contains("%v"))
|
||
m_updateFlags |= UF_VALUE;
|
||
if (m_format.contains("%p"))
|
||
m_updateFlags |= UF_PERCENT;
|
||
if (m_format.contains("%m"))
|
||
m_updateFlags |= UF_MAX;
|
||
update();
|
||
}
|
||
|
||
void QRoundProgressBar::rebuildDataBrushIfNeeded()
|
||
{
|
||
if (m_rebuildBrush) {
|
||
m_rebuildBrush = false;
|
||
QConicalGradient dataBrush;
|
||
dataBrush.setCenter(0.5,0.5);
|
||
dataBrush.setCoordinateMode(QGradient::StretchToDeviceMode);
|
||
// invert colors
|
||
for (int i = 0; i < m_gradientData.count(); i++) {
|
||
dataBrush.setColorAt(1.0 - m_gradientData.at(i).first, m_gradientData.at(i).second);
|
||
}
|
||
// angle
|
||
dataBrush.setAngle(m_nullPosition);
|
||
QPalette p(palette());
|
||
p.setBrush(QPalette::Window,Qt::NoBrush);
|
||
p.setBrush(QPalette::AlternateBase,Qt::NoBrush);
|
||
p.setBrush(QPalette::Highlight, dataBrush);
|
||
setPalette(p);
|
||
}
|
||
}
|
||
|
||
QColor QRoundProgressBar::getRingRunColor() const
|
||
{
|
||
return ringRunColor;
|
||
}
|
||
|
||
void QRoundProgressBar::setRingRunColor(const QColor &value)
|
||
{
|
||
ringRunColor = value;
|
||
}
|
||
|
||
void QRoundProgressBar::switchRunRingColor()
|
||
{
|
||
ringRunColor = QColor(55, 144, 250,255);
|
||
update();
|
||
}
|
||
|
||
void QRoundProgressBar::switchStopRingColor()
|
||
{
|
||
ringRunColor = QColor(69, 173, 110,255);
|
||
update();
|
||
}
|
||
|
||
/*
|
||
*监听主题
|
||
*/
|
||
void QRoundProgressBar::settingsStyle()
|
||
{
|
||
|
||
GsettingSubject * subject = GsettingSubject::getInstance();;
|
||
connect(subject,&GsettingSubject::blackStyle, this,[=](){
|
||
mainColor = QColor(255, 255, 255, 40);
|
||
});
|
||
connect(subject,&GsettingSubject::whiteStyle, this,[=](){
|
||
mainColor = QColor(255, 255, 255, 107);
|
||
});
|
||
subject->iniWidgetStyle();
|
||
|
||
}
|
||
|
||
|