PyQt5桌面应用开发(15):界面动画

news2024/10/6 0:32:31

本文目录

  • PyQt5桌面应用系列
  • 界面动画
  • PyQt5的动画框架
    • QPropertyAnimation
    • QAnimationGroup
    • pyqtProperty与插值
  • 一个例子
    • 代码
    • 代码解析
  • 结论

PyQt5桌面应用系列

  • PyQt5桌面应用开发(1):需求分析
  • PyQt5桌面应用开发(2):事件循环
  • PyQt5桌面应用开发(3):并行设计
  • PyQt5桌面应用开发(4):界面设计
  • PyQt5桌面应用开发(5):对话框
  • PyQt5桌面应用开发(6):文件对话框
  • PyQt5桌面应用开发(7):文本编辑+语法高亮与行号
  • PyQt5桌面应用开发(8):从QInputDialog转进到函数参数传递
  • PyQt5桌面应用开发(9):经典布局QMainWindow
  • PyQt5桌面应用开发(10):界面布局基本支持
  • PyQt5桌面应用开发(11):摸鱼也要讲基本法,两个字,16
  • PyQt5桌面应用开发(12):QFile与线程安全
  • PyQt5桌面应用开发(13):QGraphicsView框架
  • PyQt5桌面应用开发(14):数据库+ModelView+QCharts
  • PyQt5桌面应用开发(15):界面动画

界面动画

从所受教育和时间体验而言,我对界面动画没有什么感觉、也不太喜欢。但是业界老大告诉我,能用动画就用动画,不能用动画的用图片,不能用图片的用条目文字,最后的选择才是公式。

其实要看对象是谁。如果设计的软件用户,其注意力非常需要被吸引,而且他们的注意力是非常有限的,所以动画是非常有必要的。如果是开发者,那么动画就是浪费时间,因为他们的注意力是非常集中的,而且他们的注意力是可以被控制的,所以动画是非常不必要的。

好的,人身攻击的话就到此为止。我们下面来看看动画的实现,秃子嘛,除了coding什么都不会,那就好好coding,哪些伤身体的事情(泡吧、喝酒……)就交给别人去做吧。

PyQt5的动画框架

PyQt5的动画框架是QAbstractAnimation,它是一个抽象类,不能直接使用,需要使用它的子类。它的类结构如下:

  • QAbstractAnimation:抽象动画,是所有动画的基类,不能直接使用。
    • QVariantAnimation:值动画,用于改变控件的属性,比如改变控件的位置、大小、颜色等。
      • QPropertyAnimation:属性动画,用于改变控件的属性,比如改变控件的位置、大小、颜色等。
    • QAnimationGroup:动画组,可以包含多个动画,可以包含子动画组。
      • QSequentialAnimationGroup:顺序动画组,按照添加的顺序依次执行动画。
      • QParallelAnimationGroup:并行动画组,所有动画一起执行。

我们常用的,就是三个子类。

QPropertyAnimation

这个类的作用就是在一个Qt属性上定义一段动画。比如,我们可以在一个按钮上定义一个动画,让它的位置从(0, 0)移动到(100, 100)
。那么这里的属性就是按钮的位置,动画的起始值是(0, 0),结束值是(100, 100)
。这个属性在PyQt5里面定义的方式是采用@pyqtProperty(type signiture)@property_name.setter
函数修饰语法。前者定义了这个属性的取值和名称(就是python函数的名称),后者定义了赋值函数。采用这个方法唯一的要求,就是这个对象是QObject的子类。对于哪些不是QObject的对象,我们要额外定义一个QObject的子类,然后通过某种方式在赋值函数中将这个值传递给我们的对象,去更新对象的状态。

当我们定义好了属性之后,我们就可以使用QPropertyAnimation来定义动画了。

from PyQt5.QtCore import QPropertyAnimation, QRect
from PyQt5.QtWidgets import QPushButton

but = QPushButton("Animation")
animation = QPropertyAnimation(but, b'geometry', parent=None)

animation.setStartValue(QRect(0, 0, 100, 30))
animation.setEndValue(QRect(250, 250, 100, 30))
animation.setDuration(3000)
animation.start()

这里唯一需要注意的是,在定义中,那个属性的名称必须是Union[QByteArray, bytes, bytearray]
,实际上是ASCII字符串,在Python中,可以用b'geometry'
来表示。或者把字符串转换成ASCII码,比如bytes('geometry', encoding='utf-8')'geometry'.encode('utf-8')这类。

QAnimationGroup

