用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导入 气泡渲染就卡死 QPointFQtCore不在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 AI GPT-SoVITS 开源项目 桌面美化

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