代码也浪漫:用 Python 放一场烟花秀!

代码也浪漫:用 Python 放一场烟花秀!,第1张

概述天天敲代码的朋友,有没有想过代码也可以变得很酷炫又浪漫?今天就教大家用Python模拟出绽放的烟花,工作之余也可以随时让程序为自己放一场烟花秀。

天天敲代码的朋友,有没有想过代码也可以变得很酷炫又浪漫?今天就教大家用Python模拟出绽放的烟花,工作之余也可以随时让程序为自己放一场烟花秀。

这个有趣的小项目并不复杂,只需一点可视化技巧,100余行Python代码和程序库Tkinter,最后我们就能达到下面这个效果:

进群:960410445   即可获取数十套pdf!

学完本教程后,你也能做出这样的烟花秀。

整体概念梳理

我们的整个理念比较简单。

如上图示,我们这里通过让画面上一个粒子分裂为X数量的粒子来模拟爆炸效果。粒子会发生“膨胀”,意思是它们会以恒速移动且相互之间的角度相等。这样就能让我们以一个向外膨胀的圆圈形式模拟出烟花绽放的画面。经过一定时间后,粒子会进入“自由落体”阶段,也就是由于重力因素它们开始坠落到地面,仿若绽放后熄灭的烟花。

基本知识:用Python和Tkinter设计烟花

这里不再一股脑把数学知识全丢出来,我们边写代码边说理论。首先,确保你安装和导入了Tkinter,它是Python的标准 GUI 库,广泛应用于各种各样的项目和程序开发,在Python中使用 Tkinter 可以快速的创建 GUI 应用程序。

import tkinter as tk

from PIL import Image,ImageTk

from time import time,sleep

from random import choice,uniform,randint

from math import sin,cos,radians

除了Tkinter之外,为了能让界面有漂亮的背景,我们也导入PIL用于图像处理,以及导入其它一些包,比如time,random和math。它们能让我们更容易的控制烟花粒子的运动轨迹。

Tkinter应用的基本设置如下:

root = tk.Tk()

为了能初始化Tkinter,我们必须创建一个Tk()根部件(root Widget),它是一个窗口,带有标题栏和由窗口管理器提供的其它装饰物。该根部件必须在我们创建其它小部件之前就创建完毕,而且只能有一个根部件。

w = tk.Label(root,text="Hello Tkinter!")

这一行代码包含了Label部件。该Label调用中的第一个参数就是父窗口的名字,即我们这里用的“根”。关键字参数“text”指明显示的文字内容。你也可以调用其它小部件:button,Canvas等等。

w.pack()

root.mainloop()

接下来的这两行代码很重要。这里的打包方法是告诉Tkinter调整窗口大小以适应所用的小部件。窗口直到我们进入Tkinter事件循环,被root.mainloop()调用时才会出现。在我们关闭窗口前,脚本会一直在停留在事件循环。

将烟花绽放转译成代码

现在我们设计一个对象,表示烟花事件中的每个粒子。每个粒子都会有一些重要的属性,支配了它的外观和移动状况:大小,颜色,位置,速度等等。

'''

particles 类

粒子在空中随机生成随机,变成一个圈、下坠、消失

属性:

- ID: 粒子的ID

- x,y: 粒子的坐标

- vx,vy: 在坐标的变化速度

- total: 总数

- age: 粒子存在的时长

- color: 颜色

- cv: 画布

- lifespan: 最高存在时长

'''

class part:

def __init__(self,cv,IDx,total,explosion_speed,x=0.,y=0.,vx = 0.,vy = 0.,size=2.,color = 'red',lifespan = 2,**kwargs):

self.ID = IDx

self.x = x

self.y = y

self.initial_speed = explosion_speed

self.vx = vx

self.vy = vy

self.total = total

self.age = 0self.color = color

self.cv = cv

self.cID = self.cv.create_oval(

x - size,y - size,x + size,

y + size,fill=self.color)