这个类的两个子类,一个是QSequentialAnimationGroup,一个是QParallelAnimationGroup。前者是顺序执行动画,后者是并行执行动画。这两个类的使用方法是一样的,只是执行的方式不同。这两个类本身也是QAbstractAnimation(的子类),所以可以相互组合,形成比较复杂的动画。

比如有一系列动画,用QSequentialAnimationGroup来执行,那么这些动画就是顺序执行的,所有这些动画共享一个背景动画,那么可以把每个动画和背景动画用QParallelAnimationGroup来执行,然后把并行动画加入到串行动画里。

pyqtProperty与插值

其实定义Qt Property在Python里面非常简单,只需要使用@pyqtProperty(type signiture)@property_name.setter
。其实好玩的是,这个插值的过程。

QVariantAnimation大概是这样处理插值过程的,用一个QVariant来表达各种值,然后提供一个注册插值函数的接口,每种具体的类型,调用插值函数,就可以得到插值的结果。这个插值函数的接口是这样的:

void QVariantAnimation::registerInterpolator(QVariantAnimation::Interpolator func, int interpolationType)
{
    // will override any existing interpolators
    QInterpolatorVector *interpolators = registeredInterpolators();
    // When built on solaris with GCC, the destructors can be called
    // in such an order that we get here with interpolators == NULL,
    // to continue causes the app to crash on exit with a SEGV
    if (interpolators) {
        const auto locker = qt_scoped_lock(registeredInterpolatorsMutex);
        if (interpolationType >= interpolators->size())
            interpolators->resize(interpolationType + 1);
        interpolators->replace(interpolationType, func);
    }
}

当前,Qt内置了一些类的插值函数:

  • Int
  • UInt
  • Double
  • Float
  • QLine
  • QLineF
  • QPoint
  • QPointF
  • QSize
  • QSizeF
  • QColor
  • QRectF
  • QRect

如果你需要插值其他的类型,包括自定义类型,你必须自己实现插值。你可以注册一个插值函数,这个函数有三个参数:起始值,结束值,当前的进度。

最后就是一个动画的播放速度的,这个在Qt中称为QEasyCurve,它定义了在动画的播放过程中,时间和进度的关系。Qt内置了一些常用的曲线,比如:

  • Linear:线性
  • InQuad:初始点附近二次方
  • OutQuad:结束点二次方
  • InOutQuad:二次方

当然,还可以自己定义曲线,并注册到Qt中。

一个例子

我们来看一个例子,这个例子是一个动画的组合,包括一个顺序动画组和一个并行动画组。这个例子的代码如下。

在这里插入图片描述

代码

import sys

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *


class ParentBackgroundAnimation(QObject):

    def __init__(self, parent: QWidget = None):
        super(ParentBackgroundAnimation, self).__init__(parent)
        self._color = QColor(Qt.transparent)

    @pyqtProperty(QColor)
    def color(self):
        return self._color

    @color.setter
    def color(self, color):
        self._color = color
        self.update()

    def update(self):
        if self.parent is not None:
            win: QWidget = self.parent()
            win.setPalette(QPalette(self._color))


def make_animation_button(win: QWidget, layout: QLayout, animation_group: QAnimationGroup, params):
    x0, y0, w0, h0, x1, y1, w1, h1, curve, label, time_ms = params
    button = QPushButton(label, win)
    if layout is not None:
        layout.addWidget(button)

    animation = QPropertyAnimation(button, b"geometry")
    animation.setDuration(time_ms)
    animation.setStartValue(QRect(x0, y0, w0, h0))
    animation.setEndValue(QRect(x1, y1, w1, h1))

    animation.setEasingCurve(curve)

    button.clicked.connect(animation_group.start)

    animation_group.addAnimation(animation)

    return button, animation


def color_animation(win, animation_group):
    ti = ParentBackgroundAnimation(win)

    animation = QPropertyAnimation(ti, b"color")
    animation.setDuration(2000)
    animation.setStartValue(QColor(255, 0, 0, 100))
    animation.setEndValue(QColor(0, 255, 0, 100))

    animation_group.addAnimation(animation)


