Python 实战:基于 Pygame 的微信风格生日祝福程序
本文介绍了如何使用 Python 的 Pygame 库开发一个模拟微信视频通话界面的生日祝福程序。项目包含来电加载界面和主祝福界面,实现了蛋糕展示、动态文字渲染及随机烟花动画效果。文章详细讲解了环境搭建、资源加载、事件监听、面向对象设计及完整代码实现,并对常见路径错误和性能优化提供了建议。该程序适合作为 Python 图形界面编程的学习案例。

本文介绍了如何使用 Python 的 Pygame 库开发一个模拟微信视频通话界面的生日祝福程序。项目包含来电加载界面和主祝福界面,实现了蛋糕展示、动态文字渲染及随机烟花动画效果。文章详细讲解了环境搭建、资源加载、事件监听、面向对象设计及完整代码实现,并对常见路径错误和性能优化提供了建议。该程序适合作为 Python 图形界面编程的学习案例。

在 Python 开发中,Pygame 是一个功能强大的第三方库,主要用于编写 2D 游戏和多媒体应用。除了传统的游戏开发,它也非常适合制作交互式界面、动画演示以及创意小工具。
本文介绍如何使用 Pygame 实现一个模拟微信视频通话界面的生日祝福程序。该程序包含两个主要阶段:首先是模拟来电的加载界面,其次是进入主界面后展示蛋糕、祝福语及烟花特效。通过这个项目,读者可以深入理解 Pygame 的事件循环、面向对象编程(OOP)在图形界面中的应用以及资源管理的基本方法。
确保已安装 Python 3.x 版本。在命令行中运行以下命令安装 Pygame 库:
pip install pygame
为了代码的可维护性,建议将项目文件组织如下:
project_root/
├── main.py # 主程序入口
└── resource/ # 资源文件夹
├── h-2.JPG # 图标
├── baligonglu.png # 背景图
├── cake.png # 蛋糕图片
├── firework1.png # 烟花素材 1
├── firework2.png # 烟花素材 2
├── firework3.png # 烟花素材 3
├── firework4.png # 烟花素材 4
├── 繁星糖果.ttf # 字体文件
├── 爆炸.wav # 音效
├── 铃声.wav # 来电铃声
└── 生日快乐.wav # 背景音乐
注意: 请确保所有图片和音频文件的名称与代码中引用的名称完全一致,否则会导致 FileNotFoundError。
程序启动时,需要初始化 Pygame 模块并加载所需的媒体资源。为了方便路径处理,我们定义一个辅助函数 rp 来拼接绝对路径。
import os
def rp(path):
"""获取资源文件的绝对路径"""
return os.path.join(os.path.dirname(os.path.abspath(__file__)), path)
import random
import pygame, sys
pygame.mixer.init()
from pygame.locals import *
pygame.init()
本程序采用双界面设计:
Pygame 没有内置的 Button 组件,因此需要通过监听鼠标事件来模拟按钮交互。
MOUSEBUTTONDOWN: 鼠标按键按下事件。event.pos: 返回鼠标坐标元组 (x, y)。当检测到鼠标坐标落在预设的矩形区域内且发生按下事件时,触发界面跳转逻辑。
为了实现烟花的动态效果,我们采用对象池的思想:
pygame.display.update()) 形成连续动画。我们将不同的视觉元素封装为独立的类,便于管理和扩展。
仅负责绘制背景图。
静态绘制蛋糕图片。
使用 font.render() 渲染文本,支持多行不同颜色的文字显示。
包含坐标属性 (x, y) 和图片属性 (fire)。在 display 方法中更新位置并播放音效。
以下是修复了原代码中的路径引用错误、缩进问题及逻辑漏洞后的完整可运行代码。
# -*- coding: utf-8 -*-
import os
import random
import pygame, sys
# 定义路径辅助函数
def rp(path):
return os.path.join(os.path.dirname(os.path.abspath(__file__)), path)
# 初始化 Pygame
pygame.mixer.init()
from pygame.locals import *
pygame.init()
# 设置常量
fps = 30
fpsClock = pygame.time.Clock()
SCREEN_WIDTH = 340
SCREEN_HEIGHT = 700
# 加载资源
try:
icon = pygame.image.load(rp('resource/h-2.JPG'))
bali = pygame.image.load(rp('resource/baligonglu.png'))
cake = pygame.image.load(rp('resource/cake.png'))
fire1 = pygame.image.load(rp('resource/firework1.png'))
fire2 = pygame.image.load(rp('resource/firework2.png'))
fire3 = pygame.image.load(rp('resource/firework3.png'))
fire4 = pygame.image.load(rp('resource/firework4.png'))
firesImg = [fire1, fire2, fire3, fire4]
font = pygame.font.Font(rp('resource/繁星糖果.ttf'), 50)
font1 = pygame.font.Font(rp('resource/繁星糖果.ttf'), 30)
boom_sound = pygame.mixer.Sound(rp('resource/爆炸.wav'))
pygame.mixer.music.load(rp('resource/铃声.wav'))
pygame.mixer.music.play(-1) # 循环播放铃声
except Exception as e:
print(f"资源加载失败:{e}")
sys.exit()
# 设置窗口
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption('生日快乐')
pygame.display.set_icon(icon)
# --- 类定义 ---
class Load(object):
def display(self):
screen.blit(bali, (0, 0))
pygame.display.update()
class Cake(object):
def display(self):
screen.blit(cake, (46, 300))
class Firework(object):
def __init__(self):
self.x = random.randint(20, 220)
self.y = random.randint(20, 280)
self.fire = random.choice(firesImg)
def display(self):
global fires
screen.blit(self.fire, (self.x, self.y))
try:
boom_sound.play()
except:
pass
# 安全地移除并添加新烟花
if len(fires) > 0:
fires.remove(random.choice(fires))
fires.append(Firework())
class Birth(object):
def __init__(self):
self.text1 = '生'
self.text2 = '日'
self.text3 = '快'
self.text4 = '乐'
self.text5 = '历经千帆'
self.text6 = '归来依旧'
self.text7 = '19'
# 渲染文本,True 表示抗锯齿
self.render1 = font.render(self.text1, True, (128, 128, 0))
self.render2 = font.render(self.text2, True, (128, 0, 128))
self.render3 = font.render(self.text3, True, (0, 128, 128))
self.render4 = font.render(self.text4, True, (255, 255, 255))
self.render5 = font1.render(self.text5, True, (128, 0, 0))
self.render6 = font1.render(self.text6, True, (255, 255, 255))
self.render7 = font1.render(self.text7, True, (128, 0, 128))
def display(self):
screen.blit(self.render1, (155, 20))
screen.blit(self.render2, (155, 80))
screen.blit(self.render3, (155, 160))
screen.blit(self.render4, (155, 220))
screen.blit(self.render5, (110, 550))
screen.blit(self.render6, (110, 590))
screen.blit(self.render7, (155, 630))
# --- 实例化对象 ---
l = Load()
c = Cake()
b = Birth()
fires = []
fire_num = 6
for i in range(fire_num):
fires.append(Firework())
# --- 第一阶段:加载界面 ---
load = True
while load:
l.display()
for event in pygame.event.get():
if event.type == pygame.MOUSEBUTTONDOWN:
# 模拟接听按钮区域:x[248,315], y[540,600]
if event.pos[0] in range(248, 315) and event.pos[1] in range(540, 600):
load = False
break
# 切换背景音乐
pygame.mixer.music.stop()
pygame.mixer.music.load(rp('resource/生日快乐.wav'))
pygame.mixer.music.play(-1)
# --- 第二阶段:主界面 ---
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
screen.fill((0, 0, 0)) # 黑色背景
# 绘制内容
b.display()
c.display()
for f in fires:
f.display()
fpsClock.tick(fps)
pygame.display.update()
pygame.quit()
sys.exit()
如果在运行时遇到 FileNotFoundError,请检查 resource 文件夹是否存在,以及文件名是否拼写正确。建议使用 os.path.abspath(__file__) 来获取当前脚本所在目录,避免相对路径在不同操作系统下的兼容性问题。
如果烟花数量过多导致卡顿,可以减少 fire_num 的数量。此外,boom_sound.play() 在每一帧都被调用可能会造成声音重叠过多,实际项目中应增加冷却时间判断。
当前代码硬编码了窗口大小 (340, 700),这通常用于模拟手机竖屏。若要在 PC 端全屏运行,可将 set_mode 参数改为 (0, 0) 并使用 pygame.FULLSCREEN 标志。
Birth 类的 __init__ 方法中的 text 变量。resource 文件夹中的图片文件即可。render 函数中的 RGB 元组值。本项目通过 Pygame 实现了基础的 GUI 交互与动画效果。虽然相比现代前端框架,Pygame 在处理复杂 UI 时显得较为底层,但它对于学习计算机图形学基础、事件驱动编程以及多媒体控制具有极高的教学价值。掌握此类技能后,开发者可以轻松拓展至更复杂的 2D 游戏开发领域。
希望这个程序能为你的朋友带来一份独特的数字礼物。当然,技术只是锦上添花,实际的关怀与陪伴才是最重要的。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online