> 技术文档 > PyQt5—动画控件-CSDN博客

PyQt5—动画控件-CSDN博客


第三章 动画控件


一、动画控件是什么?为啥要用它?

        简单说,动画控件就是让界面元素 “动起来” 的工具。比如按钮慢慢变大、文字渐渐显示、图片平滑移动等等。用动画能让界面更生动,用户体验更好。

        PyQt5 里最常用的动画类是 QPropertyAnimation(属性动画),它能让控件的某个 “属性”(比如大小、位置、透明度)在一段时间内慢慢变化。

二、最简单的动画:让按钮慢慢变大

先看一段超简单的代码,咱们边看边讲:

import sysfrom PyQt5.QtWidgets import QApplication, QPushButtonfrom PyQt5.QtCore import QPropertyAnimation, QRect# 1. 创建应用app = QApplication(sys.argv)# 2. 创建一个按钮btn = QPushButton(\"点我变大\")btn.resize(100, 50) # 初始大小:宽100,高50btn.show()# 3. 创建动画:让按钮的geometry属性变化# geometry属性控制控件的位置和大小(x, y, 宽, 高)animation = QPropertyAnimation(btn, b\"geometry\") # 注意:属性名要加b,表示字节串# 4. 设置动画参数animation.setDuration(1000) # 动画持续1000毫秒(1秒)# 起始状态:当前按钮的大小和位置animation.setStartValue(btn.geometry())# 结束状态:宽200,高100,位置不变(x, y和起始一样)animation.setEndValue(QRect(btn.x(), btn.y(), 200, 100))# 5. 启动动画animation.start()# 6. 运行应用sys.exit(app.exec_())

