简单的动画 (numpy & PySimpleGUI)
文件建立日期: 2020/03/26
最后修订日期: None
相关软件信息:
Windows 10 | Python 3.7.6 | PySimpleGUI 4.16.0 | Numpy 1.18.2 | PIL/Pillow 7.1.1 |
说明: 本文请随意引用或更改, 只须标示出处及作者, 作者不保证内容絶对正确无误, 如造成任何后果, 请自行负责.
标题: 简单的动画 (numpy & PySimpleGUI)
以前总是使用多线程来达到多对象的运动处理, 其实在很多的GUI中都有定时处理的设计, 因此, 在不使用多线程的情况下, 试着利用GUI
本身的定时, 来处理所有的对象的移更新, 看起来也是可以很快很自然的效果. 而且设计起来比多线程更简单多, 而且容易除错.
目标:
定点定时喷出气泡(水珠)任意个.
每个气泡都有自己的方向及速度, 为了便于处理, 定为X方向及Y方向的速度.
气泡在过程中, 有三件事要处理
- 受重力加速度影响, 会向下掉落
- 落下的过程会逐渐分散, 因为分散效果不明显, 改采用颜色趋向背景色
- 气泡的位置如果超出显示区, 必须删除, 否则气泡对象会越来越多.
输出画面
代码及说明
- 导入相关的库
import numpy as np
import PySimpleGUI as sg
import random
建立气泡类
颜色不再使用文字方式, 如
yellow
,green
, 因为颜色要在运动的过程中改变, 所改采RGB
的方式#RRGGBB
来定义.一开始要为气泡建立空的数组, 每个气泡都有5个属性
图像ID - 画在画布上的对象
X , Y 气泡生成时的被初始位置坐标
V_x, V_y 气泡生成时的被初始速度, 两者构成其运动方向
class Bubble():
def __init__(self):
self.width, self.height = self.size = (301, 501)
self.x0, self.y0 = self.origin = self.width//2, self.height-50
self.number = 10 # 10 bubbles generate each time
self.range = 30
self.time = 10
self.rate = self.time/40
self.color = '#FFFFFF'
self.bg = '#0080FF'
self.radius = 3
self.bubbles = np.empty((0, 5), dtype=np.float)
- 气泡生成
- 初始位置都是固定的, 数目为
self.number
- 速度由随机数成, 就会有不同方向, 不同速度的气泡
- 依序画上气泡, 并存下其图像
id
, 供后更新或删除使用.
- 初始位置都是固定的, 数目为
def create(self):
new_bubbles = np.hstack(
(np.full((self.number, 1), 0.0),
np.full((self.number, 1), self.x0),
np.full((self.number, 1), self.y0),
(np.random.rand(self.number, 1)-0.5)*self.range,
(np.random.rand(self.number, 1)-0.5)*self.range
)).astype(np.float)
for bubble in new_bubbles:
color = self.color
bubble[0] = draw.DrawCircle((bubble[1], bubble[2]),
self.radius, fill_color=color, line_color=color)
self.bubbles = np.vstack((self.bubbles, new_bubbles))
气泡移动
方向X的速度不变, 其位置 X_{n+1} = X_n + V_x * dt
方向Y的速度受重力影响
位置 Y_{n+1} = Y_n - V_y * dt + g*t^2/2, 这里的V_y为了简单起见, 只取常数
速度 V_{n+1} = V_n-g*dt
这些式子有可能再简化,例如时间差就为1,重力加速度也为1,那计算的式子就更简单了。
def change(self):
self.bubbles[:, 1] += self.bubbles[:, 3]*self.rate
self.bubbles[:, 2] += self.bubbles[:, 4]*self.rate - 9.8*self.rate**2/2
self.bubbles[:, 4] -= 9.8*self.rate
- 更泡更新
- 颜色的变化, 由原气泡颜色, 依y轴的位置来比例更新成背景的颜色.
- 由于
PySimpleGUI
不能直接更新图像的颜色, 所以先删除原位置的图像, 再于新位置重新画上图像.
def update(self):
b = 255
color = self.color
for bubble in self.bubbles:
r = min(int(255 - 255*(self.y0-bubble[2])/self.y0), 255)
g = min(int(255 - 128*(self.y0-bubble[2])/self.y0), 255)
color = "#%02x%02x%02x" % (r, g, b)
draw.DeleteFigure(int(bubble[0]))
bubble[0] = draw.DrawCircle((bubble[1], bubble[2]),
self.radius, fill_color=color, line_color=color)
- 检查气泡是否已出界
- 使用
Numpy
, 依据气泡的位置, 确认是否出界, 建立boolean
索引. - 根据索引导出要删除的图像, 以及更新仍存在气泡列表
- 使用
def check(self):
filter = np.logical_and(
np.logical_and(self.bubbles[:,1]>0, self.bubbles[:,1]<self.width),
np.logical_and(self.bubbles[:,2]>0, self.bubbles[:,2]<self.height))
discard = self.bubbles[np.logical_not(filter)]
for key in discard[:, 0]:
draw.DeleteFigure(int(key))
self.bubbles = self.bubbles[filter]
建立气泡实例及GUI界面
GUI
界面很简单, 就是一张画布.
B = Bubble()
layout = [[sg.Graph(B.size, background_color=B.bg, key='Graph', pad=(0, 0),
graph_bottom_left=(0, 0), graph_top_right=B.size)]]
window = sg.Window('气球', layout=layout, finalize=True, margins=(0, 0))
draw = window.find_element('Graph')
- 运行主程序
- 内容很简单 - 产生气泡, 更新位置及速度, 检查是否出界, 更新气泡位置及颜色, 就这样一直循环下去, 直到用户关闭窗口.
- 扣除空行, 全部只有
65
行.
B.create()
while True:
event, values = window.read(timeout=B.time)
if event == None:
break
B.change()
B.check()
B.update()
B.create()
window.close()
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: