首页 > 其他分享 >PyQt5 使用 QStackedWidget 实现轮播展示动画(自动与手动)

PyQt5 使用 QStackedWidget 实现轮播展示动画(自动与手动)

时间:2024-09-18 16:04:31浏览次数:7  
标签:index widget 轮播 self PyQt5 next current page QStackedWidget

PyQt5 使用 QStackedWidget 实现轮播展示动画(自动与手动)

PyQt5 中,如果需要用QStackedWidget展示图片比较生硬,参考网络上的一些内容,发现用QPropertyAnimation属性动画可实现想要的效果,于是记录在这里

代码结构

本文中全部代码全在test_QStackedWidget_Animation.py这一个文件中编码,步骤中有变动的地方会注释标注,无改动的不会重复显示出来,需要看完整代码的,可直接移步到末尾。

需要注意的时,此处是用的 背景色 代替的图片,如需要,就在QStackedWidget对应页面中添加QLabel显示图片

1. 创建测试页面

添加QStackedWidget,并增加3个页面,然后动态添加几个QLabel用于显示页码,最后添加两个QPushButton按钮

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File        : test_QStackedWidget_Animation.py
@ Author      : yqbao
@ Version     : V1.0.0
@ Description : 图片轮播动画
"""
from PyQt5.QtWidgets import QApplication, QWidget, QStackedWidget, QVBoxLayout, QPushButton, QLabel
from PyQt5.QtCore import QPropertyAnimation, QEasingCurve, QRect, QEvent, Qt, QTimer


class Ui_DemoApp(object):
    def setupUi(self, DemoApp):
        DemoApp.setWindowTitle("轮播动画")
        DemoApp.resize(800, 200)
        self.stacked_widget = QStackedWidget()

        # 创建布局
        self.layout = QVBoxLayout(DemoApp)
        self.layout.addWidget(self.stacked_widget)

        # 添加页面
        page1 = QWidget()
        page1.setStyleSheet("background-color: #81bc88;")
        self.stacked_widget.addWidget(page1)
        page2 = QWidget()
        page2.setStyleSheet("background-color: #7a9dbc;")
        self.stacked_widget.addWidget(page2)
        page3 = QWidget()
        page3.setStyleSheet("background-color: #bc91a9;")
        self.stacked_widget.addWidget(page3)

        # 创建标签显示页码
        self.page_labels = [QLabel(f"{i + 1}", DemoApp) for i in range(self.stacked_widget.count())]
        for label in self.page_labels:
            label.setStyleSheet("color: gray;")  # 初始设置为暗淡的颜色
            label.setFixedSize(15, 15)
            label.setAlignment(Qt.AlignCenter)

        # 创建按钮
        self.button_next = QPushButton("下一页", DemoApp)
        self.button_prev = QPushButton("上一页", DemoApp)


class DemoApp(QWidget, Ui_DemoApp):
    def __init__(self):
        super().__init__()
        self.setupUi(self)


if __name__ == "__main__":
    import sys

    app = QApplication(sys.argv)
    demo = DemoApp()
    demo.show()
    sys.exit(app.exec_())

运行后,QLabel标签与QPushButton按钮会显示在左上角,但是不用着急,下一步就处理
image

2. 对QLabel标签与QPushButton按钮布局

这一步,需要对 QLabel标签与QPushButton按钮进行布局,具体效果就是使其悬浮在 QStackedWidget上,并且鼠标移入(进入)时才显示,移出(离开)时,隐藏,为了实现这个能力,我们需要使用eventFilterresizeEvent事件

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File        : test_QStackedWidget_Animation.py
@ Author      : yqbao
@ Version     : V1.0.0
@ Description : 图片轮播动画
"""
from PyQt5.QtWidgets import QApplication, QWidget, QStackedWidget, QVBoxLayout, QPushButton, QLabel
from PyQt5.QtCore import QPropertyAnimation, QEasingCurve, QRect, QEvent, Qt, QTimer


class Ui_DemoApp(object):
    ... # 忽略,本步骤没变化


