首頁 >後端開發 >Python教學 >python酷炫煙火表白原始碼

python酷炫煙火表白原始碼

angryTom
angryTom原創
2020-02-25 14:10:1150429瀏覽

天天敲程式碼的朋友,有沒有想過程式碼也可以變得很酷又浪漫?今天就教大家用Python模擬出綻放的煙火,工作之餘也可以隨時讓程式為自己放一場煙火秀。

python酷炫煙火表白原始碼

python酷煙火表白原始碼

這個有趣的小專案並不複雜,只需一點視覺化技巧,100餘行Python程式碼和程式庫Tkinter,最後我們就能達到下面這個效果:

python酷炫煙火表白原始碼

#學完本教學後,你也能做出這樣的煙火秀。

整體概念整理

我們的整個理念比較簡單。

python酷炫煙火表白原始碼

如上圖示,我們這裡透過讓畫面上一個粒子分裂為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__ == &#39;__main__&#39;:
    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代码放了一场烟花秀:

python酷炫煙火表白原始碼

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

以下是全部代码:

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
# 模拟重力
GRAVITY = 0.05
# 颜色选项(随机或者按顺序)
colors = [&#39;red&#39;, &#39;blue&#39;, &#39;yellow&#39;, &#39;white&#39;, &#39;green&#39;, &#39;orange&#39;, &#39;purple&#39;, &#39;seagreen&#39;, &#39;indigo&#39;, &#39;cornflowerblue&#39;]
&#39;&#39;&#39;
particles 类
粒子在空中随机生成随机,变成一个圈、下坠、消失
属性:
    - id: 粒子的id
    - x, y: 粒子的坐标
    - vx, vy: 在坐标的变化速度
    - total: 总数
    - age: 粒子存在的时长
    - color: 颜色
    - cv: 画布
    - lifespan: 最高存在时长
&#39;&#39;&#39;
class Particle:
    def __init__(self, cv, idx, total, explosion_speed, x=0., y=0., vx=0., vy=0., size=2., color=&#39;red&#39;, 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)
        # 以自由落体坠落
        elif self.alive():
            move_x = cos(radians(self.id * 360 / self.total))
            # we technically don&#39;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
&#39;&#39;&#39;
循环调用保持不停
&#39;&#39;&#39;
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, 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.8s内一直扩大
    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
    # 循环调用
    root.after(wait_time, simulate, cv)
def close(*ignore):
    """退出程序、关闭窗口"""
    global root
    root.quit()
if __name__ == &#39;__main__&#39;:
    root = tk.Tk()
    cv = tk.Canvas(root, height=400, width=600)
    # 选一个好看的背景会让效果更惊艳!
    image = Image.open("./image.jpg")
    photo = ImageTk.PhotoImage(image)
    cv.create_image(0, 0, image=photo, anchor=&#39;nw&#39;)
    cv.pack()
    root.protocol("WM_DELETE_WINDOW", close)
    root.after(100, simulate, cv)
    root.mainloop()

众多python培训视频,尽在python学习网,欢迎在线学习!

以上是python酷炫煙火表白原始碼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn