给生活一点浪漫——python实现烟花绽放

news/2025/2/12 16:05:35/

说明

让人感到浪漫的方式各有不同,曾经我很期待过年试在老家看烟花齐放的那种烟花来带的浪漫,可最近环保越来越严,烟花能看到的也越来越少,可能再也看不到曾经那种夜空中烟花齐放的场景了。

借此机会,用python做一个烟花齐放的特效,以敬曾经夜晚烟花齐放的日子。

  • 既然做烟花效果,我们应该要知道,烟花分为2部分完成,炸开爆炸后的坠落两个部分。
    实现的简单流程就是:
    • 通过让上一个粒子分裂为X数量的粒子来模拟爆炸效果。粒子会发生“膨胀”,意思是它们会以恒速移动且相互之间的角度相等。这样就能让我们以一个向外膨胀的圆圈形式模拟出烟花绽放的画面。
    • 经过一定时间后,粒子会进入“自由落体”阶段,也就是由于重力因素它们开始坠落到地面,仿若绽放后熄灭的烟花。

实现步骤

用Python和Tkinter设计烟花

  • 注:python不会导入库的可以翻我之前的文章哈,有详细说明的。
  • 首先,确保你安装和导入了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用于图像处理,以及导入其它一些包,比如timerandommath
    它们能让我们更容易的控制烟花粒子的运动轨迹。

  • Tkinter应用的基本设置如下:

root = tk.Tk()
  • 为了能初始化Tkinter,我们必须创建一个Tk()根部件root widget,它是一个窗口,带有标题栏和由窗口管理器提供的其它装饰物。该根部件必须在我们创建其它小部件之前就创建完毕,而且只能有一个根部件。
w = tk.Label(root, text="Hi Tkinter")
  • 这一行代码包含了Label部件。该Label调用中的第一个参数就是父窗口的名字,即我们这里用的“根”。关键字参数“text”指明显示的文字内容。你也可以调用其它小部件:ButtonCanvas等等。
    接下来的这两行代码很重要。这里的打包方法是告诉Tkinter调整窗口大小以适应所用的小部件。窗口直到我们进入Tkinter事件循环,被root.mainloop()调用时才会出现。在我们关闭窗口前,脚本会一直在停留在事件循环。
w.pack()
root.mainloop()

将烟花绽放转译成代码

  • 现在我们设计一个对象,表示烟花事件中的每个粒子。每个粒子都会有一些重要的属性,支配了它的外观和移动状况:大小,颜色,位置,速度等等。
