Skip to content

Commit 50b1247

Browse files
committed
✨ 增加旋转动画和弹性动画按钮示例
1 parent 7e32bff commit 50b1247

File tree

6 files changed

+459
-1
lines changed

6 files changed

+459
-1
lines changed

QPushButton/Data/Images/avatar.jpg

24.6 KB
Loading

QPushButton/README.md

+13-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
- [按钮底部线条进度](#2按钮底部线条进度)
66
- [按钮文字旋转进度](#3按钮文字旋转进度)
77
- [按钮常用信号](#4按钮常用信号)
8+
- [旋转动画按钮](#5旋转动画按钮)
9+
- [弹性动画按钮](#6弹性动画按钮)
810

911
## 1、普通样式
1012
[运行 NormalStyle.py](NormalStyle.py)
@@ -33,4 +35,14 @@
3335
根据官网文档 https://doc.qt.io/qt-5/qabstractbutton.html#signals 中的信号介绍编写
3436
按钮的点击、按下、释放、选中信号演示
3537

36-
![SignalsExample](ScreenShot/SignalsExample.gif)
38+
![SignalsExample](ScreenShot/SignalsExample.gif)
39+
40+
## 5、旋转动画按钮
41+
[运行 RotateButton.py](RotateButton.py)
42+
43+
![RotateButton](ScreenShot/RotateButton.gif)
44+
45+
## 6、弹性动画按钮
46+
[运行 RubberBandButton.py](RubberBandButton.py)
47+
48+
![RubberBandButton](ScreenShot/RubberBandButton.gif)

QPushButton/RotateButton.py

+243
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
"""
4+
Created on 2019年1月2日
5+
@author: Irony
6+
@site: https://pyqt.site https://github.com/PyQt5
7+
8+
@file: Widgets.RotateButton
9+
@description:
10+
"""
11+
12+
import os
13+
import sys
14+
15+
try:
16+
from PyQt5.QtCore import QPointF, QPropertyAnimation, QRectF, Qt, pyqtProperty
17+
from PyQt5.QtGui import QColor, QImage, QPainter, QPainterPath, QPixmap
18+
from PyQt5.QtWidgets import (
19+
QGraphicsDropShadowEffect,
20+
QPushButton,
21+
QStyle,
22+
QStyleOptionButton,
23+
QStylePainter,
24+
QApplication,
25+
QWidget,
26+
QHBoxLayout,
27+
)
28+
except ImportError:
29+
from PySide2.QtCore import QPointF, QPropertyAnimation, QRectF, Qt, pyqtProperty
30+
from PySide2.QtGui import QColor, QImage, QPainter, QPainterPath, QPixmap
31+
from PySide2.QtWidgets import (
32+
QGraphicsDropShadowEffect,
33+
QPushButton,
34+
QStyle,
35+
QStyleOptionButton,
36+
QStylePainter,
37+
QApplication,
38+
QWidget,
39+
QHBoxLayout,
40+
)
41+
42+
43+
class RotateButton(QPushButton):
44+
45+
STARTVALUE = 0 # 起始旋转角度
46+
ENDVALUE = 360 # 结束旋转角度
47+
DURATION = 540 # 动画完成总时间
48+
49+
def __init__(self, *args, **kwargs):
50+
super(RotateButton, self).__init__(*args, **kwargs)
51+
self.setCursor(Qt.PointingHandCursor)
52+
self._angle = 0 # 角度
53+
self._padding = 10 # 阴影边距
54+
self._image = "" # 图片路径
55+
self._shadowColor = QColor(33, 33, 33) # 阴影颜色
56+
self._pixmap = None # 图片对象
57+
# 属性动画
58+
self._animation = QPropertyAnimation(self, b"angle", self)
59+
self._animation.setLoopCount(1) # 只循环一次
60+
61+
def paintEvent(self, event):
62+
"""绘制事件"""
63+
text = self.text()
64+
option = QStyleOptionButton()
65+
self.initStyleOption(option)
66+
option.text = "" # 不绘制文字
67+
painter = QStylePainter(self)
68+
painter.setRenderHint(QStylePainter.Antialiasing)
69+
painter.setRenderHint(QStylePainter.HighQualityAntialiasing)
70+
painter.setRenderHint(QStylePainter.SmoothPixmapTransform)
71+
painter.drawControl(QStyle.CE_PushButton, option)
72+
# 变换坐标为正中间
73+
painter.translate(self.rect().center())
74+
painter.rotate(self._angle) # 旋转
75+
76+
# 绘制图片
77+
if self._pixmap and not self._pixmap.isNull():
78+
w = self.width()
79+
h = self.height()
80+
pos = QPointF(-self._pixmap.width() / 2, -self._pixmap.height() / 2)
81+
painter.drawPixmap(pos, self._pixmap)
82+
elif text:
83+
# 在变换坐标后的正中间画文字
84+
fm = self.fontMetrics()
85+
w = fm.width(text)
86+
h = fm.height()
87+
rect = QRectF(0 - w * 2, 0 - h, w * 2 * 2, h * 2)
88+
painter.drawText(rect, Qt.AlignCenter, text)
89+
else:
90+
super(RotateButton, self).paintEvent(event)
91+
92+
def enterEvent(self, _):
93+
"""鼠标进入事件"""
94+
# 设置阴影
95+
# 边框阴影效果
96+
effect = QGraphicsDropShadowEffect(self)
97+
effect.setBlurRadius(self._padding * 2)
98+
effect.setOffset(0, 0)
99+
effect.setColor(self._shadowColor)
100+
self.setGraphicsEffect(effect)
101+
102+
# 开启旋转动画
103+
self._animation.stop()
104+
cv = self._animation.currentValue() or self.STARTVALUE
105+
self._animation.setDuration(
106+
self.DURATION if cv == 0 else int(cv / self.ENDVALUE * self.DURATION)
107+
)
108+
self._animation.setStartValue(cv)
109+
self._animation.setEndValue(self.ENDVALUE)
110+
self._animation.start()
111+
112+
def leaveEvent(self, _):
113+
"""鼠标离开事件"""
114+
# 取消阴影
115+
self.setGraphicsEffect(None)
116+
117+
# 旋转动画
118+
self._animation.stop()
119+
cv = self._animation.currentValue() or self.ENDVALUE
120+
self._animation.setDuration(int(cv / self.ENDVALUE * self.DURATION))
121+
self._animation.setStartValue(cv)
122+
self._animation.setEndValue(self.STARTVALUE)
123+
self._animation.start()
124+
125+
def setPixmap(self, path):
126+
if not os.path.exists(path):
127+
self._image = ""
128+
self._pixmap = None
129+
return
130+
self._image = path
131+
size = (
132+
max(
133+
min(self.width(), self.height()),
134+
min(self.minimumWidth(), self.minimumHeight()),
135+
)
136+
- self._padding
137+
) # 需要边距的边框
138+
radius = int(size / 2)
139+
image = QImage(size, size, QImage.Format_ARGB32_Premultiplied)
140+
image.fill(Qt.transparent) # 填充背景为透明
141+
pixmap = QPixmap(path).scaled(
142+
size, size, Qt.KeepAspectRatioByExpanding, Qt.SmoothTransformation
143+
)
144+
# QPainter
145+
painter = QPainter()
146+
painter.begin(image)
147+
painter.setRenderHint(QPainter.Antialiasing, True)
148+
painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
149+
# QPainterPath
150+
path = QPainterPath()
151+
path.addRoundedRect(0, 0, size, size, radius, radius)
152+
# 切割圆
153+
painter.setClipPath(path)
154+
painter.drawPixmap(0, 0, pixmap)
155+
painter.end()
156+
self._pixmap = QPixmap.fromImage(image)
157+
self.update()
158+
159+
def pixmap(self):
160+
return self._pixmap
161+
162+
@pyqtProperty(str)
163+
def image(self):
164+
return self._image
165+
166+
@image.setter
167+
def image(self, path):
168+
self.setPixmap(path)
169+
170+
@pyqtProperty(int)
171+
def angle(self):
172+
return self._angle
173+
174+
@angle.setter
175+
def angle(self, value):
176+
self._angle = value
177+
self.update()
178+
179+
@pyqtProperty(int)
180+
def padding(self):
181+
return self._padding
182+
183+
@padding.setter
184+
def padding(self, value):
185+
self._padding = value
186+
187+
@pyqtProperty(QColor)
188+
def shadowColor(self):
189+
return self._shadowColor
190+
191+
@shadowColor.setter
192+
def shadowColor(self, color):
193+
self._shadowColor = QColor(color)
194+
195+
196+
class TestWindow(QWidget):
197+
198+
def __init__(self, *args, **kwargs):
199+
super().__init__(*args, **kwargs)
200+
layout = QHBoxLayout(self)
201+
202+
btn = RotateButton("pyqt.site", self)
203+
btn.setMinimumHeight(96)
204+
btn.setToolTip("旋转按钮")
205+
layout.addWidget(btn)
206+
207+
btn = RotateButton("", self)
208+
btn.setMinimumHeight(96)
209+
btn.setObjectName("imageLabel1")
210+
btn.setPixmap("./Data/Images/avatar.jpg")
211+
layout.addWidget(btn)
212+
213+
btn = RotateButton("", self)
214+
btn.setMinimumHeight(96)
215+
btn.setObjectName("imageLabel2")
216+
layout.addWidget(btn)
217+
218+
219+
if __name__ == "__main__":
220+
import cgitb
221+
222+
cgitb.enable(1, None, 5, "text")
223+
224+
# cd to current dir
225+
os.chdir(os.path.dirname(os.path.abspath(sys.argv[0])))
226+
227+
app = QApplication(sys.argv)
228+
app.setStyleSheet(
229+
"""
230+
RotateButton {
231+
font-size: 48px;
232+
}
233+
#imageLabel1, #imageLabel2 {
234+
background: transparent;
235+
}
236+
#imageLabel2 {
237+
qproperty-image: "./Data/Images/avatar.jpg";
238+
}
239+
"""
240+
)
241+
w = TestWindow()
242+
w.show()
243+
sys.exit(app.exec_())

0 commit comments

Comments
 (0)