众所周知,Qt自带了一些进度条控件,但有时并不能满足我们日常的使用要求,所以有时我们需要自己自绘制一些进度条来使界面变得好看。我在这个程序中绘制了如下3种进度条:
这个3种进度条各有特色,能适应不同的场景需要。接下来我将一步步讲解。
首先老规矩,将.h和.cpp的文件贴出来,我用的qt版本是5.9.9,如果是低版本的要注意下QWidget的位置。代码如下:
#pragma once
//.h文件
#include <QtWidgets/QWidget>
#include "ui_CustomProgress.h"
#include <QPaintEvent>
#include <QTimer>
#include <QTimerEvent>
#include<QColor>
#include <QList>
#include <QMouseEvent>
/*
各种进度条的绘制
*/
class MyProgressBar : public QWidget
{
Q_OBJECT
public:
MyProgressBar(QWidget *parent = Q_NULLPTR);
void myPainter1(QPainter &painter);
void myPainter2(QPainter &painter);
void myPainter3(QPainter &painter);
void myPainter4(QPainter &painter);
protected:
void paintEvent(QPaintEvent *event) override;
void timerEvent(QTimerEvent* event) override;
void mousePressEvent(QMouseEvent *event) override;
private:
Ui::CustomProgressClass ui;
int m_timerId;
int m_nRotationAngle;
QList<QColor> style2Color;
qreal arcLength;
int alpha;//测试阿帕奇通道,就是测试图形的透明度
QList<QPainterPath> piePathList;
int pathIndex;
};
#include "CustomProgress.h"
//.cpp文件
#include <QPainter>
#include <QPainterPath>
#include <QRect>
#include <QConicalGradient>
MyProgressBar::MyProgressBar(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
m_timerId = startTimer(5);
m_nRotationAngle = 0;
arcLength = 0.0;
this->alpha = 255;
pathIndex = 0;
}
void MyProgressBar::myPainter1(QPainter &painter)
{
QRect rect(-100, -100, 200, 200);
QConicalGradient conical(0, 0, 0);
conical.setColorAt(0, QColor("#3BB6FE"));
//conical.setColorAt(0.5, Qt::blue);
conical.setColorAt(1, QColor("#FFFFFF"));
painter.translate(width() / 2, height() / 2);
painter.rotate(m_nRotationAngle);
painter.setRenderHint(QPainter::Antialiasing);
painter.setPen(Qt::NoPen);
painter.setBrush(conical);
QPainterPath arcBigPath;
arcBigPath.moveTo(0, 0);
arcBigPath.arcTo(rect, 0, 270);
QRect rect2(-90, -90, 180, 180);
QPainterPath arcPath;
arcPath.moveTo(0, 0);
arcPath.arcTo(rect2, 0, 270);
QPainterPath outPath;
outPath = arcBigPath - arcPath;
painter.drawPath(outPath);
}
void MyProgressBar::myPainter2(QPainter &painter)
{
QRect bigRect(-100, -100, 200, 200);
QRect smallRect(-90, -90, 180, 180);
painter.translate(width() / 2, height() / 2);
QPainterPath pathBigEllipse;
pathBigEllipse.addEllipse(bigRect);
QPainterPath pathSmallEllipse;
pathSmallEllipse.addEllipse(smallRect);
painter.setBrush(Qt::white);
painter.setPen(Qt::SolidLine);
QPainterPath outPath = pathBigEllipse - pathSmallEllipse;
painter.drawPath(outPath);
painter.save();
QPainterPath piePath;
piePath.arcTo(bigRect, 0, arcLength);
QPainterPath out2 = outPath.intersected(piePath);
painter.setPen(Qt::SolidLine);
painter.setBrush(Qt::gray);
painter.drawPath(out2);
painter.restore();
QPainterPath textPath;
QFont font("Times");
textPath.addText(QPoint(-60, 0.0), font, QStringLiteral("当前进度:") + QString::number(int(this->arcLength / 360 * 100)) + "%");
painter.drawPath(textPath);
}
void MyProgressBar::myPainter3(QPainter &painter)
{
QColor color(121, 65, 13, this->alpha);
painter.setBrush(color);
painter.setPen(Qt::NoPen);
painter.drawEllipse(100, 100, 200, 200);
}
void MyProgressBar::myPainter4(QPainter &painter)
{
painter.translate(width() / 2, height() / 2);
painter.setBrush(Qt::gray);
QRect bigRect(-100, -100, 200, 200);
QPainterPath bigEllipse;
bigEllipse.addEllipse(bigRect);
QPainterPath samllEllipse;
QRect smallRect(-90, -90, 180, 180);
samllEllipse.addEllipse(smallRect);
QPainterPath out1 = bigEllipse - samllEllipse;
for (int i = 0; i < 6; i++)
{
QPainterPath piePath;
piePath.arcTo(bigRect, i * 60, 30);
piePath.closeSubpath();
piePathList.push_back(piePath.intersected(out1)); //将六个不同的扇形放到数组里
}
painter.setBrush(Qt::red);
for (int i = 0; i < piePathList.size(); i++)
{
painter.drawPath(piePathList.at(i));
}
if (this->pathIndex == piePathList.size())
{
this->pathIndex = 0;
}
painter.setBrush(Qt::white);
painter.drawPath(piePathList.at(this->pathIndex));
pathIndex++;
}
void MyProgressBar::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
myPainter4(painter);
}
void MyProgressBar::timerEvent(QTimerEvent* event)
{
if (event->timerId() == m_timerId)
{
m_nRotationAngle++;
arcLength++;
if (m_nRotationAngle > 360)
{
m_nRotationAngle = 0;
arcLength = 0;
}
update();
}
}
void MyProgressBar::mousePressEvent(QMouseEvent *event)
{
if (alpha == 0)
{
alpha = 255;
}
this->alpha--;
update();
}
在这个程序中,有3个绘制函数是有用的,那个myPainter3方法是我没有做完的实验,mousePressEvent鼠标按下事件这个方法我本来是想测试下阿帕奇通道的,最后也没有用。
为了使绘制的进度条动起来,我在构造函数中使用startTimer函数开启一个定时器,并实现timerEvent定时器事件,在这个事件中设置参数和持续刷新界面。
现在开始讲解第一个进度条的原理,对应方法myPainter1。
Qt中很容易实现两个图形的交并差的基础图形操作,第一个进度条首先采用的是渐变画刷,其次,用两个270度的扇形,一个大一个小,然后两个相减,就是代码中的outPath = arcBigPath - arcPath;这就实现了进度条的图形区域。如果想要实现其他复杂的图形,QPainterPath这个类你值得深究。在准备好后,在paintEvent这个绘制事件中调用一下就行了。
在myPainter2和myPainter4中也是用了相同的方法,使用多个图形做交并差运算得到对应的图形,然后在进行绘制。需要注意intersected这个函数,其功能是计算两个图形相交后的图形的路径,也就是保存相交后图形的位置,这也是QPainterPath类中的方法,还是那句话,如果想要创造出比较复杂的图形QPainterPath类必不可少。
总的来说,Qt自带的图形计算函数帮我们减少了很多麻烦,接下来我将继续发布我在实习公司所作的Qt绘制实验。