self.lifespan = lifespan

如果我们回过头想想最开始的想法,就会意识到必须确保每个烟花绽放的所有粒子必须经过3个不同的阶段,即“膨胀”“坠落”和“消失”。 所以我们向粒子类中再添加一些运动函数,如下所示:

def update(self,dt):

# 粒子膨胀if self.alive() and self.expand():

move_x = cos(radians(self.ID*360/self.total))*self.initial_speed

move_y = sin(radians(self.ID*360/self.total))*self.initial_speed

self.vx = move_x/(float(dt)*1000)

self.vy = move_y/(float(dt)*1000)

self.cv.move(self.cID,move_x,move_y)

# 以自由落体坠落

elif self.alive():

move_x = cos(radians(self.ID*360/self.total))

# we technically don't need to update x,y because move will do the job

self.cv.move(self.cID,self.vx + move_x,self.vy+GraviTY*dt)

self.vy += GraviTY*dt

# 如果粒子的生命周期已过,就将其移除

elif self.cID is not None:

cv.delete(self.cID)

self.cID = None

当然,这也意味着我们必须定义每个粒子绽放多久、坠落多久。这部分需要我们多尝试一些参数,才能达到最佳视觉效果。

# 定义膨胀效果的时间帧

def expand (self):

return self.age <= 1.2

# 检查粒子是否仍在生命周期内

def alive(self):

return self.age <= self.lifespan

使用Tkinter模拟

现在我们将粒子的移动概念化,不过很明显,一个烟花不能只有一个粒子,一场烟花秀也不能只有一个烟花。我们下一步就是让Python和Tkinter以我们可控的方式向天上连续“发射”粒子。

到了这里,我们需要从 *** 作一个粒子升级为在屏幕上展现多个烟花及每个烟花中的多个粒子。

我们的解决思路如下:创建一列列表,每个子列表是一个烟花,其包含一列粒子。每个列表中的例子有相同的x,y坐标、大小、颜色、初始速度。

numb_explode = randint(6,10)

# 为所有模拟烟花绽放的全部粒子创建一列列表

for point in range(numb_explode):

objects = []

x_cordi = randint(50,550)

y_cordi = randint(50,150)

size = uniform (0.5,3)

color = choice(colors)

explosion_speed = uniform(0.2,1)

total_particles = randint(10,50)

for i in range(1,total_particles):

r = part(cv,IDx = i,total = total_particles,explosion_speed = explosion_speed,x = x_cordi,y = y_cordi,

color=color,size = size,lifespan = uniform(0.6,1.75))

objects.append(r)

explode_points.append(objects)

我们下一步就是确保定期更新粒子的属性。这里我们设置让粒子每0.01秒更新它们的状态,在1.8秒之后停止更新(这意味着每个粒子的存在时间为1.6秒,其中1.2秒为“绽放”状态,0.4秒为“坠落”状态,0.2秒处于Tkinter将其完全移除前的边缘状态)。

total_time = .0

# 在1.8秒时间帧内保持更新

while total_time < 1.8:

sleep(0.01)

tnew = time()

t,dt = tnew,tnew - t

for point in explode_points:

for part in point:

part.update(dt)

cv.update()

total_time += dt

现在,我们只需将最后两个gist合并为一个能被Tkinter调用的函数,就叫它simulate()吧。该函数会展示所有的数据项,并根据我们设置的时间更新每个数据项的属性。在我们的主代码中,我们会用一个alarm处理模块after()调用此函数,after()会等待一定的时间,然后再调用函数。

我们这里设置让Tkinter等待100个单位(1秒钟)再调取simulate。

if __name__ == '__main__':

root = tk.Tk()

cv = tk.Canvas(root,height=600,wIDth=600)

# 绘制一个黑色背景

cv.create_rectangle(0,600,fill="black")

cv.pack()

root.protocol("WM_DELETE_WINDOW",close)