if __name__ == "__main__":
    app = QApplication([])

    win = QWidget()

    layout = None

    animation_group = QSequentialAnimationGroup()

    curves = [QEasingCurve.Linear, QEasingCurve.OutQuint, QEasingCurve.OutBounce, QEasingCurve.OutElastic,
              QEasingCurve.OutBack, QEasingCurve.OutExpo]
    curve_labels = ["Linear", "OutQuint", "OutBounce", "OutElastic", "OutBack", "OutExpo"]

    for i, (c, l) in enumerate(zip(curves, curve_labels)):
        sub_animation_group = QParallelAnimationGroup()
        make_animation_button(win, layout, sub_animation_group, (
            100 + i * 200, 10, 150, 80, 100 + i * 200, 600, 150, 80, c, l, 2000
        ))
        color_animation(win, sub_animation_group)
        animation_group.addAnimation(sub_animation_group)

    animation_group.start()

    win.setWindowTitle("Animate buttons.")

    win.resize(1400, 700)

    win.show()

    sys.exit(app.exec_())

代码解析

首先是一个自定义的动画类,这个类的作用是改变父窗口的背景颜色。

class ParentBackgroundAnimation(QObject):

    def __init__(self, parent: QWidget = None):
        super(ParentBackgroundAnimation, self).__init__(parent)
        self._color = QColor(Qt.transparent)

    @pyqtProperty(QColor)
    def color(self):
        return self._color

    @color.setter
    def color(self, color):
        self._color = color
        self.update()

    def update(self):
        if self.parent is not None:
            win: QWidget = self.parent()
            win.setPalette(QPalette(self._color))

这里面有一个update函数,这个函数的作用是更新父窗口的背景颜色。注意QWdiget的背景颜色是通过QPalette来设置的。

然后是一个函数,用来创建一个按钮和一个动画,最后是将这个动画添加到动画组里面。

结论

  1. QPropertyAnimation是一个用来改变属性的动画类。
  2. QPropertyAnimation的属性必须是QObject的子类。
  3. QPropertyAnimation的属性必须是QVariant类型。
  4. QPropertyAnimation的属性必须有读写的函数。
  5. 动画组同样也是动画,并行和串行可以相互嵌套,构成复杂的动画。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/539952.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【手机建站】安卓Termux+cpolar内网穿透,搭建外网可以访问的网站

文章目录 概述1.搭建apache2.安装cpolar内网穿透3.公网访问配置4.固定公网地址5.添加站点 概述 Termux是一个Android终端仿真应用程序,用于在 Android 手机上搭建一个完整的Linux 环境,能够实现Linux下的许多基本操作,不需要root权限Termux就…

电脑格式化后数据恢复软件EasyRecovery16

EasyRecovery是一款由Kroll Ontrack公司开发的专业数据恢复软件,旨在帮助用户从各种数据丢失情况下恢复文件。无论是因为误删除、格式化、分区丢失、系统崩溃还是其他原因导致的数据丢失,EasyRecovery都具有强大的恢复功能。 EasyRecovery提供了多种恢复…

什么是 Git 的 cherry-pick?

官方解析 Git 的 cherry-pick 是一种将指定的提交(commit)应用到当前分支的操作。它可以帮助我们将某个分支上的某次提交复制到另一个分支上,而无需将整个分支合并过来。 通常,我们在使用 Git 进行版本控制时,会在不…

JAVA-Activiti 7与达梦、人大金仓兼容-修改源码包(1)

目录 第一步,下载源码包 第二步,修改源码内容 1.1进行部分源码包修改 1.1.1 在org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl,增加成员变量。 1.1.2 修改org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl 类的getDefaultDatabase…

UML类图画法及其关系