class DemoApp(QWidget, Ui_DemoApp):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        # 隐藏按钮和标签初始状态
        self.button_next.hide()
        self.button_prev.hide()
        self.hide_current_page()

        # 安装事件过滤器到窗口
        self.installEventFilter(self)

    def show_current_page(self):
        """显示标签页"""
        for label in self.page_labels:
            label.show()

    def hide_current_page(self):
        """隐藏标签页"""
        for label in self.page_labels:
            label.hide()

    def set_geometry_current_page(self):
        """设置标签页位置"""
        for i, label in enumerate(self.page_labels):
            label.setGeometry(self.width() // 2 + i * 15 - 30, self.height() - 45, 60, 30)

    def eventFilter(self, obj, event):
        """事件过滤,鼠标移入与移出"""
        if event.type() == QEvent.Enter and obj is self:
            # 鼠标进入时显示按钮
            self.button_next.show()
            self.button_prev.show()
            self.show_current_page()
        elif event.type() == QEvent.Leave and obj is self:
            # 鼠标离开时隐藏按钮
            self.button_next.hide()
            self.button_prev.hide()
            self.hide_current_page()
        return super().eventFilter(obj, event)

    def resizeEvent(self, event):
        """更新按钮位置"""
        super().resizeEvent(event)
        self.button_next.setGeometry(self.width() - 80, self.height() // 2 - 15, 60, 30)
        self.button_prev.setGeometry(20, self.height() // 2 - 15, 60, 30)
        self.set_geometry_current_page()

image

3. 继承QStackedWidget添加属性动画方法

上两步,已经把页面准备的差不多了,接下来就是要开始添加动画,以及QPuahButton按钮槽函数了

1. 继承QStackedWidget并添加方法

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File        : test_QStackedWidget_Animation.py
@ Author      : yqbao
@ Version     : V1.0.0
@ Description : 图片轮播动画
"""
from PyQt5.QtWidgets import QApplication, QWidget, QStackedWidget, QVBoxLayout, QPushButton, QLabel
from PyQt5.QtCore import QPropertyAnimation, QEasingCurve, QRect, QEvent, Qt, QTimer


class AnimatedStackedWidget(QStackedWidget):
    def __init__(self):
        super().__init__()
        self.animation_duration = 500  # 动画持续时间(毫秒)
        self.current_animation = None  # 用于防止动画重叠

    def set_current_index_with_animation(self, index, direction='left'):
        current_index = self.currentIndex()

        if current_index == index or self.current_animation is not None:
            return

        # 获取当前页面和目标页面
        current_widget = self.currentWidget()
        next_widget = self.widget(index)

        # 确保目标页面的背景填充
        next_widget.setGeometry(self.rect())  # 使目标页面填充整个QStackedWidget
        next_widget.show()

        # 定义动画起始和结束位置
        width = self.width()
        if direction == 'left':
            current_end_pos = QRect(-width, 0, width, self.height())
            next_start_pos = QRect(width, 0, width, self.height())
        else:  # 'right'
            current_end_pos = QRect(width, 0, width, self.height())
            next_start_pos = QRect(-width, 0, width, self.height())

        # 当前页面退出动画
        self.current_animation = QPropertyAnimation(current_widget, b"geometry")
        self.current_animation.setDuration(self.animation_duration)
        self.current_animation.setStartValue(current_widget.geometry())
        self.current_animation.setEndValue(current_end_pos)
        self.current_animation.setEasingCurve(QEasingCurve.OutCubic)

        # 下个页面进入动画
        self.next_animation = QPropertyAnimation(next_widget, b"geometry")
        self.next_animation.setDuration(self.animation_duration)
        self.next_animation.setStartValue(next_start_pos)
        self.next_animation.setEndValue(self.rect())
        self.next_animation.setEasingCurve(QEasingCurve.OutCubic)

        # 动画结束时切换到目标页面
        def on_animation_finished():
            self.setCurrentIndex(index)
            current_widget.hide()
            self.current_animation = None  # 解除动画锁定

        # 连接动画结束信号到切换函数
        self.current_animation.finished.connect(on_animation_finished)

        # 启动动画
        self.current_animation.start()
        self.next_animation.start()

2. 替换stacked_widget

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File        : test_QStackedWidget_Animation.py
@ Author      : yqbao
@ Version     : V1.0.0
@ Description : 图片轮播动画
"""
class Ui_DemoApp(object):
    # self.stacked_widget = QStackedWidget()  # 把这句注释,使用我们自己的 AnimatedStackedWidget 类
    self.stacked_widget = AnimatedStackedWidget()

3. 为QPuahButton按钮添加槽函数,切换页面

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File        : test_QStackedWidget_Animation.py
@ Author      : yqbao
@ Version     : V1.0.0
@ Description : 图片轮播动画
"""
class DemoApp(QWidget, Ui_DemoApp):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        # 添加 按钮信号
        self.button_next.clicked.connect(self.next_page)
        self.button_prev.clicked.connect(self.prev_page)

    def next_page(self):
        """下一页"""
        current_index = self.stacked_widget.currentIndex()
        next_index = (current_index + 1) % self.stacked_widget.count()
        self.stacked_widget.set_current_index_with_animation(next_index, direction='left')

    def prev_page(self):
        """上一页"""
        current_index = self.stacked_widget.currentIndex()
        prev_index = (current_index - 1) % self.stacked_widget.count()
        self.stacked_widget.set_current_index_with_animation(prev_index, direction='right')

此时已经可以手动切换页面了,但是会发现标签页没有任何变化,别着急,下一步继续处理
image

4. 对QLabel标签页,当前页码进行高亮处理

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File        : test_QStackedWidget_Animation.py
@ Author      : yqbao
@ Version     : V1.0.0
@ Description : 图片轮播动画
"""
class DemoApp(QWidget, Ui_DemoApp):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        # 高亮当前页码
        self.highlight_current_page()


    def highlight_current_page(self, index=0):
        """更新标签样式"""
        for i, label in enumerate(self.page_labels):
            if i == index:
                label.setStyleSheet("color: black; font-weight: bold;")  # 高亮当前页码
            else:
                label.setStyleSheet("color: gray;")  # 暗淡其他页码

    def next_page(self):
        """下一页"""
        current_index = self.stacked_widget.currentIndex()
        next_index = (current_index + 1) % self.stacked_widget.count()
        self.stacked_widget.set_current_index_with_animation(next_index, direction='left')
        self.highlight_current_page(next_index)  # 添加了这句

    def prev_page(self):
        """上一页"""
        current_index = self.stacked_widget.currentIndex()
        prev_index = (current_index - 1) % self.stacked_widget.count()
        self.stacked_widget.set_current_index_with_animation(prev_index, direction='right')
        self.highlight_current_page(prev_index)  # 添加了这句

接下来还差什么呢?自动轮播?
image

5. 添加QTimer,定时自动切换(轮播)

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File        : test_QStackedWidget_Animation.py
@ Author      : yqbao
@ Version     : V1.0.0
@ Description : 图片轮播动画
"""
class DemoApp(QWidget, Ui_DemoApp):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        # 定时器设置
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.auto_next_page)  # 自动轮播
        self.timer.start(3000)  # 每3秒切换一次

    def auto_next_page(self):
        """自动切换到下一页"""
        self.next_page()

    def reset_timer(self):
        """重置定时器,以保持自动轮播"""
        self.timer.start(3000)

    def next_page(self):
        """下一页"""
        current_index = self.stacked_widget.currentIndex()
        next_index = (current_index + 1) % self.stacked_widget.count()
        self.stacked_widget.set_current_index_with_animation(next_index, direction='left')
        self.highlight_current_page(next_index)
        self.reset_timer()  # 重置定时器

    def prev_page(self):
        """上一页"""
        current_index = self.stacked_widget.currentIndex()
        prev_index = (current_index - 1) % self.stacked_widget.count()
        self.stacked_widget.set_current_index_with_animation(prev_index, direction='right')
        self.highlight_current_page(prev_index)
        self.reset_timer()  # 重置定时器

image

6. 完整代码

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
"""
@ File        : test_QStackedWidget_Animation.py
@ Author      : yqbao
@ Version     : V1.0.0
@ Description : 图片轮播动画
"""
from PyQt5.QtWidgets import QApplication, QWidget, QStackedWidget, QVBoxLayout, QPushButton, QLabel
from PyQt5.QtCore import QPropertyAnimation, QEasingCurve, QRect, QEvent, Qt, QTimer


class AnimatedStackedWidget(QStackedWidget):
    def __init__(self):
        super().__init__()
        self.animation_duration = 500  # 动画持续时间(毫秒)
        self.current_animation = None  # 用于防止动画重叠

    def set_current_index_with_animation(self, index, direction='left'):
        current_index = self.currentIndex()

        if current_index == index or self.current_animation is not None:
            return

        # 获取当前页面和目标页面
        current_widget = self.currentWidget()
        next_widget = self.widget(index)

        # 确保目标页面的背景填充
        next_widget.setGeometry(self.rect())  # 使目标页面填充整个QStackedWidget
        next_widget.show()

        # 定义动画起始和结束位置
        width = self.width()
        if direction == 'left':
            current_end_pos = QRect(-width, 0, width, self.height())
            next_start_pos = QRect(width, 0, width, self.height())
        else:  # 'right'
            current_end_pos = QRect(width, 0, width, self.height())
            next_start_pos = QRect(-width, 0, width, self.height())

        # 当前页面淡出动画
        self.current_animation = QPropertyAnimation(current_widget, b"geometry")
        self.current_animation.setDuration(self.animation_duration)
        self.current_animation.setStartValue(current_widget.geometry())
        self.current_animation.setEndValue(current_end_pos)
        self.current_animation.setEasingCurve(QEasingCurve.OutCubic)

        # 下个页面淡入动画
        self.next_animation = QPropertyAnimation(next_widget, b"geometry")
        self.next_animation.setDuration(self.animation_duration)
        self.next_animation.setStartValue(next_start_pos)
        self.next_animation.setEndValue(self.rect())
        self.next_animation.setEasingCurve(QEasingCurve.OutCubic)

        # 动画结束时切换到目标页面
        def on_animation_finished():
            self.setCurrentIndex(index)
            current_widget.hide()
            self.current_animation = None  # 解除动画锁定

        # 连接动画结束信号到切换函数
        self.current_animation.finished.connect(on_animation_finished)

        # 启动动画
        self.current_animation.start()
        self.next_animation.start()


class Ui_DemoApp(object):
    def setupUi(self, DemoApp):
        DemoApp.setWindowTitle("轮播动画")
        DemoApp.resize(800, 200)
        self.stacked_widget = AnimatedStackedWidget()

        # 创建布局
        self.layout = QVBoxLayout(DemoApp)
        self.layout.addWidget(self.stacked_widget)

        # 添加页面
        page1 = QWidget()
        page1.setStyleSheet("background-color: #81bc88;")
        self.stacked_widget.addWidget(page1)
        page2 = QWidget()
        page2.setStyleSheet("background-color: #7a9dbc;")
        self.stacked_widget.addWidget(page2)
        page3 = QWidget()
        page3.setStyleSheet("background-color: #bc91a9;")
        self.stacked_widget.addWidget(page3)

        # 创建标签显示页码
        self.page_labels = [QLabel(f"{i + 1}", DemoApp) for i in range(self.stacked_widget.count())]
        for label in self.page_labels:
            label.setStyleSheet("color: gray;")  # 初始设置为暗淡的颜色
            label.setFixedSize(15, 15)
            label.setAlignment(Qt.AlignCenter)

        # 创建按钮
        self.button_next = QPushButton("下一页", DemoApp)
        self.button_prev = QPushButton("上一页", DemoApp)


class DemoApp(QWidget, Ui_DemoApp):
    def __init__(self):
        super().__init__()
        self.setupUi(self)

        # 连接按钮事件
        self.button_next.clicked.connect(self.next_page)
        self.button_prev.clicked.connect(self.prev_page)

        # 隐藏按钮和标签初始状态
        self.button_next.hide()
        self.button_prev.hide()
        self.hide_current_page()

        # 高亮当前页码
        self.highlight_current_page()

        # 定时器设置
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.auto_next_page)  # 自动轮播
        self.timer.start(3000)  # 每3秒切换一次

        # 安装事件过滤器到窗口
        self.installEventFilter(self)

    def auto_next_page(self):
        """自动切换到下一页"""
        self.next_page()

    def reset_timer(self):
        """重置定时器,以保持自动轮播"""
        self.timer.start(3000)

    def show_current_page(self):
        """显示标签页"""
        for label in self.page_labels:
            label.show()

    def hide_current_page(self):
        """隐藏标签页"""
        for label in self.page_labels:
            label.hide()

    def set_geometry_current_page(self):
        """设置标签页位置"""
        for i, label in enumerate(self.page_labels):
            label.setGeometry(self.width() // 2 + i * 15 - 30, self.height() - 45, 60, 30)

    def eventFilter(self, obj, event):
        """事件过滤,鼠标移入与移出"""
        if event.type() == QEvent.Enter and obj is self:
            # 鼠标进入时显示按钮
            self.button_next.show()
            self.button_prev.show()
            self.show_current_page()
        elif event.type() == QEvent.Leave and obj is self:
            # 鼠标离开时隐藏按钮
            self.button_next.hide()
            self.button_prev.hide()
            self.hide_current_page()
        return super().eventFilter(obj, event)

    def highlight_current_page(self, index=0):
        """更新标签样式"""
        for i, label in enumerate(self.page_labels):
            if i == index:
                label.setStyleSheet("color: black; font-weight: bold;")  # 高亮当前页码
            else:
                label.setStyleSheet("color: gray;")  # 暗淡其他页码

    def next_page(self):
        """下一页"""
        current_index = self.stacked_widget.currentIndex()
        next_index = (current_index + 1) % self.stacked_widget.count()
        self.stacked_widget.set_current_index_with_animation(next_index, direction='left')
        self.highlight_current_page(next_index)
        self.reset_timer()

    def prev_page(self):
        """上一页"""
        current_index = self.stacked_widget.currentIndex()
        prev_index = (current_index - 1) % self.stacked_widget.count()
        self.stacked_widget.set_current_index_with_animation(prev_index, direction='right')
        self.highlight_current_page(prev_index)
        self.reset_timer()

    def resizeEvent(self, event):
        """更新按钮位置"""
        super().resizeEvent(event)
        self.button_next.setGeometry(self.width() - 80, self.height() // 2 - 15, 60, 30)
        self.button_prev.setGeometry(20, self.height() // 2 - 15, 60, 30)
        self.set_geometry_current_page()


if __name__ == "__main__":
    import sys

    app = QApplication(sys.argv)
    demo = DemoApp()
    demo.show()
    sys.exit(app.exec_())

本文章的原文地址
GitHub主页

标签:index,widget,轮播,self,PyQt5,next,current,page,QStackedWidget
From: https://www.cnblogs.com/yqbaowo/p/18418439

相关文章

  • PyQt5 使用 QFrame 绘制聊天(三角)气泡,并显示文字
    PyQt5使用QFrame绘制聊天(三角)气泡,并显示文字在PyQt5中,当需要想得到一个自定义的聊天气泡时,可以使用QPainter进行自定义绘制代码如下使用QPainter进行自定义绘制#!/usr/bin/envpython3#-*-coding:UTF-8-*-"""@File:test_QFrame.py@Author:......
  • PbootCMS幻灯片轮播图怎么调用
    在PBootCMS中,使用幻灯片轮播图标签可以方便地在全站任意位置调用指定分组的幻灯片图片。以下是如何使用该标签的具体步骤和示例代码。1.幻灯片轮播图列表基本用法html {pboot:slidegid=*num=*}<imgsrc="[slide:src]">{/pboot:slide}2.控制参数gid=*:分组......
  • PyQt5--打造精美、功能强大的桌面应用程序
    ui文件转换为python文件方法一:直接使用命令行转换,demo.ui为保存的ui名,demo.py为ui转换为python的文件。1python-mPyQt5.uic.pyuicdemo.ui-odemo.py QLabel案例:使用信号以下是QLabel控件的常用信号:linkActivated:当控件中包含超链接时,用户单击链接时触发此信号。......
  • 【看来我要63岁才能退休了】超简单!低耦合!一步在自己PyQt5、PySide6界面中加入文件资源
    【......
  • iOS开发----轮播图实现
    在iOS开发中,实现图片轮播的方法有很多,在这里介绍三种方法。轮播图片资源准备——SDWebImage这里我们通过第三方库SDWebImage来加载网络图片首先通过CocoaPods下载:接着,从网站上找想要轮播图片的地址,复制下来,y一般是下图这样,在代码中,使用UIImageView的扩展方法sd_setImage......
  • Pyqt5 实现计算器
    计算器是练习pyqt5的好项目界面设计简单 代码如下:importsysfromPyQt5.QtWidgetsimport*fromPyQt5.QtCoreimportQtclassWindow(QWidget):def__init__(self):super().__init__()self.display=Noneself.buttons=None......
  • Javascript应用(轮播图进阶)
    <!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>Document</title>......
  • Python Pyqt5 将ui文件转换成py文件
    命令行pyuicyour_ui_file.ui-ooutput_file.py如果是虚拟环境,则需要提前进入虚拟环境中执行pyuic命令uitopy文件的使用如果是ui文件转换过来的py文件,不要直接在此py文件中编写代码。如果你的ui文件发生变换就需要重新生成py文件,这个时候新的py文件就会覆盖历史的。正确使......
  • Swiper轮播图框架【前端 24】
    Swiper轮播图框架在如今的网页设计中,轮播图已成为一种不可或缺的元素,它能够以动态的方式展示图片、视频或文本信息,有效吸引用户的注意力并提升页面的互动性。在众多轮播图实现框架中,Swiper以其高度的可定制性、流畅的滑动效果以及丰富的API接口脱颖而出,成为前端开发者的首......
  • 鼠标悬停显示的轮播图
    今日整理,发现这种轮播图是最难实现的一种,1.再循环中难以控制单一品类商品显示解决办法:在外面的主类里面添加&:hover触发标签属性的更改,这样可以单一作用2.在循环中触发事件,所有的同一事件都会触发解决办法:先建立模版控制排版,再从单一内容开始微调<scriptsetup>impo......