As the saying goes, code is like poetry; I said it, too. The code is picturesque! Let’s take a look at how to use Python to light fireworks, so that we can put on a fireworks show for ourselves at any time after work. Making a fun little project like this wasn’t complicated, it took a little visualization, 100 lines of Python code and the Tkinter library, and we ended up with something like this:






Overall concept sorting

Our whole concept is simple.






As shown above, we simulate an explosion by splitting one particle on the screen into X number of particles. Particles “expand,” meaning they move at a constant speed and at the same Angle to each other. This allows us to simulate a firework explosion in the form of an expanding circle. After a certain amount of time, the particles enter a stage of “free fall,” where they start falling to the ground due to gravity, like fireworks that go out.

Basics: Designing fireworks with Python and Tkinter

Instead of throwing out all the math, we’re going to talk about theory as we write code. First, make sure you have Tkinter installed and imported. Tkinter is Python’s standard GUI library, widely used in a wide variety of projects and application development. Using Tkinter in Python allows you to quickly create GUI applications.


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

Copy the code


In addition to Tkinter, we also imported PIL for image processing and some other packages like Time, Random and Math to give the interface a nice background. They make it easier to control the trajectory of the firework particles.

The basic Settings of the Tkinter application are as follows:

root = tk.Tk()

Copy the code


To initialize Tkinter, we must create a Tk() root widget, which is a window with a title bar and other decorations provided by the window manager. The root widget must be created before we can create any other widget, and there can only be one root widget.

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

Copy the code

This line of code contains the Label widget. The first argument in the Label call is the name of the parent window, the “root” we use here. Keyword parameter text specifies the text content to be displayed. You can also call other widgets: Button, Canvas, etc.

w.pack()

root.mainloop()

Copy the code

The next two lines of code are important. The packaging method here is to tell Tkinter to resize the window to fit the widgets used. The window does not appear until we enter the Tkinter event loop and are called by root.mainloop(). The script will stay in the event loop until we close the window.


Translate fireworks into code

Now let’s design an object that represents each particle in the firework event. Every particle has some important properties that govern how it looks and moves: size, color, position, speed, and so on.

' ' '

Particles class



Particles randomly generate randomly in the air, turning into a circle, falling, and disappearing



Properties:

- id: indicates the ID of a particle

-x, y: coordinates of the particle

-vx, vy: speed of change in coordinates

- total: the total number

- age: indicates the duration of the particle

- color: color

- CV: canvas

- Lifespan: indicates the maximum 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

Copy the code

If we go back to the original idea, we realize that we have to make sure that all the particles in each firework burst must pass through three distinct stages: “expanding,” “falling,” and “disappearing.” So let’s add some more motion functions to the particle class, like this:

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)



# Fall in free fall

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



# Remove particles if their life cycle has expired

elif self.cid is not None:

cv.delete(self.cid)

self.cid = None

Copy the code

Of course, that also means we have to define how long each particle blooms and falls. This part requires us to try more parameters to achieve the best visual effect.

# Define the time frame for the expansion effect

def expand (self):

Return the self. The age < = 1.2



Check if the particle is still in its life cycle

def alive(self):

return self.age <= self.lifespan

Copy the code


Use Tkinter simulation

Now we’re conceptualizing the movement of particles, but obviously, a firework can’t have just one particle, and a fireworks show can’t have just one firework. Our next step is to get Python and Tkinter to continuously “shoot” particles into the sky in a way we can control.

At this point, we need to move from manipulating one particle to displaying multiple fireworks and multiple particles from each firework on the screen.

The solution is as follows: Create a list of columns, and each sublist is a firework that contains a list of particles. Each example in the list has the same X,y coordinates, size, color, and initial speed.

Numb_explode = randint (6, 10)

Create a list of all particles that simulate a fireworks display

for point in range(numb_explode):

objects = []

X_cordi = randint (50550).

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)

Copy the code

Our next step is to make sure we update the particle properties regularly. Here we set the particles to update their status every 0.01 seconds, stopping after 1.8 seconds (this means that each particle has a lifespan of 1.6 seconds, with 1.2 seconds in the “bloom” state, 0.4 seconds in the “fall” state, and 0.2 seconds in the edge state before Tkinter removes it completely).

total_time = .0

# Stay updated within the 1.8 second time frame

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

Copy the code

Now, let’s just merge the last two GIST into a function that can be called by Tkinter and call it Simulate (). This function displays all the data items and updates the properties of each data item based on the time we set. In our main code, we call this function with an alarm processing module, after(), which waits a certain amount of time before calling the function.

We set up Tkinter to wait 100 units (1 second) before simulate.

if __name__ == '__main__':

root = tk.Tk()

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

# Draw a black background

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

cv.pack()



root.protocol("WM_DELETE_WINDOW", close)

* * * * * * * * * * * * * * * * * * * * * * * * * * * *

root.after(100, simulate, cv)

root.mainloop()

Copy the code

Ok, so we have a fireworks show with Python code:

This article is only a simple version, but as you get more familiar with Tkinter, you can add more beautiful background photos to make your code more beautiful!

Here is the full code:

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



# Simulate gravity

GRAVITY = 0.05

# Color options (random or sequential)

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



' ' '

Particles class



Particles randomly generate randomly in the air, turning into a circle, falling, and disappearing



Properties:

- id: indicates the ID of a particle

-x, y: coordinates of the particle

-vx, vy: speed of change in coordinates

- total: the total number

- age: indicates the duration of the particle

- color: color

- CV: canvas

- Lifespan: indicates the maximum lifespan



' ' '





class Particle: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



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_x, move_y)

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



# fall in free fall 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





' ' '

The loop calls keep going

' ' '





def simulate(cv):

t = time()

explode_points = []

wait_time = randint(10, 100)

numb_explode = randint(6, 10)

# Create a two-dimensional list of all the numb_explode particles in range.

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 = 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.2s while total_time < 1.8:

Sleep (0.01)

tnew = time()

t, dt = tnew, tnew - t

for point in explode_points:for item in point:

item.update(dt)

cv.update()

total_time += dt

# loop call

root.after(wait_time, simulate, cv)





Def close(*ignore):""" exit program, close window """ "global root

root.quit()





if __name__ == '__main__':

root = tk.Tk()

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

# Choose a nice background to make the effect even more amazing!

image = 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()
Copy the code

This article comes from the cloud community partner “Programmers grow together”. For more information, you can pay attention to “Programmers grow together”.