Qt 自定义控件 - 圆形进度条(1)

文章目录

  • 1 概述
  • 2 实现效果
  • 3 实现步骤
    • 3.1 添加继承自QWidget的类
    • 3.2 实现思路

1 概述

在实际开发中很多时候Qt提供的控件无法满足其需求,就需要自定义控件。圆形进度条可用于:下载/上传进度、文件处理、安装/加载进度、资源加载、多任务处理等的场景。

开发环境

  • 系统:Window10
  • Qt版本:5.15.2
  • 编译器:MinGW_64/MSVC 2019

2 实现效果

Qt 自定义控件 - 圆形进度条(1)_第1张图片

3 实现步骤

3.1 添加继承自QWidget的类

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;
};

3.2 实现思路

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);
}
  • 其实对于一些简单的自绘控件,掌握的思路实现起来就很快了。

你可能感兴趣的:(#,Qt自定义控件,qt,开发语言)