Qt第三方圆形进度条的改进

要实现一个圆形的进度条功能,在网上找到一个比较出名的第三方封装类:QRoundProgressBar,地址:sourceforge 的 QRoundProgressBar 
功能封装的还是不错,提供了3种模式,线形、圆环、饼状。使用过程中发现圆环进度条对背景透明支持不够完善,内圆的背景无法实现透明,为了解决此问题,下面对此控件进行了一些修订,实现完整的圆形进度条。

QRoundProgressBar目前存在的不足

QRoundProgressBar在带背景图片widget下使用StyleDonut样式时,内环背景无法透明

代码如下: 
头文件:

class DRoundProgressBar;
class QTimer;
namespace Ui {
class Widget;
} class Widget : public QWidget
{
Q_OBJECT public:
explicit Widget(QWidget *parent = 0);
~Widget();
public slots:
void onTimeOut();
private:
Ui::Widget *ui;
QTimer* mTimer;
DRoundProgressBar* mRoundBar;
int mPresent;
};

cpp:

#include "Widget.h"
#include "ui_Widget.h"
#include "DRoundProgressBar.h"
#include <QTimer>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
setAutoFillBackground(true);
QPixmap img(":/img/BlueDialog_BK.png");
QPalette bkPalette;
bkPalette.setBrush(QPalette::Window,QBrush(img));
setPalette(bkPalette);
mRoundBar = new DRoundProgressBar(this);
mRoundBar->setGeometry(150,100,500,500);
mRoundBar->setBarStyle(DRoundProgressBar::StyleDonut);
mRoundBar->setRange(0,100);
QPalette palette;
palette.setBrush(QPalette::Window,Qt::NoBrush);
palette.setBrush(QPalette::AlternateBase,Qt::NoBrush);
palette.setBrush(QPalette::Highlight,QBrush(QColor(0,140,255)));
palette.setColor(QPalette::Text,QColor(0,0,0));
//palette.setBrush(QPalette::Base,Qt::white);
mRoundBar->setPalette(palette);
mTimer = new QTimer(this);
mTimer->setInterval(200);
connect(mTimer,SIGNAL(timeout()),this,SLOT(onTimeOut()));
mPresent = 0;
mTimer->start();
} Widget::~Widget()
{
delete ui;
} void Widget::onTimeOut()
{
++mPresent;
if(mPresent >= 100)
{
mPresent = 0;
}
mRoundBar->setValue(mPresent);
}

这里把QPalette::Window和QPalette::AlternateBase设置为透明,发现无法绘制圆环

原因

查看代码,看看其绘制圆环的步骤

void QRoundProgressBar::drawValue(QPainter &p, const QRectF &baseRect, double value, double arcLength)
{
// nothing to draw
if (value == m_min)
return; // for Line style
if (m_barStyle == StyleLine)
{
p.setPen(QPen(palette().highlight().color(), m_dataPenWidth));
p.setBrush(Qt::NoBrush);
p.drawArc(baseRect.adjusted(m_outlinePenWidth/2, m_outlinePenWidth/2, -m_outlinePenWidth/2, -m_outlinePenWidth/2),
m_nullPosition * 16,
-arcLength * 16);
return;
} // for Pie and Donut styles QPainterPath dataPath;
dataPath.setFillRule(Qt::WindingFill); // pie segment outer
dataPath.moveTo(baseRect.center());
dataPath.arcTo(baseRect, m_nullPosition, -arcLength);
dataPath.lineTo(baseRect.center()); p.setBrush(palette().highlight());
p.setPen(QPen(palette().shadow().color(), m_dataPenWidth));
p.drawPath(dataPath);
}

发现绘制圆环和绘制pie是一个代码,而实现圆环只不过是用一个背景覆盖上去了,这样,如果让中间区域透明就会显示画的那个扇形的那一部分(原来作者是得多懒-_-#)。 
因此。在绘制圆环时需要特殊对待,QPainterPath在画圆环时把圆环填充。

改进

这里需要把原来的drawValue函数进行修正,原来drawValue函数对绘制pie和绘制Donut styles是一样处理,这里修正为pie就画pie,画Donut styles就画圆环: 
为了绘制圆环,drawValue函数需添加两个变量,是内环的对应的矩形和半径

virtual void drawValue(QPainter& p, const QRectF& baseRect, double value, double arcLength, const QRectF & innerRect, double innerRadius);

改动后的cpp

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 Line style
if (m_barStyle == StyleLine)
{
p.setPen(QPen(palette().highlight().color(), m_dataPenWidth));
p.setBrush(Qt::NoBrush);
p.drawArc(baseRect.adjusted(m_outlinePenWidth/2, m_outlinePenWidth/2, -m_outlinePenWidth/2, -m_outlinePenWidth/2),
m_nullPosition * 16,
-arcLength * 16);
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 == StylePie)
{ // pie segment outer
dataPath.lineTo(baseRect.center()); p.setPen(QPen(palette().shadow().color(), m_dataPenWidth));
}
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.drawPath(dataPath);
}

