ARTICLE · 1078936
告别切换!Python 操控AI大模型,打造桌面级“悬浮 AI”助手(软件运行效果看视频号)
告别切换!Python 操控AI大模型,打造桌面级“悬浮 AI”助手(软件运行效果看视频号)⚡
深度工作流重构:频繁在 Word/IDE 与浏览器 AI 网页之间复制粘贴,正在严重打断你的专注力。本文带你用 Python + PySide6 打通 Win32 消息管道,实现系统级选中文本一键捕获、SSE 流式渲染与原位零延迟文本覆写。
💡 为什么你需要桌面级“悬浮 AI”?
在日常开发、文档撰写与长文阅读中,传统的 AI 交互需要经历 8 步繁琐切屏。而基于 Win32 底层管道打造的桌面助手,能将其压缩至极致:
| 操作链路 | 2 步(选中文本 ➔ 原位唤醒替换) | |
| 专注度 | 原位沉浸,无缝衔接当前工作 | |
| 渲染体验 | 极简暗黑卡片 + SSE 毫秒级打字机 | |
| 通用性 | Win32 全局支持(Word / IDE / 浏览器通用) |
🛠️ 系统全景架构设计
项目采用 分层解耦架构,确保云端 LLM 的流式 HTTP 请求不会阻塞主界面,实现毫秒级响应。
1️⃣ Win32 捕获层 ➔ 发送
Ctrl+C指令,零延迟读取系统剪贴板文本管道2️⃣ PySide6 交互层 ➔ 唤醒无边框悬浮窗,展示深色极简 UI 界面3️⃣ QThread 调度层 ➔ 后台异步请求大模型 API,基于 Qt Signal 进行 Token 级推流4️⃣ Win32 写回层 ➔ 自动交接焦点,模拟Ctrl+V将 AI 结果原位覆写
🔑 核心技术拆解:“原地替换”时序控制
“原地替换”的最核心难点在于:当用户点击悬浮窗上的按钮时,Windows 的焦点在悬浮窗本身。如果直接粘贴,内容会错发到悬浮窗内部。
必须通过精确的硬件级时序控制来恢复焦点:
Step 1: pyperclip.copy(ai_text) ➔ 将 AI 结果写入系统剪贴板Step 2: self.hide() ➔ 隐藏悬浮窗,释放系统焦点Step 3: time.sleep(0.08) ➔ 线程让步,等待 Windows 交接 HWND 句柄Step 4: send_key_combo_paste() ➔ 模拟全局 Ctrl+V,覆盖高亮目标文本💻 生产级 Python 完整源代码
下面是包含底层 Win32 按键管道、QThread 异步 SSE 线程与 UI 交互的完整源码:
import sysimport timeimport ctypesimport pyperclipfrom PySide6.QtCore import Qt, QThread, Signal, QPointfrom PySide6.QtWidgets import ( QApplication, QWidget, QVBoxLayout, QHBoxLayout, QTextEdit, QPushButton, QCheckBox, QLabel)from zhipuai import ZhipuAI# -------------------------------------------------------------------# 1. Win32 API 底层模拟与虚拟键码定义# -------------------------------------------------------------------VK_CONTROL = 0x11VK_KEY_C = 0x43VK_KEY_V = 0x56KEYEVENTF_KEYUP = 0x0002defsend_key_combo_copy():"""模拟底层硬件按键: Ctrl + C""" ctypes.windll.user32.keybd_event(VK_CONTROL, 0, 0, 0) ctypes.windll.user32.keybd_event(VK_KEY_C, 0, 0, 0) time.sleep(0.05) ctypes.windll.user32.keybd_event(VK_KEY_C, 0, KEYEVENTF_KEYUP, 0) ctypes.windll.user32.keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0)defsend_key_combo_paste():"""模拟底层硬件按键: Ctrl + V""" ctypes.windll.user32.keybd_event(VK_CONTROL, 0, 0, 0) ctypes.windll.user32.keybd_event(VK_KEY_V, 0, 0, 0) time.sleep(0.05) ctypes.windll.user32.keybd_event(VK_KEY_V, 0, KEYEVENTF_KEYUP, 0) ctypes.windll.user32.keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0)# -------------------------------------------------------------------# 2. LLM 异步流式 Worker 线程# -------------------------------------------------------------------classStreamLLMThread(QThread): token_stream = Signal(str) stream_finished = Signal()def__init__(self, api_key, system_prompt, user_content): super().__init__() self.api_key = api_key self.system_prompt = system_prompt self.user_content = user_contentdefrun(self):try: client = ZhipuAI(api_key=self.api_key) response = client.chat.completions.create( model="glm-4-flash", # 可无缝替换为 deepseek-chat messages=[ {"role": "system", "content": self.system_prompt}, {"role": "user", "content": self.user_content} ], stream=True )for chunk in response:if chunk.choices[0].delta.content: self.token_stream.emit(chunk.choices[0].delta.content) self.stream_finished.emit()except Exception as e: self.token_stream.emit(f"\n[错误]: {str(e)}") self.stream_finished.emit()# -------------------------------------------------------------------# 3. 桌面 AI 悬浮窗主界面# -------------------------------------------------------------------classAIFloatingWindow(QWidget):def__init__(self): super().__init__() self.api_key = "YOUR_API_KEY_HERE"# 替换为你的 API Key self.drag_position = QPoint() self.last_ai_response = "" self.init_ui()definit_ui(self): self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool) self.setAttribute(Qt.WA_TranslucentBackground) self.resize(560, 390)# 现代科技暗黑 QSS 注入 self.setStyleSheet(""" QWidget#MainContainer { background-color: #1E1E24; border: 1px solid #3A3A45; border-radius: 12px; } QLabel { color: #A0A0B0; font-size: 12px; } QTextEdit { background-color: #141419; color: #E5E7EB; border: 1px solid #2A2A35; border-radius: 8px; padding: 10px; font-size: 13px; } QPushButton { background-color: #2D2D38; color: #E0E0E0; border: None; border-radius: 6px; padding: 6px 12px; font-weight: bold; } QPushButton:hover { background-color: #3E3E4D; } QPushButton#PrimaryBtn { background-color: #2563EB; color: #FFFFFF; } QPushButton#PrimaryBtn:hover { background-color: #1D4ED8; } QPushButton#ActionBtn { background-color: #6366F1; color: #FFFFFF; } QPushButton#ActionBtn:hover { background-color: #4F46E5; } """) main_layout = QVBoxLayout(self) main_layout.setContentsMargins(0, 0, 0, 0) container = QWidget() container.setObjectName("MainContainer") container_layout = QVBoxLayout(container) container_layout.setContentsMargins(14, 14, 14, 14)# 顶部快捷指令栏 top_bar = QHBoxLayout() self.btn_polish = QPushButton("✨ 原地润色") self.btn_summary = QPushButton("📝 3点总结") self.btn_translate = QPushButton("🌐 中英互译") self.btn_fetch = QPushButton("🔄 载入选中") self.btn_polish.setObjectName("ActionBtn") self.btn_summary.setObjectName("ActionBtn") self.btn_translate.setObjectName("ActionBtn") top_bar.addWidget(self.btn_polish) top_bar.addWidget(self.btn_summary) top_bar.addWidget(self.btn_translate) top_bar.addWidget(self.btn_fetch)# 中央交互编辑框 self.text_editor = QTextEdit() self.text_editor.setPlaceholderText("在外部选中文字后点击【载入选中】...") self.status_label = QLabel("状态: 准备就绪")# 底部控制栏 bottom_bar = QHBoxLayout() self.chk_autostart = QCheckBox("开机自启") self.btn_clear = QPushButton("🧹 清空") self.btn_submit = QPushButton("⚡ AI 快答") self.btn_replace = QPushButton("📋 原地替换") self.btn_submit.setObjectName("PrimaryBtn") self.btn_replace.setObjectName("PrimaryBtn") bottom_bar.addWidget(self.chk_autostart) bottom_bar.addWidget(self.btn_clear) bottom_bar.addStretch() bottom_bar.addWidget(self.btn_submit) bottom_bar.addWidget(self.btn_replace) container_layout.addLayout(top_bar) container_layout.addWidget(self.text_editor) container_layout.addWidget(self.status_label) container_layout.addLayout(bottom_bar) main_layout.addWidget(container)# 绑定信号 self.btn_fetch.clicked.connect(self.capture_selected_text) self.btn_clear.clicked.connect(self.clear_text) self.btn_submit.clicked.connect(lambda: self.execute_prompt("请专业且简练地回答:")) self.btn_polish.clicked.connect(lambda: self.execute_prompt("请对以下文本进行润色与修改:")) self.btn_summary.clicked.connect(lambda: self.execute_prompt("请用3个核心要点归纳总结:")) self.btn_translate.clicked.connect(lambda: self.execute_prompt("中英互译以下内容:")) self.btn_replace.clicked.connect(self.replace_selected_text)defmousePressEvent(self, event):if event.button() == Qt.LeftButton: self.drag_position = event.globalPosition().toPoint() - self.frameGeometry().topLeft()defmouseMoveEvent(self, event):if event.buttons() == Qt.LeftButton: self.move(event.globalPosition().toPoint() - self.drag_position)defcapture_selected_text(self): self.status_label.setText("状态: 正在捕获选中内容...") self.hide() time.sleep(0.05) send_key_combo_copy() time.sleep(0.05) captured_text = pyperclip.paste().strip() self.show() self.activateWindow()if captured_text: self.text_editor.setText(f"【待处理文本】:\n{captured_text}\n\n") self.status_label.setText("状态: 文本载入成功")defexecute_prompt(self, system_prompt): user_text = self.text_editor.toPlainText().strip()ifnot user_text: return self.last_ai_response = "" self.text_editor.append(f"\n【AI 回复】:\n") self.status_label.setText("状态: AI 思考中...") self.worker = StreamLLMThread(self.api_key, system_prompt, user_text) self.worker.token_stream.connect(self.on_token_received) self.worker.stream_finished.connect(lambda: self.status_label.setText("状态: 生成完毕")) self.worker.start()defon_token_received(self, token): self.last_ai_response += token cursor = self.text_editor.textCursor() cursor.movePosition(cursor.MoveOperation.End) cursor.insertText(token) self.text_editor.setTextCursor(cursor)defreplace_selected_text(self):ifnot self.last_ai_response: return pyperclip.copy(self.last_ai_response) self.status_label.setText("状态: 正在原地替换...") self.hide() time.sleep(0.08) send_key_combo_paste()defclear_text(self): self.text_editor.clear() self.last_ai_response = "" self.status_label.setText("状态: 已清空")if __name__ == "__main__": app = QApplication(sys.argv) window = AIFloatingWindow() window.show() sys.exit(app.exec())📦 打包与独立运行
使用 PyInstaller 即可将其打包为单文件绿色可执行程序(.exe):
pyinstaller --noconsole --onefile --clean --name="AIAssistant" main.py🎯 总结:通过打通系统底层消息管道与 UI 渲染,我们实现了无缝的桌面级 AI 沉浸体验。可在此基础上进一步绑定全局热键
Alt+Q,开启真正的极致生产力!运行效果可看本视频号!