# 在1秒后才开始调用stimulate()

root.after(100,simulate,cv)

root.mainloop()

好了,这样我们就用Python代码放了一场烟花秀:

本文只一个简单版本,等进一步熟悉Tkinter后,还可以添加更多颜色更漂亮的背景照片,让代码为你绽放更美的烟花!

以下是全部代码:

import tkinter as tk

from PIL import Image,radians

# 模拟重力

GraviTY = 0.05

# 颜色选项(随机或者按顺序)

colors = ['red','blue','yellow','white','green','orange','purple','seagreen','indigo','cornflowerblue']

'''

particles 类

粒子在空中随机生成随机,变成一个圈、下坠、消失

属性:

- ID: 粒子的ID

- x,vy: 在坐标的变化速度

- total: 总数

- age: 粒子存在的时长

- color: 颜色

- cv: 画布

- lifespan: 最高存在时长

'''

class Particle:

def __init__(self,vx=0.,vy=0.,color='red',lifespan=2,

**kwargs):

self.ID = IDx

self.x = x

self.y = y

self.initial_speed = explosion_speed

self.vx = vx

self.vy = vy

self.total = total

self.age = 0self.color = color

self.cv = cv

self.cID = self.cv.create_oval(

x - size,fill=self.color)

self.lifespan = lifespan

def update(self,dt):

self.age += dt

# 粒子范围扩大

if self.alive() and self.expand():

move_x = cos(radians(self.ID * 360 / self.total)) * self.initial_speed

move_y = sin(radians(self.ID * 360 / self.total)) * self.initial_speed

self.cv.move(self.cID,move_y)

self.vx = move_x / (float(dt) * 1000)

# 以自由落体坠落

elif self.alive():

move_x = cos(radians(self.ID * 360 / self.total))

# we technically don't need to update x,self.vy + GraviTY * dt)

self.vy += GraviTY * dt

# 移除超过最高时长的粒子

elif self.cID is not None:

cv.delete(self.cID)

self.cID = None

# 扩大的时间

def expand (self):

return self.age <= 1.2

# 粒子是否在最高存在时长内

def alive(self):

return self.age <= self.lifespan

'''

循环调用保持不停

'''

def simulate(cv):

t = time()

explode_points = []

wait_time = randint(10,100)

numb_explode = randint(6,10)

# 创建一个所有粒子同时扩大的二维列表

for point in range(numb_explode):

objects = []

x_cordi = randint(50,150)

speed = uniform(0.5,1.5)

size = uniform(0.5,total_particles):

r = Particle(cv,IDx=i,total=total_particles,explosion_speed=explosion_speed,x=x_cordi,y=y_cordi,

vx=speed,vy=speed,color=color,size=size,lifespan=uniform(0.6,1.75))

objects.append(r)

explode_points.append(objects)

total_time = .0

# 1.8s内一直扩大

while total_time < 1.8:

sleep(0.01)

tnew = time()

t,tnew - t

for point in explode_points:

for item in point:

item.update(dt)

cv.update()

total_time += dt

# 循环调用

root.after(wait_time,cv)

def close(*ignore):

"""退出程序、关闭窗口"""

global root

root.quit()

if __name__ == '__main__':

root = tk.Tk()

cv = tk.Canvas(root,height=400,wIDth=600)

# 选一个好看的背景会让效果更惊艳!

image = Image.open("./image.jpg")

photo = ImageTk.PhotoImage(image)

cv.create_image(0,image=photo,anchor='nw')

cv.pack()

root.protocol("WM_DELETE_WINDOW",close)

root.after(100,cv)

root.mainloop()

总结

以上是内存溢出为你收集整理的代码也浪漫:用 Python 放一场烟花秀!全部内容,希望文章能够帮你解决代码也浪漫:用 Python 放一场烟花秀!所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1208476.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-04
下一篇 2022-06-04

发表评论

登录后才能评论

评论列表(0条)

保存