QT风格(QStyle):绘制一个自定义QProgressBar

参考:[Qt]自定义QStyle——实现QProgressBar自定义样式

参考:QStyle Progress Bar 样式设计(十七)

前面已经知道了绘制控件只需要把控件的子元素/子控件绘制出来即可。

一个默认的QProgressBar子元素如下:

QProgressBar只有子元素没有子控件(可以与之交互的是子控件,只能展示不能交互的叫子元素)

设计图:

根据这个设计图,各个子元素的位置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
QRect myProgressBarStyle::subElementRect(SubElement element,
                                         const QStyleOption *option,
                                         const QWidget *widget) const
{
    switch (element)
    {
    case CE_ProgressBarContents:
        return widget->rect();
    case SE_ProgressBarContents:
        return widget->rect();
    case SE_ProgressBarLabel:
    {
        if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
        {
            if(pb->orientation == Qt::Vertical)
            {
                return QRect(0,widget->height() * 0.4,widget->width(),widget->height() * 0.2);
            }
            else
            {
                return QRect(widget->width()*0.4,0,widget->width()*0.2,widget->height());
            }
        }
    }
    break;
    default:
        return QProxyStyle::subElementRect(element,option,widget);
    }
}

完整代码:

.h文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef MYPROGRESSBARSTYLE_H
#define MYPROGRESSBARSTYLE_H

#include <QProxyStyle>

class myProgressBarStyle : public QProxyStyle
{
public:
    myProgressBarStyle();
protected:
    void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const override;
    void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = nullptr) const override;
    QRect subElementRect(SubElement element, const QStyleOption *option, const QWidget *widget) const override;
};

#endif // MYPROGRESSBARSTYLE_H

.cpp文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include "myprogressbarstyle.h"
#include <QPainter>
#include <QStyleOption>
#include <QDebug>
#include <qdrawutil.h>
myProgressBarStyle::myProgressBarStyle()
{

}

void myProgressBarStyle::drawControl(ControlElement element,
                                     const QStyleOption *option,
                                     QPainter *painter,
                                     const QWidget *widget)const
{
    switch (element)
    {
    case CE_ProgressBar://整个进度条部分,整个绘制QProgressBar的开始
    {
        if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
        {
            QStyleOptionProgressBar subopt = *pb;
            painter->save();
            painter->setBrush(QColor(88, 88, 88, 63));
            painter->setPen(Qt::transparent);

            QRect rect = subElementRect(SE_ProgressBarContents,pb,widget);
            int diameter = 12;
            int cx = 100 * diameter / rect.width();
            int cy = 100 * diameter / rect.height();
            painter->drawRoundRect(rect, cx, cy);//绘制圆角矩形
            painter->restore();

            drawControl(CE_ProgressBarContents, &subopt, painter, widget);
            if (pb->textVisible)
            {
                subopt.rect = subElementRect(SE_ProgressBarLabel, pb, widget);
                drawControl(CE_ProgressBarLabel, &subopt, painter, widget);
            }
        }
    }
    break;
    case CE_ProgressBarContents://进度条内容部分,区别于文本部分,只包含进度区域
    {
        if (const QStyleOptionProgressBarV2 *pb = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(option))
        {
            QRect rect = pb->rect;
            QStyleOptionProgressBarV2 drawPb = *pb;
            if(pb->orientation == Qt::Horizontal)
            {
                drawPb.rect.setWidth((pb->progress * rect.width()) / pb->maximum);
            }
            else
            {
                drawPb.rect.setHeight((pb->progress * rect.height()) / pb->maximum);
            }
            drawPrimitive(PE_IndicatorProgressChunk, &drawPb, painter, widget);
        }
    }
    break;
    case CE_ProgressBarGroove://这个元素查看Qt源码发现这个部分宽度为固定值1,而且从效果上看是介于内容和文本之间的部分
    {
        if (option->rect.isValid())
            qDrawShadePanel(painter, option->rect, option->palette, true, 1,
                            &option->palette.brush(QPalette::Window));
    }
    break;
    case CE_ProgressBarLabel://进度条文本部分
    {
        if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
        {
            QPalette::ColorRole textRole = QPalette::Text;
            QRect rect = subElementRect(SE_ProgressBarLabel,option,widget);
            QPalette shadowPalette = pb->palette;
            shadowPalette.setColor(textRole, QColor("#ffffff"));
            QString text = "完成度:" + pb->text;
            proxy()->drawItemText(painter, rect, Qt::AlignCenter | Qt::TextSingleLine,
                                  shadowPalette, pb->state & State_Enabled, text, textRole);
        }
    }
    break;
    default:
        return QProxyStyle::drawControl(element,option,painter,widget);
    }
}

QRect myProgressBarStyle::subElementRect(SubElement element,
                                         const QStyleOption *option,
                                         const QWidget *widget) const
{
    switch (element)
    {
    case CE_ProgressBarContents:
        return widget->rect();
    case SE_ProgressBarContents:
        return widget->rect();
    case SE_ProgressBarLabel:
    {
        if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(option))
        {
            if(pb->orientation == Qt::Vertical)
            {
                return QRect(0,widget->height() * 0.4,widget->width(),widget->height() * 0.2);
            }
            else
            {
                return QRect(widget->width()*0.4,0,widget->width()*0.2,widget->height());
            }
        }
    }
    break;
    default:
        return QProxyStyle::subElementRect(element,option,widget);
    }
}

void myProgressBarStyle::drawPrimitive(PrimitiveElement which,
                                       const QStyleOption *option,
                                       QPainter *painter,
                                       const QWidget *widget) const
{
    switch (which)
    {
        case PE_IndicatorProgressChunk://此元素表示进度覆盖区域的元素,windows样式是一小节一小节设定的
        {
            painter->save();
            QLinearGradient linear;
            linear.setStart(0,0);
            linear.setFinalStop(widget->width(), widget->height());
            linear.setColorAt(0, QColor(255,182,193));
            linear.setColorAt(0.5, QColor(100,149,237));
            linear.setColorAt(1, QColor(255,222,173));
            painter->setPen(Qt::NoPen);
            painter->setBrush(linear);
            painter->drawRect(option->rect);
            painter->restore();
        }
        break;
        default:
            QProxyStyle::drawPrimitive(which, option, painter, widget);
    }
}

效果: