利用Python寫一場新年煙花秀

我們用到的 Python 模塊包括:tkinterPILtimerandommath,如果第三方模塊沒有裝的話,pip install 一下即可,下面看一下代碼實現。

1.導庫

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

2.煙花顏色

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

3.定義煙花類

class fireworks:
    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
        # 煙花綻放 x 軸
        self.x = x
        # 煙花綻放 x 軸
        self.y = y
        self.initial_speed = explosion_speed
        # 外放 x 軸速度
        self.vx = vx
        # 外放 y 軸速度
        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

    # 更新數據
    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)
        # 膨脹到最大下落
        elif self.alive():
            move_x = cos(radians(self.id * 360 / self.total))
            self.cv.move(self.cid, self.vx + move_x, self.vy + 0.5 * dt)
            self.vy += 0.5 * dt
        # 過期移除
        elif self.cid is not None:
            cv.delete(self.cid)
            self.cid = None

    # 定義膨脹效果的時間幀
    def expand(self):
        return self.age <= 1.5

    # 檢查粒子是否仍在生命周期內
    def alive(self):
        return self.age <= self.lifespan

4.燃放煙花

def ignite(cv):
    t = time()
    # 煙花列表
    explode_points = []
    wait_time = randint(10, 100)
    # 爆炸的個數
    numb_explode = randint(6, 10)
    for point in range(numb_explode):
        # 爆炸粒子列表
        objects = []
        # 爆炸 x 軸
        x_cordi = randint(50, 550)
        # 爆炸 y 軸
        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 = fireworks(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.8 秒時間幀內保持更新
    while total_time < 1.8:
        # 讓畫面暫停 0.01s
        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
    root.after(wait_time, ignite, cv)

5.啟動

if __name__ == "__main__":
    root = tk.Tk()
    # 繪制一個畫佈
    cv = tk.Canvas(root, height=400, width=600)
    # 背景圖
    image = Image.open("bg.jpg")
    photo = ImageTk.PhotoImage(image)
    # 在畫板上繪制一張圖片
    cv.create_image(0, 0, image=photo, anchor='nw')
    cv.pack()
    root.protocol(close)
    root.after(100, ignite, cv)
    # 生成窗口
    root.mainloop()

看一下效果:

推薦閱讀: