图片轮播动画

This commit is contained in:
Irony 2018-11-24 17:16:45 +08:00
parent e00745b006
commit 2ed15b49ed
19 changed files with 577 additions and 3 deletions

2
.gitignore vendored
View file

@ -128,4 +128,4 @@ tmp
.project
.pydevproject
.README.md.html
README.md.html
README.md.html

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?><pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Python34</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 3.0</pydev_property>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/${PROJECT_DIR_NAME}</path>

View file

@ -33,6 +33,9 @@ encoding//\u5217\u8868/QListWidget/\u817E\u8BAF\u89C6\u9891\u70ED\u64AD\u5217\u8
encoding//\u5217\u8868/QListWidget/\u817E\u8BAF\u89C6\u9891\u70ED\u64AD\u5217\u8868/TencentMovieHotPlay_ListWidget.py=utf-8
encoding//\u5217\u8868/QListWidget/\u81EA\u5B9A\u4E49\u53EF\u62D6\u62FDItem.py=utf-8
encoding//\u52A8\u753B/\u53F3\u952E\u83DC\u5355\u52A8\u753B.py=utf-8
encoding//\u52A8\u753B/\u591A\u9875\u9762\u5207\u6362\u52A8\u753B/SlidingStackedWidget.py=utf-8
encoding//\u52A8\u753B/\u591A\u9875\u9762\u5207\u6362\u52A8\u753B/UiImageSlider.py=utf-8
encoding//\u52A8\u753B/\u591A\u9875\u9762\u5207\u6362\u52A8\u753B/\u56FE\u7247\u8F6E\u64AD\u52A8\u753B.py=utf-8
encoding//\u52A8\u753B/\u6309\u94AE\u653E\u5927\u7F29\u5C0F\u52A8\u753B.py=utf-8
encoding//\u52A8\u753B/\u70B9\u9635\u7279\u6548/\u70B9\u9635\u7279\u6548.py=utf-8
encoding//\u52A8\u753B/\u7A97\u53E3\u6DE1\u5165\u6DE1\u51FA.py=utf-8

View file

@ -87,6 +87,7 @@
1. [QStackedWidget](QStackedWidget/)
1. [左侧选项卡](多页面/QStackedWidget/左侧选项卡)
2. [多页面切换动画](动画/多页面切换动画)
## [多线程](多线程)
@ -136,6 +137,7 @@
1. [右键菜单动画](动画/右键菜单动画.py)
1. [按钮放大缩小动画](动画/按钮放大缩小动画.py)
1. [仿网页点阵特效](动画/点阵特效)
1. [图片轮播动画](动画/多页面切换动画/图片轮播动画.py)
## [图表](图表)

View file

@ -123,4 +123,12 @@ def findClose(points):
p1.closest = closest
```
![截图](ScreenShot/点阵特效.gif)
![截图](ScreenShot/点阵特效.gif)
## [5、图片轮播动画](图片轮播动画.py)
1. 使用`QPropertyAnimation`对`QStackedWidget`中的子控件进行pos位移操作实现动画切换特效
1. 主要代码参考http://qt.shoutwiki.com/wiki/Extending_QStackedWidget_for_sliding_page_animations_in_Qt
1. 增加了自动切换函数
![截图](ScreenShot/图片轮播动画.gif)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

View file

@ -0,0 +1,238 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Created on 2018年11月24日
author: Irony
site: https://pyqt5.com , https://github.com/892768447
email: 892768447@qq.com
file:
description: 参考 http://qt.shoutwiki.com/wiki/Extending_QStackedWidget_for_sliding_page_animations_in_Qt
"""
from PyQt5.QtCore import Qt, pyqtProperty, QEasingCurve, QPoint, \
QPropertyAnimation, QParallelAnimationGroup, QTimer
from PyQt5.QtWidgets import QStackedWidget
__Author__ = """By: Irony
QQ: 892768447
Email: 892768447@qq.com"""
__Copyright__ = 'Copyright (c) 2018 Irony'
__Version__ = 1.0
class SlidingStackedWidget(QStackedWidget):
LEFT2RIGHT, RIGHT2LEFT, TOP2BOTTOM, BOTTOM2TOP, AUTOMATIC = range(5)
def __init__(self, *args, **kwargs):
super(SlidingStackedWidget, self).__init__(*args, **kwargs)
self._pnow = QPoint(0, 0)
# 动画速度
self._speed = 500
# 当前索引
self._now = 0
# 自动模式的当前索引
self._current = 0
# 下一个索引
self._next = 0
# 是否激活
self._active = 0
# 动画方向(默认是横向)
self._orientation = Qt.Horizontal
# 动画曲线类型
self._easing = QEasingCurve.Linear
# 初始化动画
self._initAnimation()
def setSpeed(self, speed=500):
"""设置动画速度
:param speed: 速度值,默认值为500
:type speed: int
"""
self._speed = speed
@pyqtProperty(int, fset=setSpeed)
def speed(self):
return self._speed
def setOrientation(self, orientation=Qt.Horizontal):
"""设置动画的方向(横向和纵向)
:param orientation: 方向(Qt.Horizontal或Qt.Vertical)
:type orientation: http://doc.qt.io/qt-5/qt.html#Orientation-enum
"""
self._orientation = orientation
@pyqtProperty(int, fset=setOrientation)
def orientation(self):
return self._orientation
def setEasing(self, easing=QEasingCurve.OutBack):
"""设置动画的曲线类型
:param easing: 默认为QEasingCurve.OutBack
:type easing: http://doc.qt.io/qt-5/qeasingcurve.html#Type-enum
"""
self._easing = easing
@pyqtProperty(int, fset=setEasing)
def easing(self):
return self._easing
def slideInNext(self):
"""滑动到下一页"""
now = self.currentIndex()
if now < self.count() - 1:
self.slideInIdx(now + 1)
self._current = now + 1
def slideInPrev(self):
"""滑动到上一页"""
now = self.currentIndex()
if now > 0:
self.slideInIdx(now - 1)
self._current = now - 1
def slideInIdx(self, idx, direction=4):
"""滑动到指定序号
:param idx: 序号
:type idx: int
:param direction: 方向,默认是自动AUTOMATIC=4
:type direction: int
"""
if idx > self.count() - 1:
direction = self.TOP2BOTTOM if self._orientation == Qt.Vertical else self.RIGHT2LEFT
idx = idx % self.count()
elif idx < 0:
direction = self.BOTTOM2TOP if self._orientation == Qt.Vertical else self.LEFT2RIGHT
idx = (idx + self.count()) % self.count()
self.slideInWgt(self.widget(idx), direction)
def slideInWgt(self, widget, direction):
"""滑动到指定的widget
:param widget: QWidget, QLabel, etc...
:type widget: QWidget Base Class
:param direction: 方向
:type direction: int
"""
if self._active:
return
self._active = 1
_now = self.currentIndex()
_next = self.indexOf(widget)
if _now == next:
self._active = 0
return
w_now = self.widget(_now)
w_next = self.widget(_next)
# 自动判断方向
if _now < _next:
directionhint = self.TOP2BOTTOM if self._orientation == Qt.Vertical else self.RIGHT2LEFT
else:
directionhint = self.BOTTOM2TOP if self._orientation == Qt.Vertical else self.LEFT2RIGHT
if direction == self.AUTOMATIC:
direction = directionhint
# 计算偏移量
offsetX = self.frameRect().width()
offsetY = self.frameRect().height()
w_next.setGeometry(0, 0, offsetX, offsetY)
if direction == self.BOTTOM2TOP:
offsetX = 0
offsetY = -offsetY
elif direction == self.TOP2BOTTOM:
offsetX = 0
elif direction == self.RIGHT2LEFT:
offsetX = -offsetX
offsetY = 0
elif direction == self.LEFT2RIGHT:
offsetY = 0
# 重新定位显示区域外部/旁边的下一个窗口小部件
pnext = w_next.pos()
pnow = w_now.pos()
self._pnow = pnow
# 移动到指定位置并显示
w_next.move(pnext.x() - offsetX, pnext.y() - offsetY)
w_next.show()
w_next.raise_()
self._animnow.setTargetObject(w_now)
self._animnow.setDuration(self._speed)
self._animnow.setEasingCurve(self._easing)
self._animnow.setStartValue(QPoint(pnow.x(), pnow.y()))
self._animnow.setEndValue(
QPoint(offsetX + pnow.x(), offsetY + pnow.y()))
self._animnext.setTargetObject(w_next)
self._animnext.setDuration(self._speed)
self._animnext.setEasingCurve(self._easing)
self._animnext.setStartValue(
QPoint(-offsetX + pnext.x(), offsetY + pnext.y()))
self._animnext.setEndValue(QPoint(pnext.x(), pnext.y()))
self._next = _next
self._now = _now
self._active = 1
self._animgroup.start()
def _initAnimation(self):
"""初始化当前页和下一页的动画变量"""
# 当前页的动画
self._animnow = QPropertyAnimation(
self, propertyName=b'pos', duration=self._speed,
easingCurve=self._easing)
# 下一页的动画
self._animnext = QPropertyAnimation(
self, propertyName=b'pos', duration=self._speed,
easingCurve=self._easing)
# 并行动画组
self._animgroup = QParallelAnimationGroup(
self, finished=self.animationDoneSlot)
self._animgroup.addAnimation(self._animnow)
self._animgroup.addAnimation(self._animnext)
def setCurrentIndex(self, index):
# 覆盖该方法实现的动画切换
# super(SlidingStackedWidget, self).setCurrentIndex(index)
# 坚决不能调用上面的函数,否则动画失效
self.slideInIdx(index)
def setCurrentWidget(self, widget):
# 覆盖该方法实现的动画切换
super(SlidingStackedWidget, self).setCurrentWidget(widget)
# 坚决不能调用上面的函数,否则动画失效
self.setCurrentIndex(self.indexOf(widget))
def animationDoneSlot(self):
"""动画结束处理函数"""
# 由于重写了setCurrentIndex方法所以这里要用父类本身的方法
# self.setCurrentIndex(self._next)
QStackedWidget.setCurrentIndex(self, self._next)
w = self.widget(self._now)
w.hide()
w.move(self._pnow)
self._active = 0
def autoStop(self):
"""停止自动播放"""
if hasattr(self, '_autoTimer'):
self._autoTimer.stop()
def autoStart(self, msec=3000):
"""自动轮播
:param time: 时间, 默认3000, 3
"""
if not hasattr(self, '_autoTimer'):
self._autoTimer = QTimer(self, timeout=self._autoStart)
self._autoTimer.stop()
self._autoTimer.start(msec)
def _autoStart(self):
if self._current == self.count():
self._current = 0
self._current += 1
self.setCurrentIndex(self._current)

View file

@ -0,0 +1,102 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file 'UiImageSlider.ui'
#
# Created by: PyQt5 UI code generator 5.10.1
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_Form(object):
def setupUi(self, Form):
Form.setObjectName("Form")
Form.resize(656, 612)
self.verticalLayout = QtWidgets.QVBoxLayout(Form)
self.verticalLayout.setObjectName("verticalLayout")
self.stackedWidget = SlidingStackedWidget(Form)
self.stackedWidget.setObjectName("stackedWidget")
self.verticalLayout.addWidget(self.stackedWidget)
self.groupBox = QtWidgets.QGroupBox(Form)
self.groupBox.setObjectName("groupBox")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.groupBox)
self.horizontalLayout.setObjectName("horizontalLayout")
self.spinBoxSpeed = QtWidgets.QSpinBox(self.groupBox)
self.spinBoxSpeed.setMinimum(100)
self.spinBoxSpeed.setMaximum(5000)
self.spinBoxSpeed.setProperty("value", 500)
self.spinBoxSpeed.setObjectName("spinBoxSpeed")
self.horizontalLayout.addWidget(self.spinBoxSpeed)
self.verticalLayout.addWidget(self.groupBox)
self.groupBox_2 = QtWidgets.QGroupBox(Form)
self.groupBox_2.setObjectName("groupBox_2")
self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.groupBox_2)
self.horizontalLayout_2.setObjectName("horizontalLayout_2")
self.radioButtonHor = QtWidgets.QRadioButton(self.groupBox_2)
self.radioButtonHor.setChecked(True)
self.radioButtonHor.setObjectName("radioButtonHor")
self.horizontalLayout_2.addWidget(self.radioButtonHor)
self.radioButtonVer = QtWidgets.QRadioButton(self.groupBox_2)
self.radioButtonVer.setObjectName("radioButtonVer")
self.horizontalLayout_2.addWidget(self.radioButtonVer)
self.verticalLayout.addWidget(self.groupBox_2)
self.groupBox_3 = QtWidgets.QGroupBox(Form)
self.groupBox_3.setObjectName("groupBox_3")
self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.groupBox_3)
self.horizontalLayout_3.setObjectName("horizontalLayout_3")
self.comboBoxEasing = QtWidgets.QComboBox(self.groupBox_3)
self.comboBoxEasing.setObjectName("comboBoxEasing")
self.horizontalLayout_3.addWidget(self.comboBoxEasing)
self.verticalLayout.addWidget(self.groupBox_3)
self.groupBox_4 = QtWidgets.QGroupBox(Form)
self.groupBox_4.setObjectName("groupBox_4")
self.horizontalLayout_4 = QtWidgets.QHBoxLayout(self.groupBox_4)
self.horizontalLayout_4.setObjectName("horizontalLayout_4")
self.pushButtonPrev = QtWidgets.QPushButton(self.groupBox_4)
self.pushButtonPrev.setObjectName("pushButtonPrev")
self.horizontalLayout_4.addWidget(self.pushButtonPrev)
self.pushButtonNext = QtWidgets.QPushButton(self.groupBox_4)
self.pushButtonNext.setObjectName("pushButtonNext")
self.horizontalLayout_4.addWidget(self.pushButtonNext)
self.verticalLayout.addWidget(self.groupBox_4)
self.groupBox_5 = QtWidgets.QGroupBox(Form)
self.groupBox_5.setObjectName("groupBox_5")
self.horizontalLayout_5 = QtWidgets.QHBoxLayout(self.groupBox_5)
self.horizontalLayout_5.setObjectName("horizontalLayout_5")
self.pushButtonStart = QtWidgets.QPushButton(self.groupBox_5)
self.pushButtonStart.setObjectName("pushButtonStart")
self.horizontalLayout_5.addWidget(self.pushButtonStart)
self.pushButtonStop = QtWidgets.QPushButton(self.groupBox_5)
self.pushButtonStop.setObjectName("pushButtonStop")
self.horizontalLayout_5.addWidget(self.pushButtonStop)
self.verticalLayout.addWidget(self.groupBox_5)
self.retranslateUi(Form)
QtCore.QMetaObject.connectSlotsByName(Form)
def retranslateUi(self, Form):
_translate = QtCore.QCoreApplication.translate
Form.setWindowTitle(_translate("Form", "图片轮播动画"))
self.groupBox.setTitle(_translate("Form", "动画速度"))
self.groupBox_2.setTitle(_translate("Form", "动画方向(默认是横向)"))
self.radioButtonHor.setText(_translate("Form", "横向"))
self.radioButtonVer.setText(_translate("Form", "纵向"))
self.groupBox_3.setTitle(_translate("Form", "动画曲线类型"))
self.groupBox_4.setTitle(_translate("Form", "翻页"))
self.pushButtonPrev.setText(_translate("Form", "上一页"))
self.pushButtonNext.setText(_translate("Form", "下一页"))
self.groupBox_5.setTitle(_translate("Form", "轮播"))
self.pushButtonStart.setText(_translate("Form", "轮播开始"))
self.pushButtonStop.setText(_translate("Form", "轮播停止"))
from SlidingStackedWidget import SlidingStackedWidget
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
Form = QtWidgets.QWidget()
ui = Ui_Form()
ui.setupUi(Form)
Form.show()
sys.exit(app.exec_())

View file

@ -0,0 +1,138 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>656</width>
<height>612</height>
</rect>
</property>
<property name="windowTitle">
<string>图片轮播动画</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0,0,0">
<item>
<widget class="SlidingStackedWidget" name="stackedWidget"/>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>动画速度</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QSpinBox" name="spinBoxSpeed">
<property name="minimum">
<number>100</number>
</property>
<property name="maximum">
<number>5000</number>
</property>
<property name="value">
<number>500</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>动画方向(默认是横向)</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QRadioButton" name="radioButtonHor">
<property name="text">
<string>横向</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="radioButtonVer">
<property name="text">
<string>纵向</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>动画曲线类型</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QComboBox" name="comboBoxEasing"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>翻页</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QPushButton" name="pushButtonPrev">
<property name="text">
<string>上一页</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonNext">
<property name="text">
<string>下一页</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_5">
<property name="title">
<string>轮播</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QPushButton" name="pushButtonStart">
<property name="text">
<string>轮播开始</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonStop">
<property name="text">
<string>轮播停止</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>SlidingStackedWidget</class>
<extends>QStackedWidget</extends>
<header location="global">SlidingStackedWidget</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,83 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Created on 2018年11月24日
author: Irony
site: https://pyqt5.com , https://github.com/892768447
email: 892768447@qq.com
file:
description:
"""
import os
from PyQt5.QtCore import QEasingCurve, Qt
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QWidget, QLabel
from UiImageSlider import Ui_Form # @UnresolvedImport
__Author__ = """By: Irony
QQ: 892768447
Email: 892768447@qq.com"""
__Copyright__ = 'Copyright (c) 2018 Irony'
__Version__ = 1.0
class ImageSliderWidget(QWidget, Ui_Form):
def __init__(self, *args, **kwargs):
super(ImageSliderWidget, self).__init__(*args, **kwargs)
self.setupUi(self)
# 初始化动画曲线类型
curve_types = [(n, c) for n, c in QEasingCurve.__dict__.items()
if isinstance(c, QEasingCurve.Type)]
curve_types.sort(key=lambda ct: ct[1])
curve_types = [c[0] for c in curve_types]
self.comboBoxEasing.addItems(curve_types)
# 绑定信号槽
self.spinBoxSpeed.valueChanged.connect(self.stackedWidget.setSpeed)
self.comboBoxEasing.currentTextChanged.connect(self.setEasing)
self.radioButtonHor.toggled.connect(self.setOrientation)
self.radioButtonVer.toggled.connect(self.setOrientation)
self.pushButtonPrev.clicked.connect(self.stackedWidget.slideInPrev)
self.pushButtonNext.clicked.connect(self.stackedWidget.slideInNext)
self.pushButtonStart.clicked.connect(self.autoStart)
self.pushButtonStop.clicked.connect(self.autoStop)
# 添加图片页面
for name in os.listdir('Images'):
label = QLabel(self.stackedWidget)
label.setScaledContents(True)
label.setPixmap(QPixmap('Images/' + name))
self.stackedWidget.addWidget(label)
def autoStart(self):
self.pushButtonNext.setEnabled(False)
self.pushButtonPrev.setEnabled(False)
self.stackedWidget.autoStart()
def autoStop(self):
self.pushButtonNext.setEnabled(True)
self.pushButtonPrev.setEnabled(True)
self.stackedWidget.autoStop()
def setEasing(self, name):
self.stackedWidget.setEasing(getattr(QEasingCurve, name))
def setOrientation(self, checked):
hor = self.sender() == self.radioButtonHor
if checked:
self.stackedWidget.setOrientation(
Qt.Horizontal if hor else Qt.Vertical)
if __name__ == '__main__':
import sys
from PyQt5.QtWidgets import QApplication
app = QApplication(sys.argv)
w = ImageSliderWidget()
w.show()
sys.exit(app.exec_())