---
name: 桌面宠物
description: 桌面宠物换形象工具（v8 单窗口合成架构）。把用户提交的 1 张空闲展示 GIF + 若干个随机播放的动态 GIF 抠白底，生成 E:\desktop-pet 工程素材并注册为「每 5 秒随机播放」的特效，重新打包为桌面宠物 exe。用于把「布丁店长」替换成任意动漫卡通造型（猫/狗/二次元角色等）。附自包含抠图脚本 references/gen_swap.py（支持浅色中性白与深色 floodFill 两种抠图）。给协作者独立使用。
agent_created: true
---

# 桌面宠物 · 换形象技能（v8 单窗口合成架构）

把当前的「布丁店长」桌面宠物，**换成任意其它动漫卡通造型**。

使用者需要提供：
- **1 张空闲 GIF**：宠物待机时展示（自动呼吸、可拖拽、点击冒爱心）
- **若干个动态 GIF**（1~N 个）：每 5 秒随机挑一个播放（如「眨眼 / 伸懒腰 / 招手 / 跳跃」）

本技能负责：抠白底 → 生成素材 → 注册特效 → 打包 exe。

---

## 〇、适用前提（给协作者）

- 本机需已有**桌面宠物工程** `E:\desktop-pet\`，含 `desktop_pet.py`、`assets\`、`.venv\`
  （Python 3.12，已装 Pillow / pystray / pywin32 / opencv-python / numpy / PyInstaller）。
- 如需在另一台机器协作：把整个 `E:\desktop-pet` 目录复制过去即可（.venv 随工程走）。
- 本技能附带的 `references/gen_swap.py` 自包含（仅依赖 Pillow/opencv/numpy），用工程 `.venv` 运行。

---

## 一、触发条件

用户说类似：
- "把桌面宠物换成一只柴犬" / "换成一个二次元角色"
- "桌面宠物用我这套图：1 张待机 + N 张动作"
- "替换桌面宠物的形象" + 提交 1 个空闲 GIF + 若干个动作 GIF

---

## 二、向用户收集素材（必须齐）

用 `AskUserQuestion` 或对话确认，明确告诉用户需要：

| 序号 | 文件 | 说明 | 命名建议 |
|---|---|---|---|
| 1 | 空闲图 | GIF（或 PNG），待机展示 | `idle.gif` / `idle.png` |
| 2…N | 动作 | 若干个随机动作 GIF | `动作名.gif`（如 `眨眼.gif`、`伸懒腰.gif`）|

> 动作名会作为特效 key 显示在日志，也决定随机池成员；用中文或英文都可（如 `眨眼`、`stretch`）。
> 建议至少包含一个「眨眼」类，让待机更有生气（当前布丁版就是眨眼 + 伸懒腰）。
> 数量不限（1 个也可以，只是随机性较低）。

收到文件后，集中放到一个工作目录，例如 `E:\desktop-pet\swap_in\`。

---

## 三、准备工程（每个形象一个独立副本，避免互相覆盖）

> `E:\desktop-pet\assets\` 是布丁的素材，**直接覆盖会丢失布丁**。换形象请先复制整个工程：

```bash
cp -r E:/desktop-pet E:/desktop-pet-柴犬      # 每个形象一个目录
```

之后所有操作都在新目录（如 `E:\desktop-pet-柴犬\`）进行。

---

## 四、抠白底 + 生成素材（用附带的脚本）

脚本：`references/gen_swap.py`（需工程的 `.venv`，已含 Pillow / opencv-python / numpy）。

```bash
cd E:/desktop-pet-柴犬
/e/desktop-pet/.venv/Scripts/python.exe \
  <本技能目录>/references/gen_swap.py \
  --idle    swap_in/idle.gif \
  --action  眨眼=swap_in/眨眼.gif \
  --action  伸懒腰=swap_in/伸懒腰.gif \
  --action  招手=swap_in/招手.gif \
  --out     E:/desktop-pet-柴犬/assets \
  --method  light          # 浅色/白底用 light；深色/纯色背景改用 flood
```

脚本会生成：
- `cat.png` / `cat_blink.png` / `cat_side.png` —— 都取自空闲图首帧（运行时待机固定显示；眨眼已由特效承担，故后两者等同首帧）
- `effect_眨眼.png` / `effect_伸懒腰.png` / `effect_招手.png` —— 对应动作动画（APNG）

**抠图算法说明**（脚本内置 `remove_bg_light` / `remove_bg_flood`，由 `--method` 选）：
- `light`（默认）：**中性白连通分量法**——背景=中性浅白且亮(B>210、近中性)，猫身=暖色(B 偏低)；
  只把「接触图像边界」的连通分量判为外部背景，**浅色角色内部不会被误删**。适用于白底/浅色底。
- `flood`：从四角 `floodFill` 填充背景色（自动采样左上角像素）。适用于**深色 / 纯色背景**（light 法会抠不掉）。
- 脚本**只负责抠透明，不缩放**；缩放由 `desktop_pet.py` 加载时统一处理——默认动作按内容高度 `PET_H=200`，
  空闲图与旧眨眼 `blink` 按宽度 `PET_W=135`（高约216）。
- **alpha 二值化内置**：`np.where(bg,0,255)`，背景=0、角色=255，无半透明像素，杜绝紫边。

### 自检（可选，确认无紫边隐患）
```bash
/e/desktop-pet/.venv/Scripts/python.exe - <<'PY'
from PIL import Image; import numpy as np
for f in ["cat.png","effect_眨眼.png"]:
    a = np.array(Image.open("assets/"+f).convert("RGBA"))[:,:,3]
    print(f, "半透明像素数=", int(((a>0)&(a<255)).sum()))  # 必须为 0
