周末玩个球,几行代码用PyQt5实现足球射门动画

简介:

QT作为一个全面的桌面应用程序开发包,其自然提供了对图像的动画支持。本篇文章中,就来简单地在PYQt5中使用Animation动画功能。

本篇将会依次完成以下功能:

 ●   在GUI界面中显示一个图片(用一个足球做演示);
 ●   点击按钮实现足球的直线射门动画;

 ●  点击按钮实现足球的曲线射门动画;

在图形界面显示图片的两种方法

一般情况下,想要在GUI中显示图片,我们会通过:

 ●   实例化一个QLable()部件;
 ●   实例化一个QPixmap()图形类;
 ●   通过QLabel()部件的setPixmap()方法设置QLabel()部件的图形;

就像如下代码所示:

 

# 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_())

运行上述代码,我们将会得到如下所示的图形界面:

eb53e5d7cebd54dd7dffbbd66c1b2dd7ded6c827

但是这种方法没有办法实现对图片更多的控制。为了更好地对图形界面中的图片进行控制和管理,我们还需要使用到其他的类,比如QtWidgets中的QGraphicsScene类,QGraphicsScene提供了一个场景,用于对2D图形进行管理。同时,为了展示QGraphicsScene中的内容,我们还需要使用到QtWidgets中的QGraphicsView类来提供一个视图部件。

下面,我们就通过一个简单的例子来了解一下QGraphicsScene类和QGraphicsView类的使用。

首先是完整的代码,如下所示:

 

# 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_())

在这里面,基础的窗口代码与之前的代码类似,有区别且最核心的为以下几行代码。

首先,我们实例化创建了一个用于展示图形场景的图形视图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)中展示图片的功能,运行完整的代码,其显示出来的图形界面程序如下图所示:

c53888426325c72fe55f6ad17bd70ec944e62e1e

几行代码射个门

上面我们通过两种不同的方式实现了图片在图形界面中的展示,接下来,我们借助QtCore中的QPropertyAnimation()来实现图片的动画效果。

QPropertyAnimation()类主要依靠操纵对象的QT属性来实现动画效果,其有几个比较主要的方法:

 ●   start():用于启动动画;
 ●   stop():用于停止动画;
 ●   setStartValue():用于设置动画的起始值;
 ●   setEndValue():用于设置动画的结束值;
 ●   setDuration():用于设置动画的持续时间;
 ●   setKeyValueAt():用于在特定时间创建一个关键的动画帧动作;
 ●   setLoopCount():用于设置动画的循环次数;

下面我们就使用QPropertyAnimation()类实现图片的动画。为了动画的效果比5毛特效要好一点,州的先生(公众号:zmister2016)在阿里的图标库iconfont里找了一个小足球和球门的图标,嗯,就像这样:

a65075db2c8ce2e5b97c271500c64ab65c9f6bf2

然后,我们创建一个图形界面,里面包含一个按钮、一个小球和一个球门的图片:

 

# 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_())

运行上述代码,我们会得到一个如下图所示的图形界面:

57b0169cfe82eafbc7096290b1e44b8b31df359a

我们的目的是想让图形界面中的小足球通过按钮控制,进入到球门中。接下来,我们就通过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_())

运行上述代码,点击“射门”按钮,我们将会得到如下动图所示的动画:

cfc37af4fbf3c86fd72024d6a0f95291552c146b

这样,通过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()

这样,我们的图形界面程序呈现出来的就是如下图所示的样子:

583c508e34bbb610473f1b78b5aad410587f17ca

图形上呈现了我们设置的足球将要运动的轨迹,接下来,我们通过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()方法上。运行程序代码,点击“射门”按钮,将会出现如下动图所示的动画效果:

3e8164c9b9ad308b913ea7b9445f2405e5064e77

为了更加的美观,其实可以将重写的paintEvent()去掉,在这里为了演示路径,就没有去除。

在PyQt5中使用动画是不是很简单?


原文发布时间为:2018-11-4

本文作者:州的先生

本文来自云栖社区合作伙伴“Python爱好者社区”,了解相关信息可以关注“Python爱好者社区”。

