Skip to content

Commit 5a1c8fb

Browse files
committed
多彩动画进度条
1 parent d2bbfc9 commit 5a1c8fb

File tree

5 files changed

+340
-0
lines changed

5 files changed

+340
-0
lines changed

QProgressBar/ColourfulProgress.py

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
Created on 2022/02/25
5+
@author: Irony
6+
@site: https://pyqt.site, https://github.com/PyQt5
7+
8+
@file: ColourfulProgress.py
9+
@description:
10+
"""
11+
12+
try:
13+
from PyQt5.QtCore import QLineF, QRect, QRectF, Qt
14+
from PyQt5.QtGui import QColor, QPainter, QPainterPath, QPen, QTransform
15+
from PyQt5.QtWidgets import (QApplication, QGridLayout, QProgressBar,
16+
QSlider, QStyleOptionProgressBar, QWidget)
17+
except ImportError:
18+
from PySide2.QtCore import QRect, Qt, QRectF, QLineF
19+
from PySide2.QtGui import QColor, QPainter, QPen, QTransform, QPainterPath
20+
from PySide2.QtWidgets import (QApplication, QProgressBar,
21+
QStyleOptionProgressBar, QWidget, QSlider,
22+
QGridLayout)
23+
24+
from Lib.QStyleAnimation import QProgressStyleAnimation
25+
26+
27+
class ColourfulProgress(QProgressBar):
28+
29+
def __init__(self, *args, **kwargs):
30+
self._color = kwargs.pop('color', QColor(43, 194, 83))
31+
self._fps = kwargs.pop('fps', 60)
32+
self._lineWidth = kwargs.pop('lineWidth', 50) # 线条宽度
33+
self._radius = kwargs.pop('radius', None) # None为自动计算圆角
34+
self._animation = None
35+
super(ColourfulProgress, self).__init__(*args, **kwargs)
36+
self.setColor(self._color)
37+
self.setFps(self._fps)
38+
self.setLineWidth(self._lineWidth)
39+
self.setRadius(self._radius)
40+
41+
def setColor(self, color):
42+
"""
43+
:type color: QColor
44+
:param color: 颜色
45+
"""
46+
self._color = QColor(color) if isinstance(
47+
color, (QColor, Qt.GlobalColor)) else QColor(43, 194, 83)
48+
49+
def setFps(self, fps):
50+
"""
51+
:type fps: int
52+
:param fps: 帧率
53+
"""
54+
self._fps = max(int(fps), 1) if isinstance(fps, (int, float)) else 60
55+
56+
def setLineWidth(self, width):
57+
"""
58+
:type width: int
59+
:param width: 线条宽度
60+
"""
61+
self._lineWidth = max(int(width), 0) if isinstance(width,
62+
(int, float)) else 50
63+
64+
def setRadius(self, radius):
65+
"""
66+
:type radius: int
67+
:param radius: 半径
68+
"""
69+
self._radius = max(int(radius), 1) if isinstance(radius,
70+
(int, float)) else None
71+
72+
def paintEvent(self, _):
73+
"""
74+
重写绘制事件,参考 qfusionstyle.cpp 中的 CE_ProgressBarContents 绘制方法
75+
"""
76+
option = QStyleOptionProgressBar()
77+
self.initStyleOption(option)
78+
79+
painter = QPainter(self)
80+
painter.setRenderHint(QPainter.Antialiasing)
81+
painter.translate(0.5, 0.5)
82+
83+
vertical = option.orientation == Qt.Vertical # 是否垂直
84+
inverted = option.invertedAppearance # 是否反转
85+
# 是否显示动画
86+
indeterminate = (option.minimum == option.maximum) or (
87+
option.minimum < option.progress < option.maximum)
88+
rect = option.rect
89+
90+
if vertical:
91+
rect = QRect(rect.left(), rect.top(), rect.height(),
92+
rect.width()) # 翻转宽度和高度
93+
m = QTransform.fromTranslate(rect.height(), 0)
94+
m.rotate(90.0)
95+
painter.setTransform(m, True)
96+
97+
maxWidth = rect.width()
98+
progress = max(option.progress, option.minimum)
99+
totalSteps = max(1, option.maximum - option.minimum)
100+
progressSteps = progress - option.minimum
101+
progressBarWidth = int(progressSteps * maxWidth / totalSteps)
102+
width = progressBarWidth # 已进行的进度宽度
103+
radius = max(1, (min(width,
104+
self.width() if vertical else self.height()) //
105+
4) if self._radius is None else self._radius)
106+
107+
reverse = (not vertical and
108+
option.direction == Qt.RightToLeft) or vertical
109+
if inverted:
110+
reverse = not reverse
111+
112+
# 绘制范围
113+
path = QPainterPath()
114+
if not reverse:
115+
progressBar = QRectF(rect.left(), rect.top(), width, rect.height())
116+
else:
117+
progressBar = QRectF(rect.right() - width, rect.top(), width,
118+
rect.height())
119+
120+
# 切割范围
121+
path.addRoundedRect(progressBar, radius, radius)
122+
painter.setClipPath(path)
123+
124+
# 绘制背景颜色
125+
painter.setPen(Qt.NoPen)
126+
painter.setBrush(self._color)
127+
painter.drawRoundedRect(progressBar, radius, radius)
128+
129+
if not indeterminate:
130+
if self._animation:
131+
self._animation.stop()
132+
self._animation = None
133+
else:
134+
# 叠加颜色覆盖后出现类似线条间隔的效果
135+
color = self._color.lighter(320)
136+
color.setAlpha(80)
137+
painter.setPen(QPen(color, self._lineWidth))
138+
139+
if self._animation:
140+
step = int(self._animation.animationStep() % self._lineWidth)
141+
else:
142+
step = 0
143+
self._animation = QProgressStyleAnimation(self._fps, self)
144+
self._animation.start()
145+
146+
# 动画斜线绘制
147+
if (vertical and inverted) or (not vertical and not inverted):
148+
lines = [
149+
QLineF(x + step, progressBar.bottom(),
150+
x + rect.height() + step, progressBar.top())
151+
for x in range(int(progressBar.left() - rect.height()),
152+
int(rect.right()), self._lineWidth)
153+
]
154+
else:
155+
lines = [
156+
QLineF(x - step, progressBar.top(),
157+
x + rect.height() - step, progressBar.bottom())
158+
for x in range(int(progressBar.left() - rect.height()),
159+
int(rect.right()), self._lineWidth)
160+
]
161+
painter.drawLines(lines)
162+
163+
164+
if __name__ == '__main__':
165+
import cgitb
166+
import sys
167+
168+
cgitb.enable(format='text')
169+
app = QApplication(sys.argv)
170+
171+
w = QWidget()
172+
layout = QGridLayout(w)
173+
174+
w1 = ColourfulProgress(color=QColor('#85c440'))
175+
w1.setMinimumWidth(300)
176+
w1.setMaximumWidth(300)
177+
w1.setRange(0, 100)
178+
layout.addWidget(w1, 0, 0, 1, 1)
179+
180+
w2 = ColourfulProgress(color=QColor('#f2b63c'))
181+
w2.setMinimumWidth(300)
182+
w2.setMaximumWidth(300)
183+
w2.setInvertedAppearance(True)
184+
w2.setRange(0, 100)
185+
layout.addWidget(w2, 1, 0, 1, 1)
186+
187+
w3 = ColourfulProgress(color=QColor('#db3a27'))
188+
w3.setMinimumHeight(300)
189+
w3.setMaximumHeight(300)
190+
w3.setOrientation(Qt.Vertical)
191+
w3.setRange(0, 100)
192+
layout.addWidget(w3, 0, 1, 2, 1)
193+
194+
w4 = ColourfulProgress(color=QColor('#5aaadb'))
195+
w4.setMinimumHeight(300)
196+
w4.setMaximumHeight(300)
197+
w4.setInvertedAppearance(True)
198+
w4.setOrientation(Qt.Vertical)
199+
w4.setRange(0, 100)
200+
layout.addWidget(w4, 0, 2, 2, 1)
201+
202+
slider = QSlider(Qt.Horizontal)
203+
slider.setRange(0, 100)
204+
slider.valueChanged.connect(w1.setValue)
205+
slider.valueChanged.connect(w2.setValue)
206+
slider.valueChanged.connect(w3.setValue)
207+
slider.valueChanged.connect(w4.setValue)
208+
slider.setValue(50)
209+
layout.addWidget(slider, 2, 0, 1, 3)
210+
211+
w.show()
212+
213+
sys.exit(app.exec_())

QProgressBar/Lib/QStyleAnimation.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
Created on 2022/02/26
5+
@author: Irony
6+
@site: https://pyqt.site, https://github.com/PyQt5
7+
8+
@file: QStyleAnimation.py
9+
@description:
10+
"""
11+
from enum import IntEnum
12+
13+
try:
14+
from PyQt5.QtCore import (QAbstractAnimation, QCoreApplication, QEvent,
15+
QTime)
16+
except ImportError:
17+
from PySide2.QtCore import (QAbstractAnimation, QCoreApplication, QEvent,
18+
QTime)
19+
20+
ScrollBarFadeOutDuration = 200.0
21+
ScrollBarFadeOutDelay = 450.0
22+
23+
StyleAnimationUpdate = 213
24+
25+
26+
class QStyleAnimation(QAbstractAnimation):
27+
28+
FrameRate = IntEnum(
29+
'FrameRate',
30+
['DefaultFps', 'SixtyFps', 'ThirtyFps', 'TwentyFps', 'FifteenFps'])
31+
32+
def __init__(self, *args, **kwargs):
33+
super(QStyleAnimation, self).__init__(*args, **kwargs)
34+
self._delay = 0
35+
self._duration = -1
36+
self._startTime = QTime.currentTime()
37+
self._fps = self.FrameRate.ThirtyFps
38+
self._skip = 0
39+
40+
def target(self):
41+
return self.parent()
42+
43+
def duration(self):
44+
return self._duration
45+
46+
def setDuration(self, duration):
47+
self._duration = duration
48+
49+
def delay(self):
50+
return self._delay
51+
52+
def setDelay(self, delay):
53+
self._delay = delay
54+
55+
def startTime(self):
56+
return self._startTime
57+
58+
def setStartTime(self, time):
59+
self._startTime = time
60+
61+
def frameRate(self):
62+
return self._fps
63+
64+
def setFrameRate(self, fps):
65+
self._fps = fps
66+
67+
def updateTarget(self):
68+
event = QEvent(StyleAnimationUpdate)
69+
event.setAccepted(False)
70+
QCoreApplication.sendEvent(self.target(), event)
71+
if not event.isAccepted():
72+
self.stop()
73+
74+
def start(self):
75+
self._skip = 0
76+
super(QStyleAnimation, self).start(QAbstractAnimation.DeleteWhenStopped)
77+
78+
def isUpdateNeeded(self):
79+
return self.currentTime() > self._delay
80+
81+
def updateCurrentTime(self, _):
82+
self._skip += 1
83+
if self._skip >= self._fps:
84+
self._skip = 0
85+
if self.parent() and self.isUpdateNeeded():
86+
self.updateTarget()
87+
88+
89+
class QProgressStyleAnimation(QStyleAnimation):
90+
91+
def __init__(self, speed, *args, **kwargs):
92+
super(QProgressStyleAnimation, self).__init__(*args, **kwargs)
93+
self._speed = speed
94+
self._step = -1
95+
96+
def animationStep(self):
97+
return self.currentTime() / (1000.0 / self._speed)
98+
99+
def progressStep(self, width):
100+
step = self.animationStep()
101+
progress = (step * width / self._speed) % width
102+
if (((step * width / self._speed) % (2 * width)) >= width):
103+
progress = width - progress
104+
return progress
105+
106+
def speed(self):
107+
return self._speed
108+
109+
def setSpeed(self, speed):
110+
self._speed = speed
111+
112+
def isUpdateNeeded(self):
113+
if super(QProgressStyleAnimation, self).isUpdateNeeded():
114+
current = self.animationStep()
115+
if self._step == -1 or self._step != current:
116+
self._step = current
117+
return True
118+
return False

