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 * 2def 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 & 15u = x if h < 8 else yv = 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) & 255Y = int(y) & 255Z = int(z) & 255x -= 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] + YAA = self.permutation[A] + ZAB = self.permutation[A + 1] + ZB = self.permutation[X + 1] + YBA = self.permutation[B] + ZBB = self.permutation[B + 1] + Zreturn 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) * 10pygame.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_USERsubkey = "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 Trueexcept Exception as e:print(f"设置壁纸时出错: {e}")return False# 壁纸更新线程
class WallpaperUpdater(threading.Thread):def __init__(self, update_interval=5):super().__init__()self.update_interval = update_intervalself.running = Trueself.daemon = Truedef 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 = 0running = Truewhile running:# 处理控制窗口事件for event in pygame.event.get():if event.type == pygame.QUIT:running = Falseelif event.type == pygame.KEYDOWN:if event.key == pygame.K_ESCAPE:running = Falseelif event.key == pygame.K_r:# 重置粒子系统particle_system = ParticleSystem(800)elif event.key == pygame.K_s:# 重置动态形状dynamic_shapes = DynamicShapes()# 更新time += 0.05particle_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在创意编程和图形生成方面的强大能力,结合了数学算法、粒子系统和系统编程技术。