Skip to content

Commit e68853c

Browse files
committed
窗口翻转动画(仿QQ)
1 parent 756f4fd commit e68853c

File tree

8 files changed

+262
-1
lines changed

8 files changed

+262
-1
lines changed

.settings/org.eclipse.core.resources.prefs

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ encoding//QProgressBar/MetroCircleProgress.py=utf-8
3535
encoding//QProgressBar/PercentProgressBar.py=utf-8
3636
encoding//QProgressBar/SimpleStyle.py=utf-8
3737
encoding//QProgressBar/WaterProgressBar.py=utf-8
38+
encoding//QPropertyAnimation/FlipWidgetAnimation.py=utf-8
3839
encoding//QPropertyAnimation/Lib/SlidingStackedWidget.py=utf-8
3940
encoding//QPropertyAnimation/MenuAnimation.py=utf-8
4041
encoding//QPropertyAnimation/ShakeWindow.py=utf-8

QPropertyAnimation/Data/1.png

44.4 KB
Loading

QPropertyAnimation/Data/2.png

47.8 KB
Loading
+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
"""
5+
Created on 2019年5月15日
6+
@author: Irony
7+
@site: https://pyqt5.com https://github.com/892768447
8+
@email: 892768447@qq.com
9+
@file: 翻转动画
10+
@description:
11+
"""
12+
from PyQt5.QtCore import Qt, pyqtSignal, QTimer
13+
from PyQt5.QtGui import QPixmap
14+
from PyQt5.QtWidgets import QStackedWidget, QLabel
15+
16+
from Lib.FlipWidget import FlipWidget
17+
18+
19+
__Author__ = 'Irony'
20+
__Copyright__ = 'Copyright (c) 2019 Irony'
21+
__Version__ = 1.0
22+
23+
24+
class LoginWidget(QLabel):
25+
# 只是显示登录界面截图
26+
27+
windowClosed = pyqtSignal()
28+
windowChanged = pyqtSignal()
29+
30+
def __init__(self, *args, **kwargs):
31+
super(LoginWidget, self).__init__(*args, **kwargs)
32+
self.setPixmap(QPixmap('Data/1.png'))
33+
34+
def mousePressEvent(self, event):
35+
super(LoginWidget, self).mousePressEvent(event)
36+
pos = event.pos()
37+
if pos.y() <= 40:
38+
if pos.x() > self.width() - 30:
39+
# 点击关闭按钮的地方
40+
self.windowClosed.emit()
41+
elif self.width() - 90 <= pos.x() <= self.width() - 60:
42+
# 点击切换按钮
43+
self.windowChanged.emit()
44+
45+
46+
class SettingWidget(QLabel):
47+
# 只是显示设置界面截图
48+
49+
windowClosed = pyqtSignal()
50+
windowChanged = pyqtSignal()
51+
52+
def __init__(self, *args, **kwargs):
53+
super(SettingWidget, self).__init__(*args, **kwargs)
54+
self.setPixmap(QPixmap('Data/2.png'))
55+
56+
def mousePressEvent(self, event):
57+
super(SettingWidget, self).mousePressEvent(event)
58+
pos = event.pos()
59+
if pos.y() >= self.height() - 30:
60+
if self.width() - 95 <= pos.x() <= self.width() - 10:
61+
# 点击切换按钮
62+
self.windowChanged.emit()
63+
elif pos.y() <= 40:
64+
if pos.x() > self.width() - 30:
65+
# 点击关闭按钮的地方
66+
self.windowClosed.emit()
67+
68+
69+
class Window(QStackedWidget):
70+
# 主窗口
71+
72+
def __init__(self, *args, **kwargs):
73+
super(Window, self).__init__(*args, **kwargs)
74+
self.resize(428, 329)
75+
self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint)
76+
77+
# 这个是动画窗口,先创建不显示
78+
self.flipWidget = FlipWidget()
79+
self.flipWidget.finished.connect(self.showWidget)
80+
81+
# 登录窗口
82+
self.loginWidget = LoginWidget(self)
83+
self.loginWidget.windowClosed.connect(self.close)
84+
self.loginWidget.windowChanged.connect(self.jumpSettingWidget)
85+
self.addWidget(self.loginWidget)
86+
87+
# 设置窗口
88+
self.settingWidget = SettingWidget(self)
89+
self.settingWidget.windowClosed.connect(self.close)
90+
self.settingWidget.windowChanged.connect(self.jumpLoginWidget)
91+
self.addWidget(self.settingWidget)
92+
93+
def showWidget(self):
94+
# 显示主窗口隐藏动画窗口
95+
self.setWindowOpacity(1)
96+
QTimer.singleShot(100, self.flipWidget.hide)
97+
98+
def jumpLoginWidget(self):
99+
# 翻转到登录界面
100+
self.setWindowOpacity(0) # 类似隐藏,但是保留了任务栏
101+
self.setCurrentWidget(self.loginWidget) # 很重要,一定要先切换过去,不然会导致第一次截图有误
102+
image1 = self.loginWidget.grab() # 截图1
103+
image2 = self.settingWidget.grab() # 截图2
104+
padding = 100 # 扩大边距 @UnusedVariable
105+
self.flipWidget.setGeometry(self.geometry())
106+
# .adjusted(-padding, -padding, padding, padding))
107+
self.flipWidget.updateImages(FlipWidget.Right, image2, image1)
108+
109+
def jumpSettingWidget(self):
110+
# 翻转到设置界面
111+
self.setWindowOpacity(0) # 类似隐藏,但是保留了任务栏
112+
self.setCurrentWidget(self.settingWidget) # 很重要,一定要先切换过去,不然会导致第一次截图有误
113+
image1 = self.loginWidget.grab() # 截图1
114+
image2 = self.settingWidget.grab() # 截图2
115+
padding = 100 # 扩大边距 @UnusedVariable
116+
self.flipWidget.setGeometry(self.geometry())
117+
# .adjusted(-padding, -padding, padding, padding))
118+
self.flipWidget.updateImages(FlipWidget.Left, image1, image2)
119+
120+
121+
if __name__ == '__main__':
122+
import sys
123+
from PyQt5.QtWidgets import QApplication
124+
app = QApplication(sys.argv)
125+
w = Window()
126+
w.show()
127+
sys.exit(app.exec_())