代码解读:

  • QPropertyAnimation(btn, b\"geometry\"):告诉程序 “要给 btn 控件的 geometry 属性做动画”。b\"geometry\"里的b不能少,因为 PyQt5 要求属性名是字节串。
  • setDuration(1000):动画持续时间,单位是毫秒。
  • setStartValuesetEndValue:动画的起始和结束状态。这里QRect(x, y, 宽, 高)用来表示控件的位置和大小。
  • start():启动动画。

        运行这段代码,点击按钮(其实不点也会自动动),按钮会在 1 秒内从 100x50 慢慢变成 200x100,是不是很简单?

三、常用方法:让动画更灵活

刚才用了setDurationsetStartValue这些方法,再补充几个常用的:

方法 作用 setLoopCount(n) 设置动画循环次数(n 为 - 1 时无限循环) setEasingCurve() 设置动画速度曲线(比如先快后慢、先慢后快) pause() / resume() 暂停 / 继续动画 stop() 停止动画

改改上面的例子,加个循环和速度曲线:

import sysfrom PyQt5.QtWidgets import QApplication, QPushButtonfrom PyQt5.QtCore import QPropertyAnimation, QRect, QEasingCurveapp = QApplication(sys.argv)btn = QPushButton(\"循环变大变小\")btn.resize(100, 50)btn.show()# 创建动画animation = QPropertyAnimation(btn, b\"geometry\")animation.setDuration(1000)animation.setStartValue(btn.geometry())animation.setEndValue(QRect(btn.x(), btn.y(), 200, 100))# 新增:循环2次(变大→变小→变大→变小,总共2次循环)animation.setLoopCount(2)# 新增:速度曲线:先慢后快再慢(QEasingCurve有很多预设曲线)animation.setEasingCurve(QEasingCurve.InOutQuad)animation.start()sys.exit(app.exec_())

        运行后会发现,按钮会来回变大变小 2 次,而且速度不是匀速的,更自然~

四、要实现多个控件的同步动画效果

方法一:共享同一个动画对象(适合相同属性变化)

        如果多个控件需要做完全相同的动画(比如同时变大、同时移动),可以让它们共享同一个动画对象。

示例代码:三个按钮同时变大

import sysfrom PyQt5.QtWidgets import QApplication, QPushButton, QWidget, QHBoxLayoutfrom PyQt5.QtCore import QPropertyAnimation, QRectapp = QApplication(sys.argv)window = QWidget()window.setWindowTitle(\"同步动画示例\")window.resize(400, 200)# 创建三个按钮btn1 = QPushButton(\"按钮1\")btn2 = QPushButton(\"按钮2\")btn3 = QPushButton(\"按钮3\")# 水平布局layout = QHBoxLayout(window)layout.addWidget(btn1)layout.addWidget(btn2)layout.addWidget(btn3)# 创建一个动画对象animation = QPropertyAnimation()animation.setDuration(1000)animation.setStartValue(QRect(0, 0, 80, 30)) # 初始大小animation.setEndValue(QRect(0, 0, 160, 60)) # 结束大小# 把三个按钮都添加到动画中animation.setTargetObject(btn1)animation.setPropertyName(b\"geometry\")animation.start() # 先启动btn1的动画animation.setTargetObject(btn2)animation.start() # 再启动btn2的动画(会覆盖前面的,但效果相同)animation.setTargetObject(btn3)animation.start() # 最后启动btn3的动画window.show()sys.exit(app.exec_())

代码解读:

  • 这里只创建了一个QPropertyAnimation对象,但通过多次调用setTargetObjectstart(),让三个按钮共享同一个动画参数(时长、起始值、结束值)。
  • 注意:这种方法适合所有控件的动画完全相同的情况。如果它们的动画参数(比如起始大小、时长)不同,就需要用下面的方法。

方法二:创建多个动画,同步启动(灵活控制)

        如果多个控件的动画基本相似但有细微差别(比如移动距离不同、透明度变化范围不同),可以为每个控件创建独立的动画,然后同时启动。

示例代码:三个按钮同时开始,但移动距离不同

import sysfrom PyQt5.QtWidgets import QApplication, QPushButton, QWidgetfrom PyQt5.QtCore import QPropertyAnimation, QRect, QParallelAnimationGroupapp = QApplication(sys.argv)window = QWidget()window.setWindowTitle(\"多个动画同步启动\")window.resize(400, 200)# 创建三个按钮btn1 = QPushButton(\"按钮1\", window)btn1.move(50, 50)btn2 = QPushButton(\"按钮2\", window)btn2.move(150, 50)btn3 = QPushButton(\"按钮3\", window)btn3.move(250, 50)# 创建三个独立的动画anim1 = QPropertyAnimation(btn1, b\"geometry\")anim1.setDuration(1000)anim1.setStartValue(btn1.geometry())anim1.setEndValue(QRect(50, 100, 100, 30)) # 垂直下移50像素anim2 = QPropertyAnimation(btn2, b\"geometry\")anim2.setDuration(1000)anim2.setStartValue(btn2.geometry())anim2.setEndValue(QRect(150, 120, 100, 30)) # 垂直下移70像素anim3 = QPropertyAnimation(btn3, b\"geometry\")anim3.setDuration(1000)anim3.setStartValue(btn3.geometry())anim3.setEndValue(QRect(250, 140, 100, 30)) # 垂直下移90像素# 使用并行动画组(同时执行多个动画)group = QParallelAnimationGroup()group.addAnimation(anim1)group.addAnimation(anim2)group.addAnimation(anim3)# 启动动画组group.start()window.show()sys.exit(app.exec_())

代码解读:

  • 为每个按钮创建了独立的动画,它们的移动距离不同,但时长相同。
  • QParallelAnimationGroup可以把多个动画组合在一起,调用start()时它们会同时开始。
  • 如果需要更复杂的控制(比如等待前一个动画完成再开始下一个),可以用QSequentialAnimationGroup

方法三:使用信号与槽同步(适合复杂场景)

        如果多个控件的动画需要精确同步(比如一个控件动画结束后,另一个控件立即开始),可以利用信号与槽机制。

示例代码:按钮 1 移动结束后,按钮 2 开始变色

import sysfrom PyQt5.QtWidgets import QApplication, QPushButton, QWidgetfrom PyQt5.QtCore import QPropertyAnimation, QRect, QTimerfrom PyQt5.QtGui import QColorapp = QApplication(sys.argv)window = QWidget()window.setWindowTitle(\"信号同步动画\")window.resize(400, 200)# 创建两个按钮btn1 = QPushButton(\"按钮1\", window)btn1.move(50, 50)btn2 = QPushButton(\"按钮2\", window)btn2.move(200, 50)# 按钮1的移动动画anim1 = QPropertyAnimation(btn1, b\"geometry\")anim1.setDuration(1000)anim1.setStartValue(btn1.geometry())anim1.setEndValue(QRect(50, 100, 100, 30))# 按钮2的颜色动画(使用样式表)btn2.setStyleSheet(\"background-color: white;\") # 初始白色anim2 = QPropertyAnimation(btn2, b\"styleSheet\")anim2.setDuration(1000)anim2.setStartValue(\"background-color: white;\")anim2.setEndValue(\"background-color: red;\") # 最终红色# 信号与槽:按钮1的动画结束时,启动按钮2的动画anim1.finished.connect(anim2.start)# 启动按钮1的动画(按钮2会自动在后面启动)anim1.start()window.show()sys.exit(app.exec_())

代码解读:

  • anim1.finished.connect(anim2.start) 这行代码是关键:当按钮 1 的动画结束时,会触发按钮 2 的动画开始。
  • 注意这里用了styleSheet属性来实现颜色变化,这是一种常见的 CSS 动画技巧。

方法四:使用计时器统一控制(高级技巧)

        如果需要更精确的时间控制(比如所有控件的动画都跟随同一个时间进度),可以用QTimer手动控制动画进度。

示例代码:多个控件跟随同一个计时器变化

import sysfrom PyQt5.QtWidgets import QApplication, QLabel, QWidgetfrom PyQt5.QtCore import QTimer, QPointfrom PyQt5.QtGui import QColorapp = QApplication(sys.argv)window = QWidget()window.setWindowTitle(\"计时器同步动画\")window.resize(400, 300)# 创建三个标签label1 = QLabel(\"标签1\", window)label1.move(50, 50)label1.setStyleSheet(\"background-color: red;\")label2 = QLabel(\"标签2\", window)label2.move(150, 50)label2.setStyleSheet(\"background-color: green;\")label3 = QLabel(\"标签3\", window)label3.move(250, 50)label3.setStyleSheet(\"background-color: blue;\")# 动画参数duration = 2000 # 动画总时长(毫秒)start_time = 0 # 开始时间is_animating = False# 创建计时器,每16毫秒触发一次(约60FPS)timer = QTimer()def animate(): global start_time, is_animating if not is_animating: start_time = QTimer.currentTime() is_animating = True # 计算动画进度(0.0-1.0之间) current_time = QTimer.currentTime() elapsed = current_time - start_time progress = min(elapsed / duration, 1.0) # 防止超过1.0 # 计算垂直位置(随时间从50到150像素) y = 50 + 100 * progress # 更新所有标签的位置 label1.move(50, y) label2.move(150, y) label3.move(250, y) # 更新标签大小(随时间从宽50到100像素) width = 50 + 50 * progress label1.resize(width, 30) label2.resize(width, 30) label3.resize(width, 30) # 当动画完成时停止计时器 if progress >= 1.0: timer.stop() is_animating = False# 连接计时器到动画函数timer.timeout.connect(animate)# 启动计时器timer.start(16)window.show()sys.exit(app.exec_())

代码解读:

  • 使用QTimer每 16 毫秒(约 60 帧)触发一次动画更新。
  • 通过计算时间进度(elapsed / duration),让所有控件按照相同的进度变化。
  • 这种方法的好处是可以精确控制每个控件的动画参数,适合需要复杂同步的场景。

总结:如何选择合适的方法?

  • 方法一(共享动画):最简单,适合所有控件动画完全相同的情况。
  • 方法二(并行动画组):最常用,适合动画基本相似但有细微差别的情况。
  • 方法三(信号同步):适合需要按顺序执行动画的情况。
  • 方法四(计时器):适合需要精确控制时间进度的复杂场景。

建议先从方法二(并行动画组)开始练手,这是最常用也最灵活的方法。多尝试修改代码中的参数(比如时长、结束值),观察动画效果的变化,很快就能掌握啦!

五、常用信号:动画状态变化时触发

信号就是 “事件通知”,比如动画开始、结束、暂停时,会发出对应的信号,咱们可以绑定函数来处理。

常用信号:

  • started:动画开始时触发
  • finished:动画结束时触发
  • stateChanged:动画状态改变时触发(比如从运行→暂停)

看个例子:

import sysfrom PyQt5.QtWidgets import QApplication, QPushButtonfrom PyQt5.QtCore import QPropertyAnimation, QRect, QAbstractAnimationdef animation_started(): print(\"动画开始啦!\")def animation_finished(): print(\"动画结束啦!\")app = QApplication(sys.argv)btn = QPushButton(\"点我\")btn.resize(100, 50)btn.show()animation = QPropertyAnimation(btn, b\"geometry\")animation.setDuration(1000)animation.setStartValue(btn.geometry())animation.setEndValue(QRect(btn.x(), btn.y(), 200, 100))# 绑定信号:动画状态变化时调用相应函数animation.stateChanged.connect(lambda newState, oldState: handle_state_change(newState))def handle_state_change(state): if state == QAbstractAnimation.Running: animation_started() elif state == QAbstractAnimation.Stopped: animation_finished()animation.start()sys.exit(app.exec_())

运行后,控制台会打印 “动画开始啦!” 和 “动画结束啦!”,是不是很直观?

六、实用场景 1:窗口慢慢显示(淡入效果)

有时候咱们希望窗口不是一下子弹出来,而是慢慢显示(透明度从 0 到 1),这样更优雅。

import sysfrom PyQt5.QtWidgets import QApplication, QWidgetfrom PyQt5.QtCore import QPropertyAnimation, QPointclass MyWindow(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setWindowTitle(\"淡入窗口\") self.resize(300, 200) # 初始透明度为0(完全透明) self.setWindowOpacity(0) self.show() # 创建动画:控制windowOpacity属性(透明度,0-1之间) self.animation = QPropertyAnimation(self, b\"windowOpacity\") self.animation.setDuration(2000) # 2秒 self.animation.setStartValue(0) # 完全透明 self.animation.setEndValue(1) # 完全不透明 self.animation.start()app = QApplication(sys.argv)win = MyWindow()sys.exit(app.exec_())

这里用了windowOpacity属性(透明度),从 0 到 1 渐变,窗口就慢慢显示出来了~

七、实用场景 2:图片平滑移动

比如做一个图片轮播,图片从左边滑到右边:

import sysfrom PyQt5.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayoutfrom PyQt5.QtGui import QPixmapfrom PyQt5.QtCore import QPropertyAnimation, QRectclass ImageSlider(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): self.setWindowTitle(\"图片移动\") self.resize(500, 300) # 布局 layout = QVBoxLayout() self.setLayout(layout) # 图片标签(这里用一张本地图片,记得替换成你的图片路径) self.img_label = QLabel() pixmap = QPixmap(\"test.jpg\") # 替换成你的图片路径 self.img_label.setPixmap(pixmap.scaled(200, 150)) # 缩放图片 layout.addWidget(self.img_label) # 动画:让图片的x坐标从-200(左边外面)移到150(窗口内) self.animation = QPropertyAnimation(self.img_label, b\"geometry\") self.animation.setDuration(2000) # 起始位置:x=-200(在窗口左边外面),y=50,宽200,高150 self.animation.setStartValue(QRect(-200, 50, 200, 150)) # 结束位置:x=150(窗口内),y=50,大小不变 self.animation.setEndValue(QRect(150, 50, 200, 150)) self.animation.start()app = QApplication(sys.argv)slider = ImageSlider()sys.exit(app.exec_())

运行后,图片会从左边滑进窗口,很像轮播图的效果~

八、组合动画:多个动画一起或顺序执行

有时候需要多个动画配合,比如先移动再变大。可以用QParallelAnimationGroup(同时执行)或QSequentialAnimationGroup(顺序执行)。

举个顺序执行的例子:按钮先移动,再变大:

import sysfrom PyQt5.QtWidgets import QApplication, QPushButtonfrom PyQt5.QtCore import (QPropertyAnimation, QRect, QSequentialAnimationGroup) # 顺序动画组app = QApplication(sys.argv)btn = QPushButton(\"先移再变大\")btn.setGeometry(50, 50, 100, 50) # 初始位置和大小btn.show()# 动画1:移动(x从50→200,y不变,大小不变)anim1 = QPropertyAnimation(btn, b\"geometry\")anim1.setDuration(1000)anim1.setStartValue(btn.geometry())anim1.setEndValue(QRect(200, 50, 100, 50)) # x变了,其他不变# 动画2:变大(大小从100x50→200x100,位置不变)anim2 = QPropertyAnimation(btn, b\"geometry\")anim2.setDuration(1000)anim2.setStartValue(QRect(200, 50, 100, 50)) # 承接anim1的结束位置anim2.setEndValue(QRect(200, 50, 200, 100))# 创建顺序动画组:先执行anim1,再执行anim2group = QSequentialAnimationGroup()group.addAnimation(anim1)group.addAnimation(anim2)group.start()sys.exit(app.exec_())

如果把QSequentialAnimationGroup换成QParallelAnimationGroup,两个动画就会同时执行~

九、总结

  1. 最基础的QPropertyAnimation用法:控制控件属性(大小、位置、透明度等)随时间变化。
  2. 常用方法:setDuration(时长)、setLoopCount(循环)、setEasingCurve(速度曲线)。
  3. 常用信号:started(开始)、finished(结束),可以绑定函数做后续处理。
  4. 实用场景:按钮变大、窗口淡入、图片移动、组合动画。

        这些都是最常用的动画技巧,记住 “属性动画” 的核心就是 “让控件的某个属性在一段时间内从一个值变到另一个值”。多动手改改代码里的参数(比如时长、结束值),看看效果变化,很快就能熟练啦~