用PyQt5做一个桌面桌宠!(形象参考re0的爱蜜莉雅)AI对话 + 语音播报,代码已开源
用PyQt5做一个桌面桌宠!(形象参考re0的爱蜜莉雅)AI对话 + 语音播报,代码已开源
前言
最近考试周结束,突发奇想——能不能在桌面上放一个可爱的小人,戳她会说话,还能AI聊天?于是花了一晚上,用 PyQt5 从零撸了一个桌面精灵出来。这篇文章记录完整实现过程,代码已开源~
先看效果:
📸 桌宠站在桌面上,右下角呼吸浮动

✨ 功能一览
| 功能 | 说明 |
|---|---|
| 🪟 透明置顶窗口 | 无边框、无任务栏、透背景,像真的趴在桌面上 |
| 🎨 像素风角色 | AI生成的Q版艾米莉亚(Re:Zero),多张精灵图情绪切换 |
| 🖱️ 拖拽移动 | 左键拖到哪放哪,关掉重开记住位置 |
| 👆 单击卖萌 | 随机弹出一句卖萌泡泡,不打扰 |
| 💬 双击聊天 | 精美定制对话框输入,接 Claude AI 返回回复 |
| 🔊 TTS语音 | AI回复自动配音播放(需搭配TTS后台) |
| 📜 滚动气泡 | 固定位置气泡框,长文本滚轮翻阅 |
📸 聊天框打开状态

🛠 技术栈
- Python 3.9 + PyQt5(GUI框架)
- Pillow + numpy(图像处理)
- Claude CLI(AI对话,subprocess异步调用)
- GPT-SoVITS(TTS语音,可选)
📦 项目结构
cc-desktop-pet/
├── main.py # 主窗口 + 聊天对话框(300x150)
├── character.py # 精灵渲染 + 滚动气泡 + 动画状态机
├── ai_client.py # Claude CLI 异步调用封装
├── tts_client.py # TTS请求客户端(文件协议)
├── config.py # 窗口配置 + 20条卖萌短句
└── sprites/emilia/ # 5张像素风精灵图 + 快捷图标
🔧 核心实现
1. 透明无边框窗口
这是整个桌宠的"灵魂"——窗口必须完全透明、没有标题栏、始终在最前且不在任务栏显示。
self.setWindowFlags(
Qt.FramelessWindowHint | # 无边框
Qt.WindowStaysOnTopHint | # 始终置顶
Qt.Tool # 不在任务栏显示
)
self.setAttribute(Qt.WA_TranslucentBackground) # 透明背景
2. PNG精灵图渲染
用 QPainter 在 paintEvent 中直接画透明PNG,角色就浮在桌面上了。
def paintEvent(self, event):
p = QPainter(self)
p.setRenderHint(QPainter.SmoothPixmapTransform)
sprite = self._current_sprite # 根据情绪状态选的精灵图
p.drawPixmap(x, y, sprite) # 画在透明窗口上
p.end()
3. 动画状态机
QTimer 驱动呼吸浮动 + 情绪切换。待机时绝不乱换图,只有交互才切表情:
| 触发 | 状态 | 精灵图 | 说明 |
|---|---|---|---|
| 无操作 | idle | idle.png | 永久呼吸浮动 |
| 单击 | thinking | think.png | 歪头思考 + 弹幕气泡,2.5秒回idle |
| 双击等待AI | thinking | think.png | 等待Claude回复 |
| AI回复中 | talking | idle.png | 安静陪伴,不切图抢戏 |
| (保留) | happy/talk/blink | 未激活 | 可按需开启,见自定义章节 |
# 呼吸:50ms一次,正弦波Y轴偏移±4px
def _on_breath(self):
self._breath_phase += 0.06
self._offset_y = int(math.sin(self._breath_phase) * 4)
self.update()
4. 固定滚动气泡
回复显示在角色下方固定气泡框内,长文本自动折行,鼠标滚轮翻动。
def wheelEvent(self, event):
delta = event.angleDelta().y()
self._bubble_scroll -= delta
self._bubble_scroll = max(0, min(self._bubble_scroll, max_scroll))
self.update()
# paintEvent 中 setClipRect 裁剪可见区域
p.setClipRect(QRectF(bubble_rect))
p.drawText(x, y - self._bubble_scroll, line) # 减去滚动偏移
5. Claude CLI 对话
subprocess 调 claude -p,消息通过 stdin 传入(避免 Windows GBK 编码问题),异步线程不阻塞UI。
proc = subprocess.run(
["claude", "-p"],
input=message.encode("utf-8"), # stdin 传中文
capture_output=True,
creationflags=subprocess.CREATE_NO_WINDOW, # 不弹黑框
)
reply = proc.stdout.decode("utf-8").strip()
6. TTS语音
写请求文件 → GPT-SoVITS常驻进程自动生成WAV → winsound播放。客户端只管写文件,不重复播放。
def speak(text):
with open(TTS_REQUEST, 'w', encoding='utf-8') as f:
f.write(text.strip())
# daemon检测到文件更新 → 生成WAV → 播放,客户端无需等待
📸 完整效果:角色 + 气泡 + 桌面环境

