QT做为一个全面的桌面应用程序开发包,其天然提供了对图像的动画支持。本篇文章中,就来简单地在PYQt5中使用Animation动画功能实现一个足球射门的动画效果。html
本篇将会依次完成如下功能:app
本文首发州的先生博客: Python GUI教程(十五):在PyQt5中使用动画,转载请注明出处
通常状况下,想要在GUI中显示图片,咱们会经过:oop
就像以下代码所示:动画
# coding:utf-8 from PyQt5 import QtGui,QtWidgets import sys class MainUi(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("动画使用-zmister.com") # 设置窗口标题 self.resize(400,200) # 规定窗口大小 self.main_widget = QtWidgets.QWidget() # 建立一个widget部件 self.label = QtWidgets.QLabel(self.main_widget) # 建立一个文本标签部件 png = QtGui.QPixmap() # 建立一个绘图类 png.load("logo.png") # 从pngz中加载一个图片 self.label.setPixmap(png) # 设置文本标签的图形 self.setCentralWidget(self.main_widget) if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) gui = MainUi() gui.show() sys.exit(app.exec_())
运行上述代码,咱们将会获得以下所示的图形界面:ui
可是这种方法没有办法实现对图片更多的控制。为了更好地对图形界面中的图片进行控制和管理,咱们还须要使用到其余的类,好比QtWidgets中的QGraphicsScene类,QGraphicsScene提供了一个场景,用于对2D图形进行管理。同时,为了展现QGraphicsScene中的内容,咱们还须要使用到QtWidgets中的QGraphicsView类来提供一个视图部件。spa
下面,咱们就经过一个简单的例子来了解一下QGraphicsScene类和QGraphicsView类的使用。3d
首先是完整的代码,以下所示:code
# coding:utf-8 from PyQt5 import QtGui,QtWidgets,QtCore import sys class MainUi(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("动画使用-zmister.com") # 设置窗口标题 self.resize(400,200) # 规定窗口大小 self.main_widget = QtWidgets.QWidget() # 建立一个widget部件 self.grapview = QtWidgets.QGraphicsView(self.main_widget) # 建立一个图形视图,继承自main_widget self.grapview.setGeometry(QtCore.QRect(10, 10, 380, 180)) # 设置图形视图的矩形区域 self.scene = QtWidgets.QGraphicsScene() # 建立一个图形管理场景 self.grapview.setScene(self.scene) png = QtGui.QPixmap() # 建立一个绘图类 png.load("logo.png") # 从png中加载一个图片 item = QtWidgets.QGraphicsPixmapItem(png) # self.scene.addItem(item) self.setCentralWidget(self.main_widget) if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) gui = MainUi() gui.show() sys.exit(app.exec_())
在这里面,基础的窗口代码与以前的代码相似,有区别且最核心的为如下几行代码。htm
首先,咱们实例化建立了一个用于展现图形场景的图形视图QGraphicsView(),将它继承自self.main_widget主窗口部件:对象
self.grapview = QtWidgets.QGraphicsView(self.main_widget)
而后,咱们实例化建立了一个图形场景管理类QGraphicsScene(),并经过setScene()方法将图形视图self.grapview的图形场景设置为刚刚实例化建立的QGraphicsScene():
self.scene = QtWidgets.QGraphicsScene() self.grapview.setScene(self.scene)
最后,咱们经过QPixmap()建立并加载了一个图片,将其添加到图形项目QGraphicsPixmapItem()中,并经过addItem()方法将图形项目添加到图形场景管理self.scene中:
png = QtGui.QPixmap() png.load("logo.png") item = QtWidgets.QGraphicsPixmapItem(png) self.scene.addItem(item)
如此,咱们就完成了经过QGraphicsView()类和QGraphicsScene()类在图形界面(GUI)中展现图片的功能,运行完整的代码,其显示出来的图形界面程序以下图所示:
上面咱们经过两种不一样的方式实现了图片在图形界面中的展现,接下来,咱们借助QtCore中的QPropertyAnimation()来实现图片的动画效果。
QPropertyAnimation()类主要依靠操纵对象的QT属性来实现动画效果,其有几个比较主要的方法:
下面咱们就使用QPropertyAnimation()类实现图片的动画。为了动画的效果比5毛特效要好一点,州的先生(公众号:zmister2016)在阿里的图标库iconfont里找了一个小足球和球门的图标,嗯,就像这样:
而后,咱们建立一个图形界面,里面包含一个按钮、一个小球和一个球门的图片:
# coding:utf-8 from PyQt5 import QtGui,QtWidgets,QtCore import sys class MainUi(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("动画使用-zmister.com") # 设置窗口标题 self.resize(400,200) # 规定窗口大小 self.main_widget = QtWidgets.QWidget() # 建立一个widget部件 self.button = QtWidgets.QPushButton('射门',self.main_widget) # 建立一个按钮 self.button.setGeometry(10,10,60,30) # 设置按钮位置 self.label = QtWidgets.QLabel(self.main_widget) # 建立一个文本标签部件用于显示足球 self.label.setGeometry(50,80,50,50) # 设置足球位置 png = QtGui.QPixmap() # 建立一个绘图类 png.load("football.png") # 从png中加载一个图片 self.label.setPixmap(png) # 设置文本标签的图形 self.label.setScaledContents(True) self.qiumen = QtWidgets.QLabel(self.main_widget) # 建立一个文本标签部件用于显示球门 self.qiumen.setGeometry(345, 75, 50, 50) # 设置球门位置 pngqiumen = QtGui.QPixmap() # 建立一个绘图类 pngqiumen.load("qiumen.png") # 从png中加载一个图片 self.qiumen.setPixmap(pngqiumen) # 设置文本标签的图形 self.setCentralWidget(self.main_widget) if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) gui = MainUi() gui.show() sys.exit(app.exec_())
运行上述代码,咱们会获得一个以下图所示的图形界面:
咱们的目的是想让图形界面中的小足球经过按钮控制,进入到球门中。接下来,咱们就经过QPropertyAnimation()类来实现这个效果。
在MainUi()中新建一个名为shoot()的方法,在其中写入如下代码:
self.anim = QtCore.QPropertyAnimation(self.label,b'geometry') # 设置动画的对象及其属性 self.anim.setDuration(2000) # 设置动画间隔时间 self.anim.setStartValue(QtCore.QRect(50,80,50,50)) # 设置动画对象的起始属性 self.anim.setEndValue(QtCore.QRect(360, 90, 10, 10)) # 设置动画对象的结束属性 self.anim.start() # 启动动画
最后,咱们再讲按钮的点击信号绑定到shoot()方法上:
self.button.clicked.connect(self.shoot)
完整的代码以下所示:
# coding:utf-8 from PyQt5 import QtGui,QtWidgets,QtCore import sys class MainUi(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("动画使用-zmister.com") # 设置窗口标题 self.resize(400,200) # 规定窗口大小 self.main_widget = QtWidgets.QWidget() # 建立一个widget部件 self.button = QtWidgets.QPushButton('射门',self.main_widget) # 建立一个按钮 self.button.setGeometry(10,10,60,30) # 设置按钮位置 self.button.clicked.connect(self.shoot) self.label = QtWidgets.QLabel(self.main_widget) # 建立一个文本标签部件用于显示足球 self.label.setGeometry(50,80,50,50) # 设置足球位置 png = QtGui.QPixmap() # 建立一个绘图类 png.load("football.png") # 从png中加载一个图片 self.label.setPixmap(png) # 设置文本标签的图形 self.label.setScaledContents(True) # 图片随文本部件的大小变更 self.qiumen = QtWidgets.QLabel(self.main_widget) # 建立一个文本标签部件用于显示球门 self.qiumen.setGeometry(345, 75, 50, 50) # 设置球门位置 pngqiumen = QtGui.QPixmap() # 建立一个绘图类 pngqiumen.load("qiumen.png") # 从png中加载一个图片 self.qiumen.setPixmap(pngqiumen) # 设置文本标签的图形 self.setCentralWidget(self.main_widget) def shoot(self): self.anim = QtCore.QPropertyAnimation(self.label,b'geometry') # 设置动画的对象及其属性 self.anim.setDuration(2000) # 设置动画间隔时间 self.anim.setStartValue(QtCore.QRect(50,80,50,50)) # 设置动画对象的起始属性 self.anim.setEndValue(QtCore.QRect(360, 90, 10, 10)) # 设置动画对象的结束属性 self.anim.start() # 启动动画 if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) gui = MainUi() gui.show() sys.exit(app.exec_())
运行上述代码,点击“射门”按钮,咱们将会获得以下动图所示的动画:
这样,经过QPropertyAnimation()的setDuration()方法、setStartValue()方法、setEndValue()方法咱们就实现了一个简单的动画。
可是上面的射门动画是一条直线将小足球移动到了球门以内,简单粗暴欠缺了些许美感,下面,咱们让这个射门换一种方式,用圆月弯刀的香蕉球将小足球射入球门。
还记得上面咱们提过QPropertyAnimation()的setKeyValueAt()这个用于设置动画关键帧的方法。如今咱们就将利用它来实现足球射门时的曲线。
与上面的图形界面的代码不同的是,咱们须要绘制一条曲线线条来做为足球射门时的路径。因此咱们须要对上面的图形界面的代码进行一些修改:
# coding:utf-8 from PyQt5 import QtGui,QtWidgets,QtCore import sys class MainUi(QtWidgets.QMainWindow): def __init__(self): super().__init__() self.init_ui() def init_ui(self): self.setWindowTitle("动画使用-州的先生zmister.com") # 设置窗口标题 self.resize(400,200) # 规定窗口大小 self.main_widget = QtWidgets.QWidget() # 建立一个widget部件 self.button = QtWidgets.QPushButton('射门',self.main_widget) # 建立一个按钮 self.button.setGeometry(10,10,60,30) # 设置按钮位置 self.label = QtWidgets.QLabel(self.main_widget) # 建立一个文本标签部件用于显示足球 self.label.setGeometry(50,150,50,50) # 设置足球位置 png = QtGui.QPixmap() # 建立一个绘图类 png.load("football.png") # 从png中加载一个图片 self.label.setPixmap(png) # 设置文本标签的图形 self.label.setScaledContents(True) # 图片随文本部件的大小变更 self.qiumen = QtWidgets.QLabel(self.main_widget) # 建立一个文本标签部件用于显示球门 self.qiumen.setGeometry(345, 75, 50, 50) # 设置球门位置 pngqiumen = QtGui.QPixmap() # 建立一个绘图类 pngqiumen.load("qiumen.png") # 从png中加载一个图片 self.qiumen.setPixmap(pngqiumen) # 设置文本标签的图形 self.path = QtGui.QPainterPath() # 实例化一个绘制类,用于绘制动做 self.path.moveTo(50, 150) self.path.cubicTo(50, 150, 50, 20, 370, 90) self.setCentralWidget(self.main_widget) # 重写patintEvent()方法 def paintEvent(self, e): qp = QtGui.QPainter() qp.begin(self) qp.drawPath(self.path) # 在图形界面上根据self.path绘制一条线条 qp.end() if __name__ == '__main__': app = QtWidgets.QApplication(sys.argv) gui = MainUi() gui.show() sys.exit(app.exec_())
在上面的代码中,与以前的程序代码有一下不一样之处:
咱们实例化建立了一个QtGui.QPainterPath(),用于进行绘制操做。经过它的moveTo()方法,设置了绘制的起始点,经过它的cubicTo()方法,设置的绘制的整个路径:
self.path = QtGui.QPainterPath() # 实例化一个绘制类,用于绘制动做 self.path.moveTo(50, 150) self.path.cubicTo(50, 150, 50, 20, 370, 90)
接着,咱们重写了窗口的paintEvent()方法,根据绘制操做的定义,在图形上绘制一条相应的线条:
def paintEvent(self, e): qp = QtGui.QPainter() qp.begin(self) qp.drawPath(self.path) # 在图形界面上根据self.path绘制一条线条 qp.end()
这样,咱们的图形界面程序呈现出来的就是以下图所示的样子:
图形上呈现了咱们设置的足球将要运动的轨迹,接下来,咱们经过QPropertyAnimation()的setKeyValueAt()设置关键帧的路径,来实现足球曲线射门,具体操做一样在shoot()方法中进行:
def shoot(self): self.anim_x = QtCore.QPropertyAnimation(self.label, b'geometry') self.anim_x.setDuration(3000) self.anim_x.setStartValue(QtCore.QRect(50,150,50,50)) # 设置动画对象的起始属性 positionValues = [n / 10 for n in range(0, 10)] for n,i in enumerate(positionValues): x = self.path.pointAtPercent(i).x() y = self.path.pointAtPercent(i).y() z = 50 - n*3.5 self.anim_x.setKeyValueAt(i,QtCore.QRect(x, y,z,z)) self.anim_x.setEndValue(QtCore.QRect(360, 90, 10, 10)) self.anim_x.start()
最后,一样将“射门”按钮的点击信号绑定在shoot()方法上。运行程序代码,点击“射门”按钮,将会出现以下动图所示的动画效果:
为了更加的美观,其实能够将重写的paintEvent()去掉,在这里为了演示路径,就没有去除。
在PyQt5中使用动画是否是很简单?有问题欢迎留言讨论。