迅雷下载进度悬浮球

一、效果图
在这里插入图片描述
二、效果图拆解
根据效果图上显示,进度悬浮球外观是一个圆,内部是两条水波纹荡漾实现,最中间的文字是三个标签组成。
三、技术难点
根据效果图显示,小球中间的水波纹实现是本文的难点,其中有两个难点,分别是:①水波纹如何实现?②如何绘制这样的下面是圆的一部分,上面是水波纹?
四、难点实现讲解
①效果图上的水波纹实际上两条正弦/余弦函数周期波动,一条颜色透明度比较高,放在后面,一条颜色透明度比较低,放在前面,采用定时器,周期性的移动这两条波动函数,实现水波纹效果。
②如何绘制下面是圆形,上面是水波纹的异形图案?Qt的QPainterPath类提供了一个函数计算两个闭合图案的交集的路径所有点,再使用QPainter绘制路径即可。效果图上的实现图案路径分成两个,一个是圆形图案路径,一个是由左下角到水波高度的直线+波动图像路径+水波纹高度到右下角的直线构成的闭合图像。两个图案求交集后就是效果图上的下面是圆的一部分,上面是正弦/余弦函数的一部分图案。
五、绘制过程拆解
①先绘制一个圆圈,颜色指定为灰白色,指定一个透明度即可。并将圆圈的路径记录在QPainterPath中。
②计算正弦/余弦函数所在整个控件上的坐标路径集合,放在QPainterPath中。
③计算上一步中的路径集合与第一步中的路径集合的交集区域。
④先绘制一个颜色透明度较高的异形窗口放在后面,再绘制一个颜色透明度相对较低的异形窗口放前面。
⑤设置定时器,定时周期性的移动上面计算的正弦/余弦函数,达到水波纹效果。
六、准备工作
①初始化窗口类,类继承自QDialog,设置窗口大小,去掉标题栏,任务栏,窗口置顶等,代码如下所示:

	m_value=80;		//当前进度值
    m_offset=0;	·	//当前偏移量
	setFixedSize(70,70);
	//去掉标题栏 设置置顶 去掉任务栏图标
	setWindowFlags(Qt::FramelessWindowHint|Qt::Tool|Qt::WindowCloseButtonHint|Qt::WindowStaysOnTopHint);

②设置控件再主屏幕上的显示位置,将窗口移动到主屏幕右下角位置,代码如下:

	//获取主屏尺寸 设置位置
	QScreen *primaryScreen=QGuiApplication::primaryScreen();
	if(primaryScreen==nullptr)
	    return;
	geometry=primaryScreen->availableGeometry();
	this->move(geometry.width()-100,geometry.height()*0.6);

③很重要的一点,设置窗口背景透明,如果不设置这个,窗口运行起来就是有一个白色背景,设置代码如下:

	//设置窗口背景透明
	setAttribute(Qt::WA_TranslucentBackground, true);

④设置窗口可以随着鼠标拖动而移动,这里需要重写QWidget类的三个虚函数方法,分别是:

	void mousePressEvent(QMouseEvent*);
	void mouseMoveEvent(QMouseEvent*);
	void mouseReleaseEvent(QMouseEvent*);

实现代码如下所示:

	//定义变量-记录最后的移动位置
	QPoint last;
	//实现三个虚函数
	void GuardXiaobei::mousePressEvent(QMouseEvent *e)
	{
	    last = e->globalPos();
	}
	void GuardXiaobei::mouseMoveEvent(QMouseEvent *e)
	{
	    if((last.x()==0)&&(last.y()==0))
	        return;
	    int dx = e->globalX() - last.x();
	    int dy = e->globalY() - last.y();
	    last = e->globalPos();
	    this->move(x()+dx, y()+dy);
	}
	void GuardXiaobei::mouseReleaseEvent(QMouseEvent *e)
	{
	    if((last.x()==0)&&(last.y()==0))
	        return;
	    int dx = e->globalX() - last.x();
	    int dy = e->globalY() - last.y();
	    this->move(x() + dx, y() + dy);
	}

七、详细绘制过程
①首先初始化必要的变量,比如设置圆圈的半径大小(这里默认设置为35像素),初始化界面上显示百分比的几个控件,设置对齐方式等,设置几个控件的代码如下:

	//初始化控件
	lab_Value=new QLabel();
	QLabel *lab_Unit=new QLabel();
	QLabel *lab_NetSpeed=new QLabel();
	QHBoxLayout* valueLayout=new QHBoxLayout();
	QVBoxLayout* mainLayout=new QVBoxLayout();
	//设置控件样式
	lab_Value->setFont(QFont("宋体",18));
	//设置控件文字
	lab_Value->setText("10");
	lab_Unit->setText("MB/s");
	lab_NetSpeed->setText("+ 0B/s");
	//设置控件文字对齐方式
	lab_Value->setAlignment(Qt::AlignHCenter|Qt::AlignBottom);
	lab_Unit->setAlignment(Qt::AlignHCenter|Qt::AlignBottom);
	lab_NetSpeed->setAlignment(Qt::AlignHCenter|Qt::AlignTop);
	lab_Value->setFixedHeight(40);
	lab_Unit->setFixedHeight(36);
	//设置布局
	valueLayout->setMargin(0);
	valueLayout->setSpacing(0);
	valueLayout->addStretch(1);
	valueLayout->addWidget(lab_Value);
	valueLayout->addWidget(lab_Unit);
	valueLayout->addStretch(1);
	mainLayout->setMargin(0);
	mainLayout->setSpacing(0);
	mainLayout->addLayout(valueLayout,3);
	mainLayout->addWidget(lab_NetSpeed,2);
	setLayout(mainLayout);