🐛 爬坑记录
踩了一晚上的坑,记录一下免得大家重蹈覆辙:
| 坑 | 现象 | 原因 | 解决 |
|---|---|---|---|
| QPointF导入 | 气泡渲染就卡死 | QPointF在QtCore不在QtGui,paintEvent抛异常 |
从QtCore导入 |
| lambda闭包 | 定时器回调异常 | Qt中lambda捕获变量不可靠 | 改用functools.partial |
| GBK编码 | 中文乱码崩溃 | subprocess默认系统编码 | decode("utf-8") + stdin传消息 |
| 终端弹窗 | 聊天时弹出cmd | 子进程无CREATE_NO_WINDOW |
加creationflags |
| 双重语音 | 每句话播两遍 | daemon和client端都播了wav | 去掉client端播放 |
| 图标缓存 | 改ico后不生效 | Windows图标缓存极顽固 | 改文件名+重启explorer |
| 桌面路径 | 文件放错找不到 | 中文Windows桌面是桌面不是Desktop |
两边都写 |
🚀 快速开始
# 1. 安装依赖
pip install PyQt5 Pillow numpy
# 2. 克隆项目
git clone https://github.com/Nicc29jl/cc-desktop-pet.git
cd cc-desktop-pet
# 3. 启动
python main.py
可选依赖:
- 安装 Claude Code CLI → 获得AI对话能力
- 搭配 GPT-SoVITS + cc_voice_daemon → 获得TTS语音
🎨 自定义角色
替换 sprites/emilia/ 下的PNG即可(保持文件名不变):
| 文件 | 用途 | 是否启用 |
|---|---|---|
idle.png |
待机 + AI回复 | ✅ 核心 |
think.png |
思考/等待 | ✅ 核心 |
talk.png |
说话张嘴 | 保留(talking状态用idle更安静) |
happy.png |
开心表情 | 保留(可改为单击触发) |
blink.png |
眨眼闭眼 | 保留(可开启定时眨眼) |
💡 目前默认只用 idle + think 两张,简约不花哨。另外三张代码里都有加载,想开启去
character.py改一行即可~
要求:透明背景PNG,建议256px左右高度,Q版大头比例。
🔗 GitHub 地址
👉 https://github.com/Nicc29jl/cc-desktop-pet
欢迎 Star ⭐ 和 PR ~
📝 结语
一个晚上从零撸出来的小项目。从透明窗口到精灵渲染,从AI对话到语音播报,中间踩了不少坑,但看到小人趴在桌面上的那一刻——值了 😄
如果你也想在桌面上养一只会聊天的小精灵,clone 代码跑起来。
标签:
PyQt5桌面精灵Python桌宠Claude AIGPT-SoVITS开源项目桌面美化
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)