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. web IDE Eclipse Che安装

    安装:使用安装器安装 使用安装器安装Che 环境 依赖 下载地址 通用 任何操作系统,java8,Git,Maven 3.0.5+,Docker 1.7+ 3.12.52 - 117MB window ...

  2. ActiveReports 报告应用程序教程 (2)---上市类报告

     大多数数字在报告中系统类报告列表.实例:客户名单.产品列表.设备清单.采购清单.优惠券.商品发票.工作人员清点等..根据报告,查看类别列表相对简单.但,分丰富,在清单类报表中能够增加数据统计.数 ...

  3. C# 使用外部别名

    原文:C# 使用外部别名 版权声明:博客已迁移到 http://lindexi.gitee.io 欢迎访问.如果当前博客图片看不到,请到 http://lindexi.gitee.io 访问博客.本文 ...

  4. intraweb 11.0.63 for delphi7 破解

    资源地址:http://download.csdn.net/detail/marszzx/9472912 本资源来自互联网,整理后上传,本资源仅供学习使用,请勿作用商业用途 delphi开发网站,似乎 ...

  5. 《网络编程》ioctl 操作

    概要 ioctl 功能与 fcntl 功能类似,它可以被用于描述操作的叙述字符,获取或设置属性的描述是开放式的叙事休息,但在网络编程的两个功能有关的不同类型的操作.fcntl 作.文件操作,而 ioc ...

  6. 实用WordPress后台MySQL操作命令

    关键字: WordPress MySQL 后台 数据库 地址:http://www.cnblogs.com/txw1958/archive/2013/01/06/wordpress-sql.html ...

  7. 局部QEventLoop帮助QWidget不消失(也就是有一个局部事件循环始终在运行,导致程序被卡住那里,但仍可以接受事件。说白了就是有一个while语句死活不肯退出,直到收到退出信号)

    熟悉的陌生人 Qt 是事件驱动的,所以当你用Qt的时候,几乎时时刻刻和 QEventLoop 打交道.,只是你可能没有意识到: QCoreApplicaton::exec() QApplication ...

  8. nginx 502错 failed (13: Permission denied)

    安装nginx和php-fpm之后出现502错误 找了个理由说php-fpm不启动 ,但在我的实践中,该过程开始 找了半天没找到病因.视图nginx记录后 我发现下面的错误 [crit] 2686#0 ...

  9. (转)总结:JavaScript异步、事件循环与消息队列、微任务与宏任务

    前言 Philip Roberts 在演讲 great talk at JSConf on the event loop 中说:要是用一句话来形容 JavaScript,我可能会这样: “JavaSc ...

  10. Fidder模拟发送请求

    在Fiddler的Composer一栏,可以模拟请求 举例 首先通过浏览器访问页面http://baidu.com/ ,在右侧可以拿到请求情况 在Inspectors一栏可以看到请求和响应结果,复制请 ...