具体过程见代码的注释。

这里作者把内圆直径定死为外圆的0.75倍,我觉得这样失去了灵活性,因此加入了一个float变量,m_innerOuterRate,默认为0.75,替代原来的0.75常数,并加入方法float innerOuterRate() constvoid setInnerOuterRate(float r)进行设置

原来的paintEvent函数的函数顺序也需要改变:

下面是原来的paintEvent函数:

void QRoundProgressBar::paintEvent(QPaintEvent* /*event*/)
{
double outerRadius = qMin(width(), height());
QRectF baseRect(1, 1, outerRadius-2, outerRadius-2); QImage buffer(outerRadius, outerRadius, QImage::Format_ARGB32_Premultiplied); QPainter p(&buffer);
p.setRenderHint(QPainter::Antialiasing); // data brush
rebuildDataBrushIfNeeded(); // background
drawBackground(p, buffer.rect()); // base circle
drawBase(p, baseRect); // data circle
double arcStep = 360.0 / (m_max - m_min) * m_value;
drawValue(p, baseRect, m_value, arcStep); // center circle
double innerRadius(0);
QRectF innerRect;
calculateInnerRect(baseRect, outerRadius, innerRect, innerRadius);
drawInnerBackground(p, innerRect); // text
drawText(p, innerRect, innerRadius, m_value); // finally draw the bar
p.end(); QPainter painter(this);
painter.fillRect(baseRect, palette().background());
painter.drawImage(0,0, buffer);
}

原来作者使用了QImage作为缓存,但qt自带双缓冲,这一步没有必要(用QImage时在ubuntu还有小问题,在控件比较小时(200*200以下)会出现花屏现象,原因未知),修改为:


void QRoundProgressBar::paintEvent(QPaintEvent* /*event*/)
{
double outerRadius = qMin(width(), height());
QRectF baseRect(1, 1, outerRadius-2, outerRadius-2); QPainter p(this);
p.setRenderHint(QPainter::Antialiasing); // data brush
rebuildDataBrushIfNeeded(); // background
drawBackground(p, rect());
double innerRadius(0);
QRectF innerRect;
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); // center circle drawInnerBackground(p, innerRect); // text
drawText(p, innerRect, innerRadius, m_value); // finally draw the bar
p.end();
}

主要是把calculateInnerRect(baseRect, outerRadius, innerRect, innerRadius);函数提前计算出内圆对应的参数。并传入给新修改的drawValue函数。把多余的双缓冲机制去掉.

此时效果还未达到需求的效果,发现drawBase函数还需要修改,原来的drawBase函数如下:

void QRoundProgressBar::drawBase(QPainter &p, const QRectF &baseRect)
{
switch (m_barStyle)
{
case StyleDonut:
p.setPen(QPen(palette().shadow().color(), m_outlinePenWidth));
p.setBrush(palette().base());
p.drawEllipse(baseRect);
break; case StylePie:
p.setPen(QPen(palette().base().color(), m_outlinePenWidth));
p.setBrush(palette().base());
p.drawEllipse(baseRect);
break; case StyleLine:
p.setPen(QPen(palette().base().color(), m_outlinePenWidth));
p.setBrush(Qt::NoBrush);
p.drawEllipse(baseRect.adjusted(m_outlinePenWidth/2, m_outlinePenWidth/2, -m_outlinePenWidth/2, -m_outlinePenWidth/2));
break; default:;
}
}

上面的drawBase函数可见,由于原作者比较懒,对于Donut styles模式就直接画了一个外圆,并不是一个空心圆环,改进如下:

void QRoundProgressBar::drawBase(QPainter &p, const QRectF &baseRect,const QRectF &innerRect)
{
switch (m_barStyle)
{
case StyleDonut:
{
QPainterPath dataPath;
dataPath.setFillRule(Qt::OddEvenFill);
dataPath.moveTo(baseRect.center());
dataPath.addEllipse(baseRect);
dataPath.addEllipse(innerRect);
p.setPen(QPen(palette().shadow().color(), m_outlinePenWidth));
p.setBrush(palette().base());
p.drawPath(dataPath);
break;
}
case StylePie:
p.setPen(QPen(palette().base().color(), m_outlinePenWidth));
p.setBrush(palette().base());
p.drawEllipse(baseRect);
break; case StyleLine:
p.setPen(QPen(palette().base().color(), m_outlinePenWidth));
p.setBrush(Qt::NoBrush);
p.drawEllipse(baseRect.adjusted(m_outlinePenWidth/2, m_outlinePenWidth/2, -m_outlinePenWidth/2, -m_outlinePenWidth/2));
break;
default:;
}
}

