Python数据可视化艺术:动态壁纸生成器 - 详解
解决方案概述
我们将使用以下技术:
Pygame:用于创建图形和动画
Perlin噪声:生成自然的流动效果
Win32 API:将生成的动画设置为桌面壁纸
多线程:确保壁纸更新不影响主程序运行
完整代码实现
import pygame
import numpy as np
import random
import win32gui
import win32con
import win32api
import threading
import math
import sys
import os
from pygame.locals import *
# 初始化pygame
pygame.init()
# 获取屏幕尺寸
screen_info = pygame.display.Info()
SCREEN_WIDTH, SCREEN_HEIGHT = screen_info.current_w, screen_info.current_h
# 创建隐藏窗口用于渲染
os.environ['SDL_VIDEO_WINDOW_POS'] = f"{SCREEN_WIDTH + 100},0"
screen = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
# Perlin噪声生成器
class PerlinNoiseGenerator:
def __init__(self, seed=None):
self.seed = seed if seed is not None else random.randint(0, 1000000)
random.seed(self.seed)
self.permutation = list(range(256))
random.shuffle(self.permutation)
self.permutation = self.permutation * 2
def fade(self, t):
return t * t * t * (t * (t * 6 - 15) + 10)
def lerp(self, a, b, x):
return a + x * (b - a)
def grad(self, hash, x, y, z):
h = hash & 15
u = x if h < 8 else y
v = y if h < 4 else (x if h in (12, 14) else z)
return (u if (h & 1) == 0 else -u) + (v if (h & 2) == 0 else -v)
def noise(self, x, y=0, z=0):
X = int(x) & 255
Y = int(y) & 255
Z = int(z) & 255
x -= math.floor(x)
y -= math.floor(y)
z -= math.floor(z)
u = self.fade(x)
v = self.fade(y)
w = self.fade(z)
A = self.permutation[X] + Y
AA = self.permutation[A] + Z
AB = self.permutation[A + 1] + Z
B = self.permutation[X + 1] + Y
BA = self.permutation[B] + Z
BB = self.permutation[B + 1] + Z
return self.lerp(
self.lerp(
self.lerp(self.grad(self.permutation[AA], x, y, z),
self.grad(self.permutation[BA], x - 1, y, z),
u
),
self.lerp(
self.grad(self.permutation[AB], x, y - 1, z),
self.grad(self.permutation[BB], x - 1, y - 1, z),
u
),
v
)
# 粒子系统
class ParticleSystem:
def __init__(self, num_particles=500):
self.particles = []
self.colors = [
(255, 0, 100), (0, 200, 255), (100, 255, 0),
(255, 150, 0), (180, 0, 255), (0, 150, 255)
]
self.generate_particles(num_particles)
def generate_particles(self, num):
for _ in range(num):
x = random.randint(0, SCREEN_WIDTH)
y = random.randint(0, SCREEN_HEIGHT)
size = random.randint(2, 6)
speed = random.uniform(0.5, 3.0)
angle = random.uniform(0, 2 * math.pi)
color = random.choice(self.colors)
life = random.randint(100, 300)
self.particles.append([x, y, size, speed, angle, color, life])
def update(self, noise_gen, time):
for p in self.particles:
# 使用Perlin噪声影响运动方向
noise_val = noise_gen.noise(p[0] * 0.01, p[1] * 0.01, time * 0.1)
p[4] += noise_val * 0.2
# 更新位置
p[0] += math.cos(p[4]) * p[3]
p[1] += math.sin(p[4]) * p[3]
# 生命周期
p[6] -= 1
# 边界处理
if p[0] < -50 or p[0] > SCREEN_WIDTH + 50 or p[1] < -50 or p[1] > SCREEN_HEIGHT + 50 or p[6] <= 0:
p[0] = random.randint(0, SCREEN_WIDTH)
p[1] = random.randint(0, SCREEN_HEIGHT)
p[6] = random.randint(100, 300)
def draw(self, surface):
for p in self.particles:
alpha = min(255, p[6] * 2)
color = (p[5][0], p[5][1], p[5][2], alpha)
pygame.draw.circle(surface, color, (int(p[0]), int(p[1])), p[2])
# 动态几何形状
class DynamicShapes:
def __init__(self):
self.shapes = []
self.generate_shapes()
def generate_shapes(self):
# 创建不同几何形状
for _ in range(8):
shape_type = random.choice(['circle', 'triangle', 'square', 'hexagon'])
size = random.randint(80, 200)
x = random.randint(size, SCREEN_WIDTH - size)
y = random.randint(size, SCREEN_HEIGHT - size)
color = (
random.randint(50, 200),
random.randint(50, 200),
random.randint(50, 200),
random.randint(30, 100)
)
speed = random.uniform(0.5, 2.0)
angle = random.uniform(0, 2 * math.pi)
rotation_speed = random.uniform(-0.03, 0.03)
self.shapes.append([shape_type, x, y, size, color, speed, angle, rotation_speed, 0])
def update(self, time):
for s in self.shapes:
# 更新位置
s[1] += math.cos(s[6]) * s[5]
s[2] += math.sin(s[6]) * s[5]
s[7] += s[7] # 旋转
s[8] = time # 存储时间用于动画
# 边界反弹
if s[1] < s[3] or s[1] > SCREEN_WIDTH - s[3]:
s[6] = math.pi - s[6]
if s[2] < s[3] or s[2] > SCREEN_HEIGHT - s[3]:
s[6] = -s[6]
def draw(self, surface):
for s in self.shapes:
color = s[4]
if s[0] == 'circle':
pygame.draw.circle(surface, color, (int(s[1]), int(s[2])), s[3], 3)
# 内部动画
inner_size = s[3] * 0.7 + math.sin(s[8] * 0.05) * 10
pygame.draw.circle(surface, color, (int(s[1]), int(s[2])), int(inner_size), 1)
elif s[0] == 'triangle':
points = []
for i in range(3):
angle = s[7] + i * (2 * math.pi / 3)
px = s[1] + math.cos(angle) * s[3]
py = s[2] + math.sin(angle) * s[3]
points.append((px, py))
pygame.draw.polygon(surface, color, points, 3)
elif s[0] == 'square':
rect = pygame.Rect(0, 0, s[3]*2, s[3]*2)
rect.center = (s[1], s[2])
pygame.draw.rect(surface, color, rect, 3, border_radius=10)
elif s[0] == 'hexagon':
points = []
for i in range(6):
angle = s[7] + i * (math.pi / 3)
px = s[1] + math.cos(angle) * s[3]
py = s[2] + math.sin(angle) * s[3]
points.append((px, py))
pygame.draw.polygon(surface, color, points, 3)
# 设置壁纸的函数
def set_wallpaper(image_path):
key = win32con.HKEY_CURRENT_USER
subkey = "Control Panel\\Desktop"
try:
reg_key = win32api.RegOpenKeyEx(key, subkey, 0, win32con.KEY_WRITE)
win32api.RegSetValueEx(reg_key, "WallpaperStyle", 0, win32con.REG_SZ, "10") # 填充
win32api.RegSetValueEx(reg_key, "TileWallpaper", 0, win32con.REG_SZ, "0")
win32api.RegCloseKey(reg_key)
win32gui.SystemParametersInfo(win32con.SPI_SETDESKWALLPAPER, image_path, win32con.SPIF_UPDATEINIFILE)
return True
except Exception as e:
print(f"设置壁纸时出错: {e}")
return False
# 壁纸更新线程
class WallpaperUpdater(threading.Thread):
def __init__(self, update_interval=5):
super().__init__()
self.update_interval = update_interval
self.running = True
self.daemon = True
def run(self):
temp_file = os.path.join(os.getenv('TEMP'), 'python_wallpaper.bmp')
while self.running:
# 保存当前帧为临时文件
pygame.image.save(screen, temp_file)
# 设置壁纸
set_wallpaper(temp_file)
# 等待下一次更新
pygame.time.wait(self.update_interval * 1000)
def stop(self):
self.running = False
# 主函数
def main():
global screen
# 创建噪声生成器
noise_gen = PerlinNoiseGenerator()
# 创建粒子系统
particle_system = ParticleSystem(800)
# 创建动态形状
dynamic_shapes = DynamicShapes()
# 创建半透明表面用于轨迹效果
overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
# 启动壁纸更新线程
wallpaper_thread = WallpaperUpdater(update_interval=10)
wallpaper_thread.start()
# 创建控制窗口
control_window = pygame.display.set_mode((300, 200))
pygame.display.set_caption("动态壁纸控制器")
# 字体
font = pygame.font.SysFont(None, 24)
# 主循环
clock = pygame.time.Clock()
time = 0
running = True
while running:
# 处理控制窗口事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
elif event.key == pygame.K_r:
# 重置粒子系统
particle_system = ParticleSystem(800)
elif event.key == pygame.K_s:
# 重置动态形状
dynamic_shapes = DynamicShapes()
# 更新
time += 0.05
particle_system.update(noise_gen, time)
dynamic_shapes.update(time)
# 绘制
# 添加淡出效果(创建运动轨迹)
overlay.fill((0, 0, 0, 15), special_flags=pygame.BLEND_RGBA_MULT)
screen.blit(overlay, (0, 0))
# 绘制粒子
particle_system.draw(screen)
# 绘制动态形状
dynamic_shapes.draw(screen)
# 绘制控制窗口
control_window.fill((40, 40, 60))
# 显示控制信息
texts = [
"动态壁纸生成器",
"按 ESC 退出程序",
"按 R 重置粒子",
"按 S 重置形状",
f"粒子数量: {len(particle_system.particles)}",
f"形状数量: {len(dynamic_shapes.shapes)}"
]
for i, text in enumerate(texts):
text_surface = font.render(text, True, (200, 220, 255))
control_window.blit(text_surface, (20, 20 + i * 30))
pygame.display.flip()
clock.tick(30)
# 清理
wallpaper_thread.stop()
wallpaper_thread.join(timeout=1.0)
pygame.quit()
sys.exit()
if __name__ == "__main__":
main()
运行说明
安装必要的库:
bash pip install pygame numpy pywin32运行程序后会出现控制窗口:
按 ESC 退出程序 按 R 重置粒子系统 按 S 重置几何形状程序会自动将生成的动画设置为桌面壁纸(每10秒更新一次)
技术亮点
Perlin噪声:用于生成自然的粒子运动路径,避免机械感
粒子系统:800个彩色粒子在屏幕上流动,每个粒子有独立属性
动态几何:随机生成的几何图形在屏幕上移动、旋转和缩放
壁纸设置:使用Win32 API实时更新桌面壁纸
多线程:壁纸更新在后台线程进行,不影响主渲染循环
淡出效果:通过半透明覆盖层创建粒子轨迹效果
响应式设计:自动适应不同屏幕分辨率
自定义选项
你可以调整以下参数来改变壁纸效果:
ParticleSystem 中的粒子数量
DynamicShapes 中的形状数量和类型
WallpaperUpdater 中的更新间隔
修改粒子颜色和形状颜色
调整粒子大小和速度范围
注意事项
- 该程序仅在Windows系统上测试通过
- 首次运行时可能需要允许程序更改系统设置
- 退出程序后,最后一次生成的壁纸会保留在桌面上
- 对于性能较弱的电脑,可以减少粒子数量或增大壁纸更新间隔
- 这个动态壁纸生成器展示了Python在创意编程和图形生成方面的强大能力,结合了数学算法、粒子系统和系统编程技术。

浙公网安备 33010602011771号