UML类图画法及其关系 本文主要是介绍 UML类图画法及其关系,方便今后温习!!! 一、类之间的关系汇总 泛化(Generalization)实现(Realization)关联(Association&#xff…

基于springboot+jsp的校园音乐网站系统

该系统能完成的主要功能分为管理员和用户两个用户角色。主要功能包括主页、个人中心、用户管理、校园歌手管理、明星歌手管理、歌曲类别管理、校园歌曲管理、歌曲MV管理、歌手歌曲管理、系统管理等。而用户登入系统也可以对自己的信息以及修改个人资料进行查看等功能。该系统在…

YOLO-NAS C++部署 2023.5.17

这不最近新出了网络,YOLO-NAS,听过性能和速度都不错,而且int8量化后效果也不错。 一、吐槽 但是我打开该项目阅读readme.txt时候,发现这些示例网站一个都打不开! 而且readme.txt很不详细,你想训练自己的模…

设计模式之【访问者模式】,动态双分派的魅力

文章目录 一、什么是访问者模式1、访问者模式的应用场景2、访问者模式的五大角色3、访问者模式的优缺点 二、实例1、访问者模式的通用写法2、宠物喂食实例3、KPI考核案例小总结 三、分派1、什么是分派2、静态分派3、动态分派4、双分派5、访问者模式中的伪动态双分派 四、访问者…

mkv转mp4格式怎么转,5种便捷工具盘点

mkv转mp4格式怎么转?因为当我们下载视频时,通常无法选择格式,这可能会导致下载的视频无法打开。如果下载的是MKV格式,它可以容纳多个音频、视频和字幕流。然而,并非所有播放器都支持MKV格式的视频文件。尽管MKV是常见的…

2D车道线检测算法总结

关于2D车道线检测算法的总结主要分为两类:一类基于语义分割来做,一类基于anchor和关键点来做。还有基于曲线方程来做的,但是落地的话还是上面两种为主。 一、基于语义分割的车道线检测算法 1.LaneNet 论文创新点: 1.将车道线检…

【软考数据库】第十五章 知识产权和标准化

目录 15.1 知识产权概述 15.2 保护期限 15.3 知识产权人的确定 15.4 侵权判断 15.5 标准划分 前言: 笔记来自《文老师软考数据库》教材精讲,精讲视频在b站,某宝都可以找到,个人感觉通俗易懂。 15.1 知识产权概述 知识产权是…

RN_iOS项目部署流程实例

文章目录 1、环境配置1.1 安装node1.2 安装Watchman1.3 安装npm1.4 安装cocoapods 2、百家云demo下载3、运行百家云demo3.1 顺利的话3.2 踩过的坑(按这个目录流程走)3.2.1 npm install -g react-native-cli3.2.2 安装:npm install3.2.3 npm降…

新一代智能柔性换层跨巷道多车调度的HEGERLS托盘四向穿梭车物流解决方案

随着电子商务和智能制造技术的快速发展,对自动化仓储、密集仓储、自动搬运系统、自动识别、无线通信等多系统集成的需求也在不断增加,物流设备系统的密集化、自动化、智能化等技术也在不断完善。密集存储技术的优势是空间可用性高、运行模式高效、工作人…

Docker-Compose 入门到实战详尽笔记

本文首发自「慕课网」(www.imooc.com),想了解更多IT干货内容,程序员圈内热闻,欢迎关注"慕课网"或慕课网公众号! 作者:暮闲 | 慕课网讲师 使用过 Docker 的小伙伴们都知道&#xff0…

职场小白如何在工作中快速的升职加薪

缘起 近来连续两个季度很轻松的获得优秀,在这轻松的背后,一定有些原因支撑这领导给了这个评价。坦白说,最近两个季度,无一天加班,因为我们团队不提倡加班;我这边离领导较远,属于两个城市异地办…

一天吃透Java面试八股文

Java的特点 Java是一门面向对象的编程语言。面向对象和面向过程的区别参考下一个问题。 Java具有平台独立性和移植性。 Java有一句口号:Write once, run anywhere,一次编写、到处运行。这也是Java的魅力所在。而实现这种特性的正是Java虚拟机JVM。已编…

chatgpt赋能Python-pyhton如何安装

Python的安装方法 Python是一种高级编程语言,适用于多种开发需求,从网站构建到机器学习。其易用和灵活的语法使其成为一种非常受欢迎的编程语言。本文将向您介绍如何安装Python。 Python的安装步骤 以下是安装Python的步骤。 步骤1:下载P…

[笔记]初识Burpsuit

文章目录 前言一、安装配置1.1 环境1.2 安装过程1.3 科技过程 二、常用功能2.1 Manual penetration testing features2.2 Advanced/custom automated attacks2.3 Automated scanning for vulnerabilities2.4 Productivity tools2.5 Extensions 三、拓展功能 前言 Burp Suite(b…

【C++】 制作游戏壳

目录 前言 GameFrame游戏壳 搭建游戏壳 游戏初始化 游戏重绘 游戏运行 用回调函数实现游戏运行 关闭窗口,退出程序 测试 增加子类继承游戏壳子 继承 多态 优化 测试 总结 使用方法 常见错误 完整代码 GameFrame.h main.cpp 前言 为了方便以后制…

数据存储梳理记录

目录 1、FMDB-第三方SQLite数据库框架1.1 现状1.2 线程安全问题1.2.1 FMDatabase1.2.2 FMDatabaseQueue1.2.3 FMDatabasePool 2、进程间通信2.1 URL Scheme2.2 keyChain2.3 UIPasteboard2.4 UIDocumentInteractionController2.5 local socket2.6 AirDrop2.7 UIActivityViewCont…