【python】开发了一个电子桌面桌宠,会要饭,满屏跑,效果太棒了

【python】开发了一个电子桌面桌宠,会要饭,满屏跑,效果太棒了
文档版本1.0适用平台Windows开发语言Python 3.10GUI 框架PySide6当前宠物橘白猫「小橘」、藏獒「小山」目录项目概览技术栈项目结构系统架构模块详解5.1 入口 应用生命周期 (main.py / app.py)5.2 动画资源管理 (assets.py)5.3 数据模型 (models.py)5.4 动画状态机 (controller.py)5.5 透明渲染窗口 (window.py)5.6 设置持久化 (settings.py)5.7 路径解析 (resources.py)5.8 宠物选择 目录 (selection.py / pet_catalog.py)动画清单格式 (animations.json)交互系统7.1 鼠标眼动追踪7.2 拖拽与点击7.3 自主行为7.4 系统托盘精灵生成管线构建与打包测试配置与环境变量1. 项目概览本项目是一款离线的 Windows 桌面宠物应用。宠物以透明、无边框、始终置顶的窗口呈现在桌面上支持自主行走、奔跑、休息观察鼠标并响应用户的点击、拖拽、喂食等操作。当前版本包含两只宠物宠物ID昵称状态数总帧数步行动画橘白猫orange_cat小橘178716 帧藏獒tibetan_mastiff小山1713720 帧2. 技术栈层级技术版本用途语言Python≥3.10主体逻辑GUIPySide6≥6.6, 7透明窗口、渲染、系统托盘图像处理Pillow≥9.1, 12精灵图加载与处理打包PyInstaller≥5.1生成独立 .exe精灵生成NumPy / OpenCV≥1.23 / ≥4.5光流插值、色键去底配置格式JSON—动画清单、用户设置设置存储Windows Registry—开机自启注册表项3. 项目结构桌面宠物游戏/ ├── main.py # 应用入口 ├── run.bat # 快速启动脚本 ├── build.bat # 完整构建脚本 ├── pyproject.toml # 项目元数据、Ruff 配置 ├── requirements.txt # 运行时依赖 ├── requirements-dev.txt # 开发构建依赖 ├── orange_cat_pet.spec # PyInstaller 打包配置 │ ├── desktop_pet/ # 核心程序包 │ ├── __init__.py # 版本号 (1.0.0) │ ├── app.py # 应用生命周期协调器 │ ├── window.py # 透明宠物窗口 │ ├── controller.py # 动画状态机 │ ├── assets.py # 动画资源加载器 │ ├── models.py # 数据模型 枚举 │ ├── settings.py # 设置持久化 自启管理 │ ├── resources.py # 路径解析 (源码/打包) │ ├── selection.py # 宠物选择对话框 │ └── pet_catalog.py # 宠物定义目录 │ ├── assets/ # 游戏资源 │ ├── animations.json # 猫咪动画清单 │ ├── tibetan_mastiff_animations.json # 藏獒动画清单 │ ├── IMAGEGEN_PROMPTS.md # 精灵图 AI 生成提示词 │ ├── icons/orange_cat.ico # 应用图标 │ └── sprites/ # 精灵图表 / 生成帧 │ ├── generated/ # 猫咪已处理帧 (87 张 PNG) │ └── tibetan_mastiff/generated/ # 藏獒已处理帧 (137 张 PNG) │ ├── tools/ # 构建工具 │ ├── build_sprites.py # 猫精灵抽取 步态插值 │ ├── build_mastiff_sprites.py # 藏獒精灵抽取 动画生成 │ └── make_icon.py # Windows .ico 生成 │ ├── tests/ # 单元测试 │ ├── test_assets.py # 资源完整性测试 │ ├── test_models_settings.py # 设置序列化测试 │ ├── test_qt_smoke.py # Qt 烟雾测试 │ └── test_selection.py # 选择对话框测试 │ ├── build/orange_cat_pet/ # PyInstaller 构建中间产物 └── dist/OrangeCatPet/ # 最终发布目录4. 系统架构┌──────────────────────────────────────────────────────────┐ │ main.py │ │ (入口, HIGHDPI 设置) │ └─────────────────┬────────────────────────────────────────┘ │ ┌─────────────────▼────────────────────────────────────────┐ │ DesktopPetApplication │ │ ┌──────────────────────────────────────────────────┐ │ │ │ 应用协调: QApplication / SettingsStore │ │ │ │ 宠物切换: _activate_pet() / _choose_pet() │ │ │ │ 托盘管理: _create_or_refresh_tray() │ │ │ └──────────────────────────────────────────────────┘ │ └─────────────────┬────────────────────────────────────────┘ │ ┌─────────────┼─────────────┐ ▼ ▼ ▼ ┌───────┐ ┌──────────┐ ┌─────────────┐ │Assets │ │Controller│ │Selection │ │Library│ │(状态机) │ │Dialog │ └───┬───┘ └────┬─────┘ └─────────────┘ │ │ ▼ ▼ ┌─────────────────────────────────────┐ │ PetWindow │ │ ┌───────────────────────────────┐ │ │ │ QWidget (透明, 置顶, 无边框) │ │ │ │ ┌─────────────────────────┐ │ │ │ │ │ QPainter 渲染当前帧 │ │ │ │ │ │ 眼球追踪计算 绘制 │ │ │ │ │ ├─────────────────────────┤ │ │ │ │ │ motion_timer (30ms) │ │ │ │ │ │ behaviour_timer (1s) │ │ │ │ │ │ single_click_timer │ │ │ │ │ ├─────────────────────────┤ │ │ │ │ │ 鼠标事件处理 │ │ │ │ │ │ 右键上下文菜单 │ │ │ │ │ └─────────────────────────┘ │ │ │ └───────────────────────────────┘ │ └─────────────────────────────────────┘5. 模块详解5.1 入口 应用生命周期 (main.py / app.py)main.py— 应用入口设置QT_ENABLE_HIGHDPI_SCALING1环境变量后创建并启动DesktopPetApplication。DesktopPetApplication— 应用生命周期协调器 (desktop_pet/app.py:16)职责如下初始化QApplication应用名 “桌面宠物伙伴”组织名 “OrangeCatDesktopPet”setQuitOnLastWindowClosed(False)确保关闭窗口后隐藏到托盘而非退出run()方法启动时先弹出宠物选择对话框选择后进入 Qt 事件循环_activate_pet()方法切换宠物时销毁旧窗口、重新加载动画库、创建新窗口、刷新托盘quit()方法保存设置、隐藏托盘、退出应用应用级信号流window.request_quit ────────── app.quit() window.request_pet_selection ─ app.choose_pet()5.2 动画资源管理 (assets.py)AnimationLibrary(desktop_pet/assets.py:12) — 从 JSON 动画清单文件加载并管理所有动画资源。核心功能方法说明_load()解析 JSON 清单为 17 个PetState构建AnimationClipclip(state)返回指定状态的AnimationClippixmap(frame)惰性加载并缓存QPixmap按路径缓存避免重复文件 I/O加载过程读取 JSON → 解析canvas尺寸遍历PetState枚举 → 从animations对象中取出帧数组每帧解析path、duration_ms、eyes眼球锚点、eye_radius、hitbox校验文件存在性缺失直接抛FileNotFoundError构建不可变AnimationClipfrozendataclass5.3 数据模型 (models.py)PetState(desktop_pet/models.py:9) — 17 种宠物状态的字符串枚举枚举值中文枚举值中文IDLE待机BLINK眨眼WATCH观察WALK行走RUN奔跑SIT坐下LIE趴下SLEEP睡觉WAKE醒来STRETCH伸懒腰GROOM舔毛YAWN打哈欠HAPPY开心SURPRISED惊讶ANGRY生气EAT进食DRAGGED被拖拽FrameMetadata(desktop_pet/models.py:29) — 不可变帧数据字段类型说明pathPath图片文件路径duration_msint帧持续时间 (最小值 16ms)eyestuple[tuple[float, float], ...]眼球锚点坐标序列eye_radiustuple[float, float]瞳孔基准半径 (x, y)hitboxtuple[int, int, int, int]碰撞检测区域 (x, y, w, h)AnimationClip(desktop_pet/models.py:38) — 不可变动画片段字段类型说明statePetState所属状态framestuple[FrameMetadata, ...]帧序列loopbool是否循环播放next_statePetState | None非循环动画结束后的过渡状态PetSettings(desktop_pet/models.py:46) — 用户设置数据类可变支持from_dict/to_dictJSON 序列化含输入校验。5.4 动画状态机 (controller.py)PetController(desktop_pet/controller.py:9) — 管理动画状态切换与帧推进。优先级系统每个状态有优先级数值高优先级可抢占低优先级非循环动画播放中会锁住优先级状态100DRAGGED90EAT80HAPPY75SURPRISED,ANGRY65WAKE55STRETCH,GROOM,YAWN40SLEEP25RUN20WALK15WATCH10SIT,LIE8BLINK5IDLE状态切换逻辑 (set_state,controller.py:51)if 新状态 当前状态 and not force → 忽略 if 当前非循环动画未播完 and not force and 新优先级 当前优先级 → 忽略 否则 → 切换状态, 重置帧索引, 发射信号, 重新调度定时器帧推进 (_advance,controller.py:76)if 还有下一帧 → frame_index elif 循环动画 → 回到第 0 帧 else (非循环动画播完) → 过渡到 next_state (默认 IDLE) 发射 frame_changed → 重新调度定时器5.5 透明渲染窗口 (window.py)PetWindow(desktop_pet/window.py:18) — 继承QWidget所有渲染与交互的核心。窗口属性属性值固定尺寸library.canvas_size(256×256)WA_TranslucentBackgroundTrueWA_NoSystemBackgroundTrueautoFillBackgroundFalse窗口标志FramelessWindowHint | Tool | WindowStaysOnTopHint定时器定时器间隔用途motion_timer30ms行走/奔跑位移 ±2px (走) / ±5px (跑)behaviour_timer1s饥饿/心情更新、随机行为决策single_click_timer单次区分单击与双击渲染管线 (paintEvent)1. QPainter(painter) 描画到 Widget 2. 判断朝向: facing_right 决定是否水平翻转 3. 绘制精灵: drawPixmap(target_rect, pixmap) 4. 眼球追踪计算: a. 获取全局鼠标位置 QCursor.pos() b. 映射到 Widget 局部坐标 c. 遍历 frame.eyes 中每只眼睛的锚点 d. 计算方向向量 → 归一化 → 瞳孔偏移量 e. 绘制白色虹膜 黑色瞳孔 白色高光点多显示器支持使用QApplication.screenAt(center)获取当前所在屏幕使用availableGeometry()获取不含任务栏的工作区移动时通过_clamped_position()约束宠物不出工作区边界右键菜单动态构建QMenu包含以下选项选项功能喂食切换到EAT状态召回将宠物移到当前屏幕中心底部选择宠物弹出选择对话框暂停冻结/恢复动画和行为置顶切换WindowStaysOnTopHint开机启动写入/删除注册表自启项退出保存设置 → 完全退出5.6 设置持久化 (settings.py)SettingsStore(desktop_pet/settings.py:22) — JSON 设置文件的读写封装。存储路径说明%LOCALAPPDATA%\OrangeCatDesktopPet\settings.json默认路径由ORANGE_CAT_DATA_DIR环境变量覆盖测试用途原子保存机制 (save,settings.py:35)写入 .tmp 文件 → 调用 Path.replace() 原子替换原文件容错机制 (load,settings.py:26)任何异常 (OSError, ValueError, TypeError, JSONDecodeError) → 返回默认 PetSettingsAutoStartManager(desktop_pet/settings.py:45) — 通过操作HKCU\Software\Microsoft\Windows\CurrentVersion\Run注册表键实现开机自启。is_enabled()— 读取注册表判断是否已有启动项set_enabled(bool)— 写入或删除注册表值command()— 生成正确的启动命令行区分源码运行 vs PyInstaller 打包5.7 路径解析 (resources.py)resource_path(relative_path)— 统一路径解析if sys.frozen (PyInstaller 打包): 返回 Path(sys._MEIPASS) / relative_path else: 返回 Path(__file__).resolve().parents[1] / relative_path支持参数形式resource_path(assets/animations.json)— 字符串resource_path(Path(assets/sprites/generated/idle/00.png))—Path对象5.8 宠物选择对话框 (selection.py / pet_catalog.py)PetSelectionDialog(desktop_pet/selection.py) — 模态对话框 (960×630)首次启动时弹出其后可通过右键菜单打开。展示所有宠物的预览卡片图片、名称、描述、选中按钮当前已选宠物高亮显示点击确定后触发app._activate_pet()PetDefinition(desktop_pet/pet_catalog.py) — 宠物定义的不可变 dataclass字段类型说明pet_idstr宠物唯一标识display_namestr中文显示名descriptionstr简短描述manifestPath动画清单路径preview_imagePath选择页预览图路径frame_countint总帧数当前宠物目录小橘:orange_cat→assets/animations.json(87 帧)小山:tibetan_mastiff→assets/tibetan_mastiff_animations.json(137 帧)6. 动画清单格式 (animations.json){ version: 2, canvas: [256, 256], animations: { idle: { frames: [ { path: assets/sprites/generated/idle/00.png, duration_ms: 100, eyes: [[120.3, 80.5], [140.2, 80.5]], eye_radius: [5.0, 4.0], hitbox: [10, 20, 236, 236] } ], loop: true }, eat: { frames: [ /* ... */ ], loop: false, next_state: idle } } }字段说明字段类型必填说明versionint否清单格式版本canvas[int, int]是画布尺寸animations.state.framesarray是帧数组 (至少 1 帧)frames[].pathstring是相对路径frames[].duration_msint是帧显示时长 (ms)frames[].eyes[[float, float]]否眼球锚点坐标frames[].eye_radius[float, float]否瞳孔半径默认 [5, 4]frames[].hitbox[int, int, int, int]否点击碰撞区animations.state.loopbool否是否循环默认trueanimations.state.next_statestring否播完后过渡到的状态7. 交互系统7.1 鼠标眼动追踪每帧渲染时实时计算瞳孔位置产生「宠物注视鼠标」的效果。算法步骤1. 获取全局鼠标坐标: QCursor.pos() 2. 映射到 Widget 坐标系: widget-mapFromGlobal(global_pos) 3. 计算宠物中心: (width/2, height/2) 4. 对每只眼睛的锚点 (eye_x, eye_y): 5. dx mouse_x - eye_x 6. dy mouse_y - eye_y 7. dist sqrt(dx² dy²) 8. scale 1 - clamp(dist / max_distance, 0, 1) 9. pupil_x eye_x normalize(dx) * max_offset * scale 10. pupil_y eye_y normalize(dy) * max_offset * scale 11. 绘制: - QColor(255, 255, 255, 220) 画白色虹膜 - QColor(20, 20, 20, 235) 画黑色瞳孔在偏移位置 - QColor(255, 255, 255, 180) 画小白色高光点7.2 拖拽与点击事件处理方式mousePressEvent记录拖拽起点启动单击计时器 (300ms)mouseMoveEvent超过拖拽阈值 (4px) 后进入DRAGGED状态实时更新窗口位置mouseReleaseEvent结束拖拽回到IDLE保存位置mouseDoubleClickEvent取消单击计时器切换到HAPPY状态单击超时 (300ms)切换到SURPRISED状态contextMenuEvent弹出右键菜单7.3 自主行为behaviour_timer(1 秒间隔) 执行以下逻辑饥饿值更新每秒 -0.02高活跃度状态额外 -0.03心情值更新非暂停状态下微调随机行为决策根据饥饿值、心情值、当前状态概率性切换到行走、奔跑、坐下、趴下、睡觉、舔毛、伸懒腰、打哈欠、眨眼等状态边缘弹跳碰到屏幕边缘时调转方向 (facing_right not facing_right)7.4 系统托盘操作效果单击 / 双击托盘图标召回宠物 (call_home())托盘图标使用宠物HAPPY状态第一帧作为图标托盘 Tooltip显示{宠物名}桌宠托盘右键菜单与窗口右键菜单相同8. 精灵生成管线精灵制作与处理全流程由tools/build_sprites.py和tools/build_mastiff_sprites.py实现。整体流程原始 4×4 姿态图集 (cat_pose_atlas.png / mastiff_pose_atlas_v2.png) │ ▼ ┌─────────────────────────────────────────────┐ │ 1. 提取: 4×4 网格分割重叠区域扩展 │ │ 最大连通分量提取 → 透明背景角色 │ │ 2. 归一化: 各姿态统一到 256×256 画布 │ │ 保持底部基线对齐 │ │ 3. 去底: 洋红色 (#ff00ff) 色键 → 透明通道 │ │ 溢出色彩去除 (spill removal) │ └─────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ 步态关键帧 (walk_cycle_v5.png 的 4 张 │ │ 极值姿势: 左前/左后/右前/右后) │ │ │ │ │ ▼ │ │ Farneback 光流插值 (OpenCV) │ │ - 猫: 4 关键帧 → 3 中间帧/段 → 16 帧 │ │ - 藏獒: 4 关键帧 → 4 中间帧/段 → 20 帧 │ │ - RGBA 预乘处理避免透明边缘伪影 │ │ │ │ │ ▼ │ │ 状态动画: 对各姿态施加微妙运动变化 │ │ (位移 / 缩放 / 旋转) 实现呼吸、弹跳等效果 │ └─────────────────────────────────────────────┘ │ ▼ 输出: generated/ animations.json │ ▼ make_icon.py → orange_cat.ico光流插值关键细节使用 OpenCVcalcOpticalFlowFarneback对预乘 alpha 的 RGBA 数据做稠密光流估计每个像素通道独立插值alpha 通道参与计算但插值后 clamp 到 [0,255]透明背景区域alpha 0的 RGB 在插值前清零避免「透明像素 RGB 污染」9. 构建与打包环境准备python-m pip install-r requirements.txt# 运行时依赖python-m pip install-r requirements-dev.txt# 构建依赖完整构建流程build.bat按顺序执行tools/build_sprites.py → 生成猫咪精灵 tools/make_icon.py → 生成应用图标 pyinstaller --noconfirm --clean orange_cat_pet.spec → 打包PyInstaller 配置 (orange_cat_pet.spec)关键配置项入口脚本main.py窗口模式consoleFalse不显示控制台窗口包含资源目录assets/整体打包进_MEIPASS额外二进制 / 数据文件通过TOC清单指定输出dist/OrangeCatPet/OrangeCatPet.exe ← 最终可执行文件10. 测试运行测试$env:QT_QPA_PLATFORMoffscreen$env:ORANGE_CAT_DATA_DIR$PWD\.runtime\test-datapython-m unittest discover-s tests-v测试覆盖测试文件覆盖范围test_assets.py动画清单完整性、RGBA 图片验证、尺寸一致性、步态帧数、色键去底效果test_models_settings.pyPetSettings序列化/反序列化、边界值校验、默认值恢复test_qt_smoke.pyQt 环境可用性、窗口创建、动画剪辑加载test_selection.py选择对话框 UI 元素、宠物卡片渲染11. 配置与环境变量运行环境环境变量说明默认值QT_ENABLE_HIGHDPI_SCALING启用高 DPI 缩放1ORANGE_CAT_DATA_DIR设置文件存储目录 (用于测试)无QT_QPA_PLATFORMQt 平台插件 (测试用)无设置文件路径:%LOCALAPPDATA%\OrangeCatDesktopPet\settings.json格式: JSON字段:selected_pet,x,y,volume,always_on_top,autostart,hunger,mood,paused开机自启Windows 注册表项HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run └── OrangeCatDesktopPet pythonw.exe路径 main.py路径源码运行时使用pythonw.exe无控制台启动打包后直接指向OrangeCatPet.exe。本文档描述的项目版本为 1.0.0对应pyproject.toml中定义的版本。若想要获取代码和游戏 绿泡泡搜索 “码来的小朋友” 然后发送回复“14桌面宠物” 即可获取。