QPropertyAnimation/Lib/FlipWidget.py

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
#!/usr/bin/env python
2+
# -*- coding: utf-8 -*-
3+
4+
"""
5+
Created on 2019年5月15日
6+
@author: Irony
7+
@site: https://pyqt5.com https://github.com/892768447
8+
@email: 892768447@qq.com
9+
@file: FlipWidget
10+
@description: 动画翻转窗口
11+
"""
12+
from PyQt5.QtCore import pyqtSignal, Qt, QPropertyAnimation, QEasingCurve,\
13+
pyqtProperty, QPointF
14+
from PyQt5.QtGui import QPainter, QTransform
15+
from PyQt5.QtWidgets import QWidget
16+
17+
18+
__Author__ = 'Irony'
19+
__Copyright__ = 'Copyright (c) 2019'
20+
21+
22+
class FlipWidget(QWidget):
23+
24+
Left = 0 # 从右往左
25+
Right = 1 # 从左往右
26+
Scale = 3 # 图片缩放比例
27+
finished = pyqtSignal()
28+
29+
def __init__(self, *args, **kwargs):
30+
super(FlipWidget, self).__init__(*args, **kwargs)
31+
# 无边框无任务栏
32+
self.setWindowFlags(self.windowFlags() |
33+
Qt.FramelessWindowHint | Qt.SubWindow)
34+
# 背景透明
35+
self.setAttribute(Qt.WA_TranslucentBackground, True)
36+
# 翻转角度
37+
self._angle = 0
38+
# 属性动画针对自定义属性`angle`
39+
self._animation = QPropertyAnimation(self, b'angle', self)
40+
self._animation.setDuration(550)
41+
self._animation.setEasingCurve(QEasingCurve.OutInQuad)
42+
self._animation.finished.connect(self.finished.emit)
43+
44+
@pyqtProperty(int)
45+
def angle(self):
46+
return self._angle
47+
48+
@angle.setter
49+
def angle(self, angle):
50+
self._angle = angle
51+
self.update()
52+
53+
def updateImages(self, direction, image1, image2):
54+
"""设置两张切换图
55+
:param direction: 方向
56+
:param image1: 图片1
57+
:param image2: 图片2
58+
"""
59+
self.image1 = image1
60+
self.image2 = image2
61+
self.show()
62+
self._angle = 0
63+
# 根据方向设置动画的初始和结束值
64+
if direction == self.Right:
65+
self._animation.setStartValue(1)
66+
self._animation.setEndValue(-180)
67+
elif direction == self.Left:
68+
self._animation.setStartValue(1)
69+
self._animation.setEndValue(180)
70+
self._animation.start()
71+
72+
def paintEvent(self, event):
73+
super(FlipWidget, self).paintEvent(event)
74+
75+
if hasattr(self, 'image1') and hasattr(self, 'image2') and self.isVisible():
76+
77+
painter = QPainter(self)
78+
painter.setRenderHint(QPainter.Antialiasing, True)
79+
painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
80+
81+
# 变换
82+
transform = QTransform()
83+
# 把圆心设置为矩形中心
84+
transform.translate(self.width() / 2, self.height() / 2)
85+
86+
if self._angle >= -90 and self._angle <= 90:
87+
# 当翻转角度在90范围内显示第一张图,且从大图缩放到小图的过程
88+
painter.save()
89+
# 设置翻转角度
90+
transform.rotate(self._angle, Qt.YAxis)
91+
painter.setTransform(transform)
92+
# 缩放图片高度
93+
width = self.image1.width() / 2
94+
height = int(self.image1.height() *
95+
(1 - abs(self._angle / self.Scale) / 100))
96+
image = self.image1.scaled(
97+
self.image1.width(), height,
98+
Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
99+
painter.drawPixmap(
100+
QPointF(-width, -height / 2), image)
101+
painter.restore()
102+
else:
103+
# 当翻转角度在90范围内显示第二张图,且从小图缩放到原图的过程
104+
painter.save()
105+
if self._angle > 0:
106+
angle = 180 + self._angle
107+
else:
108+
angle = self._angle - 180
109+
# 设置翻转角度, 注意这里角度有差异
110+
transform.rotate(angle, Qt.YAxis)
111+
painter.setTransform(transform)
112+
# 缩放图片高度
113+
width = self.image2.width() / 2
114+
height = int(self.image2.height() *
115+
(1 - ((360 - abs(angle)) / self.Scale / 100)))
116+
image = self.image2.scaled(
117+
self.image2.width(), height,
118+
Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
119+
painter.drawPixmap(
120+
QPointF(-width, -height / 2), image)
121+
painter.restore()

QPropertyAnimation/README.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -124,4 +124,15 @@ def findClose(points):
124124

125125
通过`QPropertyAnimation`对控件的pos属性进行死去活来的修改
126126

127-
![ShakeWindow](ScreenShot/ShakeWindow.gif)
127+
![ShakeWindow](ScreenShot/ShakeWindow.gif)
128+
129+
## 6、窗口翻转动画(仿QQ)
130+
[运行 FlipWidgetAnimation.py](FlipWidgetAnimation.py)
131+
132+
1. 用了两个`QLabel`来显示模拟的图片界面,并实现鼠标点击模拟真实的窗口对应位置点击
133+
2. 用了`QStackedWidget`来存放上面的两个界面`QLabel`
134+
3. 点击切换时主要是对上面的两个界面进行截图并传递给翻转动画窗口
135+
4. 通过`setWindowOpacity`控制主窗口的显示隐藏(保留任务栏),当然也可以用`hide`
136+
5. 动画窗口`FlipWidget.py`主要实现两张图片的翻转显示,考虑到0-90和90-180之前的情况,以及图片的缩放动画
137+
138+
![FlipWidgetAnimation](ScreenShot/FlipWidgetAnimation.gif)
Loading

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ https://pyqt5.com 社区是专门针对PyQt5学习和提升开设的博客网站
152152
- [点阵特效](QPropertyAnimation/RlatticeEffect.py)
153153
- [页面切换/图片轮播动画](QPropertyAnimation/PageSwitching.py)
154154
- [窗口抖动](QPropertyAnimation/ShakeWindow.py)
155+
- [窗口翻转动画(仿QQ)](QPropertyAnimation/FlipWidgetAnimation.py)
155156
- [折叠动画](Test/partner_625781186/2.折叠控件)
156157

157158
- Others

0 commit comments

Comments
 (0)