②重载QWidget的绘图函数paintEvent。初始化绘图的QPainter对象,启用反锯齿,代码如下所示:

	QPainter painter(this);
	//启动反锯齿
	painter.setRenderHint(QPainter::Antialiasing, true);

③计算圆形的宽度,绘制一个填充圆,外边框的颜色值为RGBA(230,230,230,200)。内部填充色为RGB(255,255,255)。代码如下所示:

	int height = this->height();
	int width = this->width();
	int roundWidth = qMin(width, height)-2;
	//圆路径集合
    QPainterPath roundPath;
    roundPath.addEllipse(1, 1, roundWidth, roundWidth);
	//绘制一个填充圆
	painter.setPen(QColor(230,230,230,200));
	painter.setBrush(QColor(255,255,255));
	painter.drawEllipse(1, 1, roundWidth, roundWidth);
	//painter.drawPath(roundPath);		//和上一句效果一样 等价替换

其中height和width下面代码中还要使用,这里绘制的效果图如下所示:
在这里插入图片描述
④计算正弦/余弦函数波形图的路径,代码如下所示,下面的变量m_offset是偏移量,这里可以先定义为0,后面在定时更新的时候要用:

	//波浪线路径集合
	QPainterPath wavyPath1;
	QPainterPath wavyPath2;
	//计算当前值在进度条中高度
	int currentHeight = (1-(m_value)/100.0)*height;
	//计算圆中有多少个完整周期 这里为1.5个周期
	double roundCycle = 3 * M_PI /width;
	//移动到左下角起始点
	wavyPath1.moveTo(0, height);
	wavyPath2.moveTo(0, height);
	for(int i=0;i<=width;++i) {
	    //第一条波浪Y轴
	    double wavyY1 = (double)(2*qSin(roundCycle*i+m_offset)) + currentHeight;
	    //第二条波浪Y轴
	    double wavyY2 = -(double)(2*qSin(roundCycle*i+m_offset)) + currentHeight;
	    if (m_value == 0) {
	        wavyY1 = height;
	        wavyY2 = height;
	    }
	    if (m_value == 100) {
	        wavyY1 = 0;
	        wavyY2 = 0;
	    }
	    //添加点在路径中
	    wavyPath1.lineTo(i, wavyY1);
	    wavyPath2.lineTo(i, wavyY2);
	}
	//移动到右下角结束点 形成一个闭合路径 后面才能取交集
	wavyPath1.lineTo(width, height);
	wavyPath2.lineTo(width, height);

⑤先将绘制的圆形的路径添加到一个路径变量中,然后计算异形图像路径和圆形图案之间交集路径,并绘制,代码如下所示:

	//用波浪线和填充圆的交集绘制进度
	QPainterPath intersectedPath;
	painter.setPen(Qt::NoPen);
	//第一条波浪与圆的交集路径集合并绘制路径
	intersectedPath=roundPath.intersected(wavyPath1);
	painter.setBrush(QColor(185,187,255,100));
	painter.drawPath(intersectedPath);
	//第二条波浪与圆的交集路径集合并绘制路径
	intersectedPath=roundPath.intersected(wavyPath2);
	painter.setBrush(QColor(107,150,255,150));
	painter.drawPath(intersectedPath);

效果图如下所示:
在这里插入图片描述
⑥此时的绘制函数代码已经全部完成,剩下就是添加定时器,让水波纹周而复始的移动,代码如下所示:

	//在构造函数中启动定时器
	startTimer(150);
	//重写timerEvent函数,定期执行更新操作
	void ThunderFloatBall::timerEvent(QTimerEvent*)
	{
		update();
	}
	在paintEvent函数中,计算偏移量
	//计算偏移值
	m_offset+=0.5;
	if (m_offset > (width)) {
	    m_offset = 0;
	}

八、最后总结
①目前只是实现了悬浮球的鼠标点击拖动,并没有做悬浮球碰到边界的检测和贴边收缩功能。后续增加这个功能。
②上面描述的是绘制的整个过程,源码中,给出了设置开一系列参数设置函数,可以根据自身需要设置不同的值,实现不同的效果。

九、代码获取
从Git下载,地址为:https://github.com/youyicc/ThunderFloatBall.git

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值