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()

运行说明

  1. 安装必要的库:

    bash
    pip install pygame numpy pywin32
  2. 运行程序后会出现控制窗口:

    按 ESC 退出程序
    按 R 重置粒子系统
    按 S 重置几何形状
  3. 程序会自动将生成的动画设置为桌面壁纸(每10秒更新一次)

技术亮点

  1. Perlin噪声:用于生成自然的粒子运动路径,避免机械感

  2. 粒子系统:800个彩色粒子在屏幕上流动,每个粒子有独立属性

  3. 动态几何:随机生成的几何图形在屏幕上移动、旋转和缩放

  4. 壁纸设置:使用Win32 API实时更新桌面壁纸

  5. 多线程:壁纸更新在后台线程进行,不影响主渲染循环

  6. 淡出效果:通过半透明覆盖层创建粒子轨迹效果

  7. 响应式设计:自动适应不同屏幕分辨率

自定义选项

  1. 你可以调整以下参数来改变壁纸效果:

  2. ParticleSystem 中的粒子数量

  3. DynamicShapes 中的形状数量和类型

  4. WallpaperUpdater 中的更新间隔

  5. 修改粒子颜色和形状颜色

  6. 调整粒子大小和速度范围

注意事项

  1. 该程序仅在Windows系统上测试通过
  2. 首次运行时可能需要允许程序更改系统设置
  3. 退出程序后,最后一次生成的壁纸会保留在桌面上
  4. 对于性能较弱的电脑,可以减少粒子数量或增大壁纸更新间隔
  5. 这个动态壁纸生成器展示了Python在创意编程和图形生成方面的强大能力,结合了数学算法、粒子系统和系统编程技术。
posted @ 2026-02-08 20:06  gccbuaa  阅读(39)  评论(0)    收藏  举报