最后运行效果:

代码见:czyt1988的github

http://blog.csdn.net/czyt1988/article/details/53422274

Qt第三方圆形进度条-及其改进的更多相关文章

  1. Qt自定义控件系列(一) --- 圆形进度条

    本系列主要使用Qt painter来实现一些基础控件.主要是对平时自行编写的一些自定义控件的总结. 为了简洁.低耦合,我们尽量不使用图片,qrc,ui等文件,而只使用c++的.h和.cpp文件. 由于 ...

  2. Qt之QRoundProgressBar(圆形进度条)

    简述 QRoundProgressBar类能够实现一个圆形进度条,继承自QWidget,并且有和QProgressBar类似的API接口. 简述 详细说明 风格 颜色 字体 共有函数 共有槽函数 详细 ...

  3. android 自定义控件——(四)圆形进度条

    ----------------------------------↓↓圆形进度条(源代码下有属性解释)↓↓---------------------------------------------- ...

  4. WPF 实现圆形进度条

    项目中用到圆形进度条,首先就想到使用 ProgressBar 扩展一个,在园子里找到迷途的小榔头给出的思路和部分代码,自己加以实现. 进度小于60显示红色,大于60则显示绿色.效果如下: 基本思路: ...

  5. html5 svg 圆形进度条

    html5 svg 圆形进度条 <!DOCTYPE html> <html lang="en"> <head> <meta charset ...

  6. canvas圆形进度条

    通过定义一个canvas标签, new方法传进ID值,和旋转角度值,即可生成圆形进度条 <!DOCTYPE html> <html lang="en"> & ...

  7. iOS之UI--Quartz2D的入门应用--重绘下载圆形进度条

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  8. Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)

      Android 高手进阶(21)  版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请注明地址:http://blog.csdn.net/xiaanming/article/detail ...

  9. 简单实用的纯CSS百分比圆形进度条插件

    percircle是一款简单实用的纯CSS百分比圆形进度条插件.你不需要做任何设置,只需要按该圆形进度条插件提供的标准HTML结构来编写代码,就可以生成一个漂亮的百分比圆形进度条. 首先要做的就是引入 ...

随机推荐

  1. hbase 2.0.2 增删改查

    package cn.hbase.demo; import java.io.IOException; import java.util.Iterator; import org.apache.hado ...

  2. hadoop 3.x 完全分布式集群搭建/异常处理/测试

    共计三台虚拟机分别为hadoop002(master,存放namenode),hadoop003(workers,datanode以及resourcemanage),hadoop004(workers ...

  3. matlab 求解线性规划问题

    线性规划 LP(Linear programming,线性规划)是一种优化方法,在优化问题中目标函数和约束函数均为向量变量的线性函数,LP问题可描述为: minf(x):待最小化的目标函数(如果问题本 ...

  4. 重装huson遇到的一些错误及解决

    作者:朱金灿 来源:http://blog.csdn.net/clever101 服务器换了一块新硬盘,拷贝回原来的数据后结果发现Apache tomcat服务不能启动了,错误提示是:本地计算机上的A ...

  5. 数据集成工具Teiid Designer的环境搭建

    由于实验室项目要求的关系,看了些数据汇聚工具 Teiid 的相关知识.这里总结下 Teiid 的可视化配置工具 Teiid Designer 的部署过程. 背景知识 数据集成是把不同来源.格式.特点性 ...

  6. ubuntu grub 操作

    系统开机时,按住 shift 进入 grub 1. 什么是 Grub GNU GRUB(GRand Unified Bootloader 简称"GRUB")是一个来自GNU项目的多 ...

  7. .net 程序员 两年工作总结

    2013 年7月毕业,算一算从开始在现任的公司实习到现在已经有小两年的时间了.公司的工作虽然不忙,但也一直没有时间思考一下. 现在决定辞职了,忽然一下轻松的让人想思考. 普通程序员.普通本科生.普通的 ...

  8. OpenGL中GLSL渲染茶壶光照完整程序

    顶点着色器VertexShader.txt: uniform vec3 lightposition;//光源位置 uniform vec3 eyeposition;//相机位置 uniform vec ...

  9. [UWP开发]NavigationView基础使用方法

    原文:[UWP开发]NavigationView基础使用方法 [UWP开发]NavigationView基础使用方法 NavigationView是秋季创意者更新(16299)引入的新控件,用于生成W ...

  10. c#调api串口通讯

    原文:c#调api串口通讯 在调试ICU通信设备的时候,由于串口通信老出现故障,所以就怀疑CF实现的SerialPort类是否有问题,所以最后决定用纯API函数实现串口读写. 先从网上搜索相关代码(关 ...