'''
Generic class for particlesparticles are emitted almost randomly on the sky, forming a round of circle (a star) before falling and getting removed
from canvasAttributes:- 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 = idxself.x = xself.y = yself.initial_speed = explosion_speedself.vx = vxself.vy = vyself.total = totalself.age = 0self.color = colorself.cv = cvself.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_speedmove_y = sin(radians(self.id*360/self.total))*self.initial_speedself.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 jobself.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 - tfor 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()

只是有想实现烟花的想法,代码并非原创来自网上哈。

效果展示

执行后效果如下图【成品是可以动态调整各个参数的哈,还可以添加更多颜色更漂亮的背景照片,当然前提是你熟悉Tkinter后才能更好的进行各类参数调整】

在这里插入图片描述

完整代码

'''
FIREWORKS SIMULATION WITH TKINTER
*self-containing code
*to run: simply type python simple.py in your console
*compatible with both Python 2 and Python 3
*Dependencies: tkinter, Pillow (only for background image)
*The design is based on high school physics, with some small twists only for aesthetics purpose
'''
import tkinter as tk
#from tkinter import messagebox
#from tkinter import PhotoImage
from PIL import Image, ImageTk
from time import time, sleep
from random import choice, uniform, randint
from math import sin, cos, radians# gravity, act as our constant g, you can experiment by changing it
GRAVITY = 0.05
# list of color, can choose randomly or use as a queue (FIFO)
colors = ['red', 'blue', 'yellow', 'white', 'green', 'orange', 'purple', 'seagreen', 'indigo', 'cornflowerblue']'''
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
'''
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 = idxself.x = xself.y = yself.initial_speed = explosion_speedself.vx = vxself.vy = vyself.total = totalself.age = 0self.color = colorself.cv = cvself.cid = self.cv.create_oval(x - size, y - size, x + size,y + size, fill=self.color)self.lifespan = lifespandef update(self, dt):self.age += dt# particle expansionsif self.alive() and self.expand():move_x = cos(radians(self.id*360/self.total))*self.initial_speedmove_y = sin(radians(self.id*360/self.total))*self.initial_speedself.cv.move(self.cid, move_x, move_y)self.vx = move_x/(float(dt)*1000)# falling down in projectile motionelif 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 jobself.cv.move(self.cid, self.vx + move_x, self.vy+GRAVITY*dt)self.vy += GRAVITY*dt# remove article if it is over the lifespanelif self.cid is not None:cv.delete(self.cid)self.cid = None# define time frame for expansiondef expand (self):return self.age <= 1.2# check if particle is still alive in lifespandef alive(self):return self.age <= self.lifespan'''
Firework simulation loop:
Recursively call to repeatedly emit new fireworks on canvas
a list of list (list of stars, each of which is a list of particles)
is created and drawn on canvas at every call, 
via update protocol inside each 'part' object 
'''
def simulate(cv):t = time()explode_points = []wait_time = randint(10,100)numb_explode = randint(6,10)# create list of list of all particles in all simultaneous explosionfor point in range(numb_explode):objects = []x_cordi = randint(50,550)y_cordi = randint(50, 150)speed = uniform (0.5, 1.5)          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, vx = speed, vy = speed, color=color, size = size, lifespan = uniform(0.6,1.75))objects.append(r)explode_points.append(objects)total_time = .0# keeps undate within a timeframe of 1.8 secondwhile total_time < 1.8:sleep(0.01)tnew = time()t, dt = tnew, tnew - tfor point in explode_points:for item in point:item.update(dt)cv.update()total_time += dt# recursive call to continue adding new explosion on canvasroot.after(wait_time, simulate, cv)def close(*ignore):"""Stops simulation loop and closes the window."""global rootroot.quit()if __name__ == '__main__':root = tk.Tk()cv = tk.Canvas(root, height=600, width=600)# use a nice background imageimage = Image.open("image.jpg")photo = ImageTk.PhotoImage(image)cv.create_image(0, 0, image=photo, anchor='nw')cv.pack()root.protocol("WM_DELETE_WINDOW", close)root.after(100, simulate, cv)root.mainloop()

http://www.ppmy.cn/news/569606.html

相关文章

Java圆的测试类_java – 最有效的方法来测试数字是否在360度圆的某个22.5度范围内...

整数算术是你的朋友&#xff1a; private static final POINTS new String[]{ "N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", &quo…

【表面缺陷检测】基于yolov5的钢板表面缺陷检测(附代码和数据集,Ubuntu系统)

写在前面: 首先感谢兄弟们的订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 文末获取代码和数据集,请先看检测效果: 一.介绍 钢板表面缺陷检测是一种利用计算机视觉技术对钢板表面缺陷进行自动化…

2020.9.26 360笔试第二题

题目大意 给出一个带权无向图&#xff0c;没有自环和重边&#xff0c;给出两个节点s和t&#xff0c;求从s到达t的路径中的所有边里&#xff0c;边权最大的边的权至少有多大。保证s与t连通。 用例以n&#xff08;节点个数&#xff09;、m&#xff08;边数&#xff09;、s、t&…

我的第一个WinCE驱动--流式驱动【转】

我的第一个WinCE驱动-- 流式驱动【转】 2008-10-29 14:00 转做WinCE驱动已经有两个月了&#xff0c;却没有做过一个真正的WinCE平台下的驱动&#xff0c;心里都有些急了&#xff0c;很想尝试下&#xff0c;好尽快融入WinCE驱动开发这个团队。 头儿也很理解&#xff0c;给了俺个…

NW(New World)快速开发平台介绍(完整的中小型管理系统解决方案)

在公司做了3年多的web平台开发&#xff0c;自己(和同事)总结了一套快速开发平台&#xff0c;定位于快速开发中小型的管理系统。NW开发平台是一个完整的解决方案&#xff0c;包括前端组件、权限、事务、dao、报表、vo生成器、eclipse插件等等&#xff0c;也就是说你的产品可以完…

360linux网卡驱动,驱动下载

教育服务器 网卡驱动 For Window NT Disk 1 网卡驱动 For Window NT Disk 2 IDE RAID 驱动For RedHat 7.2 IDE RAID 驱动For Windows 2000/NT/XP NP系列 ^^^ NP60 返回 光驱&#xff0c;for dos 网卡&#xff0c;for Windows NT4 网卡&#xff0c;for Netware 5 NP70 网卡驱动 …

Qt制作360安仔精灵

有两个QLabel, 一个显示静态图片, 一个显示动态图片, 相互切换显示 .. #ifndef TESTCHICKEN_H #define TESTCHICKEN_H#include <QtWidgets/QDialog> #include "ui_testChicken.h" #include <QPoint>class QMovie; class testChicken : public QDialog {Q…

360hackgame writeup

AppLeU0 2014/05/05 11:22 0x00扯扯蛋 第一次在wooyun投稿&#xff0c;新人求带。360网络攻防hackgame开荒记&#xff0c;自己做的一些题&#xff0c;中间有一些是领先的&#xff0c;也算开荒吧&#xff0c;就叫开荒记吧&#xff0c;自己的脑洞也比较大&#xff0c;有的题各种…