相关文章
|
3月前
|
文字识别 黑灰产治理
内容审核如何识别隐蔽引流?从样本构建到策略闭环
隐蔽引流识别要解决的是“正常表达背后的风险意图”。企业应把文本语义识别、联系方式检测、OCR、账号风险、行为序列和策略引擎组合起来,并在 POC 阶段准备正常样本、明确联系方式样本、暗语变体样本、评论协同样本、私信诱导样本和历史举报样本,综合评估召回率、误杀率、延迟、标签颗粒度和运营闭环能力。
|
4月前
|
SQL 人工智能 测试技术
Loop Engineering 与 Spec-Driven Development 结合下的 token 收敛
本文深度剖析Loop Engineering(循环工程)——2026年兴起的第四代AI工程范式:它并非取代Prompt/SDD,而是构建“目标→执行→验证→修正→再执行”的自主闭环,让AI自我迭代直至达标;同时直面Token爆炸痛点,结合SDD规范实现上下文收敛与成本可控。(239字)
|
3月前
|
缓存 边缘计算 云计算
lingducloud零度云:阿里云CDN海外加速怎么样?
本文由零度云特约呈现,深度评测阿里云海外CDN:聚焦亚太优势、全球节点布局、QUIC/DCDN/EdgeRoutine等硬核性能,解析其对LCP/TTFB等Core Web Vitals及Google排名的关键提升,并揭示计费陷阱与竞品对比,助出海企业提速拓客、稳占SEO先机。
|
4月前
|
编解码 监控 算法
【专利技术】一种可溯源的高效鲁棒视频水印技术
合肥高维数据与中科大联合研发新型视频水印技术,首创空间域高效嵌入+多帧鲁棒提取方案,在PSNR≈49dB(视觉无感)前提下,实现单帧50–130ms实时处理,压缩/转码/翻拍后提取准确率超90%,兼顾高效率、强鲁棒与广适配,为安防、政务、教育、广电等全场景数字内容提供可溯源版权保护。(239字)
436 2
|
安全 JavaScript 前端开发
HarmonyOS NEXT~HarmonyOS 语言仓颉:下一代分布式开发语言的技术解析与应用实践
HarmonyOS语言仓颉是华为专为HarmonyOS生态系统设计的新型编程语言,旨在解决分布式环境下的开发挑战。它以“编码创造”为理念,具备分布式原生、高性能与高效率、安全可靠三大核心特性。仓颉语言通过内置分布式能力简化跨设备开发,提供统一的编程模型和开发体验。文章从语言基础、关键特性、开发实践及未来展望四个方面剖析其技术优势,助力开发者掌握这一新兴工具,构建全场景分布式应用。
1295 35
|
设计模式 算法 开发者
「全网最细 + 实战源码案例」设计模式——策略模式
策略模式(Strategy Pattern)是一种行为型设计模式,用于定义一系列可替换的算法或行为,并将它们封装成独立的类。通过上下文持有策略对象,在运行时动态切换算法,提高代码的可维护性和扩展性。适用于需要动态切换算法、避免条件语句、经常扩展算法或保持算法独立性的场景。优点包括符合开闭原则、运行时切换算法、解耦上下文与策略实现、减少条件判断;缺点是增加类数量和策略切换成本。示例中通过定义抽象策略接口和具体策略类,结合上下文类实现动态算法选择。
686 8
「全网最细 + 实战源码案例」设计模式——策略模式
|
SQL NoSQL Java
Java 进阶:使用 Lambda 表达式实现超强的排序功能
我们在系统开发过程中,对数据排序是很常见的场景。一般来说,我们可以采用两种方式: 借助存储系统(SQL、NoSQL、NewSQL 都支持)的排序功能,查询的结果即是排好序的结果 查询结果为无序数据,在内存中排序。
2076 0
Java 进阶:使用 Lambda 表达式实现超强的排序功能
|
Windows 搜索推荐 开发者
|
数据可视化 Python
可视化 | Python绘制精美仪表盘
可视化 | Python绘制精美仪表盘

热门文章

最新文章