庆祝法国队夺冠:用Python放一场烟花秀

每天敲代码的朋友,有没有想过代码也能够变得很酷炫又浪漫?今天就教你们用Python模拟出绽开的烟花庆祝昨晚法国队夺冠,工做之余也能够随时让程序为本身放一场烟花秀。python

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

 

 

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

总体梳理概念

咱们的整个理念比较简单。app

 

 

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

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

这里再也不一股脑把数学知识全丢出来,咱们边写代码边说理论。首先,确保你安装和导入了Tkinter,它是Python的标准 GUI 库,普遍应用于各类各样的项目和程序开发,在Python中使用 Tkinter 能够快速的建立 GUI 应用程序。ide

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应用的基本设置以下:oop

root = tk.Tk()
复制代码

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

w = tk.Label(root, text="Hello Tkinter!")
复制代码

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

w.pack()
root.mainloop()
复制代码

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

将烟花绽开转译成代码

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

'''
Generic class for particles

particles are emitted almost randomly on the sky, forming a round of circle (a star) before falling and getting removed
from canvas

Attributes:
    - id: identifier of a particular particle in a star
    - x, y: x,y-coordinate of a star (point of explosion)
    - vx, vy: speed of particle in x, y coordinate
    - total: total number of particle in a star
    - age: how long has the particle last on canvas
    - color: self-explantory
    - cv: canvas
    - lifespan: how long a particle will last on canvas
    - intial_speed: speed of particle at explosion

'''
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 = 0
        self.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, 0, 600, 600, fill="black")
    cv.pack()

    root.protocol("WM_DELETE_WINDOW", close)
    # 在1秒后才开始调用stimulate()
    root.after(100, simulate, cv)
    root.mainloop()
复制代码

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

 

 

本文只是基本版本,等你进一步熟悉Tkinter后,还能够添加更多颜色更漂亮的背景照片,让代码为你绽开更美的烟花!

我有一个微信公众号,常常会分享一些python技术相关的干货;若是你喜欢个人分享,能够用微信搜索“python语言学习”关注

欢迎你们加入千人交流答疑裙:699+749+852

相关文章
相关标签/搜索