QProgressBar/README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- [Metro进度条](#4Metro进度条)
88
- [水波纹进度条](#5水波纹进度条)
99
- [圆形水位进度条](#6圆形水位进度条)
10+
- [多彩动画进度条](#7多彩动画进度条)
1011

1112
## 1、常规样式美化
1213
[运行 SimpleStyle.py](SimpleStyle.py)
@@ -45,3 +46,10 @@
4546
参考 https://github.com/linuxdeepin/dtkwidget/blob/master/src/widgets/dwaterprogress.cpp
4647

4748
![WaterProgressBar](ScreenShot/WaterProgress.gif)
49+
50+
## 7、多彩动画进度条
51+
[运行 ColourfulProgress.py](ColourfulProgress.py)
52+
53+
动画实现参考 qfusionstyle.cpp 中的 CE_ProgressBarContents 绘制方法,存在一点角落地方动画连贯性小问题
54+
55+
![ColourfulProgress](ScreenShot/ColourfulProgress.gif)
422 KB
Loading

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ https://pyqt.site 论坛是专门针对PyQt5学习和提升开设的网站,分
127127
- [Metro进度条](QProgressBar/MetroCircleProgress.py)
128128
- [水波纹进度条](QProgressBar/WaterProgressBar.py)
129129
- [圆形水位进度条](QProgressBar/WaterProgress.py)
130+
- [多彩动画进度条](QProgressBar/ColourfulProgress.py)
130131
- [QSplashScreen](QSplashScreen)
131132
- [启动画面动画](QSplashScreen/GifSplashScreen.py)
132133
- [QOpenGLWidget](QOpenGLWidget)

0 commit comments

Comments
 (0)