Qt第三方圆形进度条-及其改进
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() const
和void 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第三方圆形进度条-及其改进的更多相关文章
- Qt自定义控件系列(一) --- 圆形进度条
本系列主要使用Qt painter来实现一些基础控件.主要是对平时自行编写的一些自定义控件的总结. 为了简洁.低耦合,我们尽量不使用图片,qrc,ui等文件,而只使用c++的.h和.cpp文件. 由于 ...
- Qt之QRoundProgressBar(圆形进度条)
简述 QRoundProgressBar类能够实现一个圆形进度条,继承自QWidget,并且有和QProgressBar类似的API接口. 简述 详细说明 风格 颜色 字体 共有函数 共有槽函数 详细 ...
- android 自定义控件——(四)圆形进度条
----------------------------------↓↓圆形进度条(源代码下有属性解释)↓↓---------------------------------------------- ...
- WPF 实现圆形进度条
项目中用到圆形进度条,首先就想到使用 ProgressBar 扩展一个,在园子里找到迷途的小榔头给出的思路和部分代码,自己加以实现. 进度小于60显示红色,大于60则显示绿色.效果如下: 基本思路: ...
- html5 svg 圆形进度条
html5 svg 圆形进度条 <!DOCTYPE html> <html lang="en"> <head> <meta charset ...
- canvas圆形进度条
通过定义一个canvas标签, new方法传进ID值,和旋转角度值,即可生成圆形进度条 <!DOCTYPE html> <html lang="en"> & ...
- iOS之UI--Quartz2D的入门应用--重绘下载圆形进度条
*:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
Android 高手进阶(21) 版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请注明地址:http://blog.csdn.net/xiaanming/article/detail ...
- 简单实用的纯CSS百分比圆形进度条插件
percircle是一款简单实用的纯CSS百分比圆形进度条插件.你不需要做任何设置,只需要按该圆形进度条插件提供的标准HTML结构来编写代码,就可以生成一个漂亮的百分比圆形进度条. 首先要做的就是引入 ...
随机推荐
- web IDE Eclipse Che安装
安装:使用安装器安装 使用安装器安装Che 环境 依赖 下载地址 通用 任何操作系统,java8,Git,Maven 3.0.5+,Docker 1.7+ 3.12.52 - 117MB window ...
- ActiveReports 报告应用程序教程 (2)---上市类报告
大多数数字在报告中系统类报告列表.实例:客户名单.产品列表.设备清单.采购清单.优惠券.商品发票.工作人员清点等..根据报告,查看类别列表相对简单.但,分丰富,在清单类报表中能够增加数据统计.数 ...
- C# 使用外部别名
原文:C# 使用外部别名 版权声明:博客已迁移到 http://lindexi.gitee.io 欢迎访问.如果当前博客图片看不到,请到 http://lindexi.gitee.io 访问博客.本文 ...
- intraweb 11.0.63 for delphi7 破解
资源地址:http://download.csdn.net/detail/marszzx/9472912 本资源来自互联网,整理后上传,本资源仅供学习使用,请勿作用商业用途 delphi开发网站,似乎 ...
- 《网络编程》ioctl 操作
概要 ioctl 功能与 fcntl 功能类似,它可以被用于描述操作的叙述字符,获取或设置属性的描述是开放式的叙事休息,但在网络编程的两个功能有关的不同类型的操作.fcntl 作.文件操作,而 ioc ...
- 实用WordPress后台MySQL操作命令
关键字: WordPress MySQL 后台 数据库 地址:http://www.cnblogs.com/txw1958/archive/2013/01/06/wordpress-sql.html ...
- 局部QEventLoop帮助QWidget不消失(也就是有一个局部事件循环始终在运行,导致程序被卡住那里,但仍可以接受事件。说白了就是有一个while语句死活不肯退出,直到收到退出信号)
熟悉的陌生人 Qt 是事件驱动的,所以当你用Qt的时候,几乎时时刻刻和 QEventLoop 打交道.,只是你可能没有意识到: QCoreApplicaton::exec() QApplication ...
- nginx 502错 failed (13: Permission denied)
安装nginx和php-fpm之后出现502错误 找了个理由说php-fpm不启动 ,但在我的实践中,该过程开始 找了半天没找到病因.视图nginx记录后 我发现下面的错误 [crit] 2686#0 ...
- (转)总结:JavaScript异步、事件循环与消息队列、微任务与宏任务
前言 Philip Roberts 在演讲 great talk at JSConf on the event loop 中说:要是用一句话来形容 JavaScript,我可能会这样: “JavaSc ...
- Fidder模拟发送请求
在Fiddler的Composer一栏,可以模拟请求 举例 首先通过浏览器访问页面http://baidu.com/ ,在右侧可以拿到请求情况 在Inspectors一栏可以看到请求和响应结果,复制请 ...