在实际开发中很多时候Qt提供的控件无法满足其需求,就需要自定义控件。圆形进度条可用于:下载/上传进度、文件处理、安装/加载进度、资源加载、多任务处理等的场景。
开发环境
CircularProgressBar
类头文件定义
class CircularProgressBar : public QWidget
{
Q_OBJECT
public:
enum ProgressBarTextFormat
{
PBTF_Number, // 数字
PBTF_Percent // 百分比
};
explicit CircularProgressBar(QWidget *parent = nullptr);
inline ProgressBarTextFormat progressBarTextFormat() const { return m_curTextFormat; }
void setProgressBarTextFoamat(ProgressBarTextFormat textFormat);
public slots:
void setTick(int tick);
protected:
void paintEvent(QPaintEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void showEvent(QShowEvent *event) override;
signals:
private:
void init();
void paintText(QPainter *painter);
private:
qreal m_padding; // 进度内外边距
QFont m_font; // 绘制文本的字体
qreal m_outerRadius; // 最外层的圆形半径
QTimer *m_timer;
int m_tick;
ProgressBarTextFormat m_curTextFormat;
};
CircularProgressBar
类源文件定义
// 这个函数用于接收主界面QSlider中valueChanged信号的值
void CircularProgressBar::setTick(int tick)
{
m_tick = tick;
update();
}
// 用于在控件大小改变时更新绘制字体的大小
void CircularProgressBar::resizeEvent(QResizeEvent *event)
{
Q_UNUSED(event)
m_outerRadius = qMin(size().width(), size().height());
m_font.setPointSizeF(m_outerRadius / 6);
update();
}
// 绘制不同的文本格式
void CircularProgressBar::paintText(QPainter *p)
{
qreal radius = m_outerRadius / 2;
p->setFont(m_font);
p->setPen(Qt::black);
QString text = "";
if (m_curTextFormat == PBTF_Number) {
text = QString("%1").arg(m_tick);
} else if (m_curTextFormat == PBTF_Percent) {
text = QString("%1%").arg(m_tick);
}
QFontMetricsF fontMetrics(m_font);
qreal textWidth = fontMetrics.horizontalAdvance(text);
qreal textHeight = fontMetrics.height();
qreal tx = ((radius * 2) - textWidth) / 2;
qreal ty = ((radius * 2) - textHeight) / 2;
QRectF textRect(tx, ty, textWidth, textHeight);
#ifdef DRAW_HELP1
p->setPen(Qt::red);
p->drawRoundedRect(textRect, 0, 0);
#endif
p->drawText(textRect, text);
}
// 初始化设置
void CircularProgressBar::init()
{
resize(150, 150);
m_padding = 3;
m_font.setFamily("微软雅黑");
m_font.setPointSizeF(38);
m_font.setBold(true);
m_curTextFormat = PBTF_Number;
}
实现思路: 先绘制出最外层的背景;再根据内外的边距绘制出圆形进度;然后是带有内边距的背景;其次是带有文本的中心区域;最后是文本。
// 这是最主要的代码
void CircularProgressBar::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
// 1. 绘制背景1
QColor bgColor(250, 250, 250);
QPen pen;
pen.setColor(bgColor);
#ifdef DRAW_HELP1
p.setPen(QPen(QColor(Qt::red), 0.3));
#else
p.setPen(bgColor);
#endif
p.setBrush(bgColor);
qreal radius = m_outerRadius / 2;
QPointF center(radius, radius);
p.drawEllipse(center, radius, radius);
// 2. 绘制圆形进度
QRectF arcRect(0, 0, radius * 2, radius * 2);
arcRect -= QMargins(m_padding, m_padding, m_padding, m_padding);
QColor circularColor(71, 164, 233);
p.setPen(circularColor);
p.setBrush(circularColor);
// 为了更精确地表示角度,Qt使用了16进制度量法。将角度乘以16可以得到相应的16进制角度值,因此30 * 16表示30度。
// p.drawPie(arcRect, 90 * 16, -(90 * 16));
if (m_tick != 0)
p.drawPie(arcRect, 90 * 16, -(m_tick * 3.6 * 16));
// 3. 绘制背景2
qreal radius2 = radius * 0.75;
p.setPen(bgColor);
p.setBrush(bgColor);
qreal radius3 = radius2 + m_padding;
p.drawEllipse(center, radius3, radius3);
// 4. 绘制中心区域
QColor centerColor(240, 240, 240);
p.setPen(centerColor);
p.setBrush(centerColor);
p.drawEllipse(center, radius2, radius2);
// 5. 绘制文本
paintText(&p);
}