PY
```

---

## 五、注册这些特效（改 desktop_pet.py）

打开新工程里的 `desktop_pet.py`，把 `effect_files` 字典改成收集到的动作（key 与第四步文件名一致）：

```python
self.effect_files = {
    "眨眼":   _find_asset("effect_眨眼.png",   ["effect_眨眼.gif"]),
    "伸懒腰": _find_asset("effect_伸懒腰.png", ["effect_伸懒腰.gif"]),
    "招手":   _find_asset("effect_招手.png",   ["effect_招手.gif"]),
}
```

`_start_random_effect` 会从 `effect_cache` 的 key 里随机选，**加 key 即自动进入随机池，无需改其它代码**。
空闲图已通过 `CAT_OPEN_PATH = _find_asset("cat.png")` 自动生效（无需额外注册）。

> 触发节奏：特效结束 → 冷却 `SPECIAL_COOLDOWN_MS=5000`（5 秒）→ 再随机播下一个，正好满足「5 秒后随机展示」。

---

## 六、打包

```bash
cd E:/desktop-pet-柴犬
tasklist | grep -i DesktopPet && taskkill /F /IM DesktopPet.exe
/e/desktop-pet-柴犬/.venv/Scripts/python.exe -m py_compile desktop_pet.py
/e/desktop-pet-柴犬/.venv/Scripts/python.exe build_pet.py   # → dist/DesktopPet.exe

# 复制为正式名（绕开 safe-delete，用 Python）
/e/desktop-pet-柴犬/.venv/Scripts/python.exe - <<'PY'
import os, shutil
src="dist/DesktopPet.exe"; dst="dist/布丁店长.exe"
if os.path.exists(dst): os.remove(dst)
shutil.copy2(src, dst)
print("size=%.1fMB" % (os.path.getsize(dst)/1024/1024))
PY
```

双击 `dist/布丁店长.exe`（或 `dist/DesktopPet.exe`）验证。若形象大小与位置不对：
- 用本技能的 `PET_H`（动作高度）、`PET_W`（空闲/旧眨眼宽度）调首帧对齐；
- 或生成「修改前后」预览 PNG 对比（用 Pillow 把素材按比例贴到同一画布），确认无误再打包。

---

## 七、常见坑（换形象必读）

1. **【最关键】素材里绝不能留半透明像素 —— 否则出现「紫边 / 紫块」**
   - **原理**：宠物窗口靠 `-transparentcolor` 色键（品红 `#ff00fe`）实现异形透明，而色键是**二值**的：
     像素要么**精确等于** `#ff00fe` 被完全抠掉，要么**完全不透明**，没有中间态。
   - **后果**：任何半透明像素（alpha 介于 1~254）都会与品红底混合成**亮紫色**且无法抠掉 → 角色周围一圈**紫边**；
     成片半透明 → 一整块**亮紫块**。
   - **做法**：抠完背景后必须做 **alpha 二值化**，且**必须放在 resize 之后**。`gen_swap.py` 已内置
     （`np.where(bg,0,255)`）。**用户在 GIMP/PS 手修图时若留羽化边缘，仍会出紫边**，需回脚本重抠或导出硬边。
2. **背景颜色决定抠图方法**：白底/浅色底用 `--method light`（默认）；**深色 / 纯色背景必须加 `--method flood`**，
   否则 light 法识别不出背景、整张保留不透明。
3. **「日志说宠物活着但看不见」= 被别的进程/窗口盖住了**，不要往「为什么隐藏」方向查（当前单窗口架构已无此问题）。
4. **空闲图与动作首帧务必同源**：不同源会导致待机↔动作切换时忽胖忽瘦、位置跳变
   （代码按「不透明内容底边中心」锚点对齐，同源才稳）。
5. **打包前先杀干净旧进程**：否则覆盖 exe 会报
   `PermissionError: [WinError 32] 另一个程序正在使用此文件`。**每次只保留唯一正式 exe**，
   旧构建移入 `dist/_old_builds/`——留一堆测试 exe 会让用户反复跑错版本、白测好几轮。
6. **每个形象独立工程目录**：别直接覆盖 `E:\desktop-pet\assets\`，否则丢失布丁原素材（第三节）。

---

## 八、交付清单

完成后告诉用户：
1. exe 路径（如 `E:\desktop-pet-柴犬\dist\布丁店长.exe`）
2. 用了哪 1 张空闲图 + 哪 N 个动作
3. 如想换回布丁：`E:\desktop-pet\dist\布丁店长.exe` 仍在
4. 后续加动作/功能 → 用 **布丁店长** 技能（同一套代码架构）
