按住热键说话,松开即得润色后的文字,自动注入到当前输入框。
| 功能 | 状态 | 说明 |
|---|---|---|
| 全局热键 Push-to-talk | ✅ | 在任意应用中按住热键录音,松开结束 |
| 实时音量波形 | ✅ | 悬浮窗显示随麦克风输入跳动的波形条 |
| Whisper 语音转文字 | ✅ | 调用 OpenAI Whisper API,支持中英日等多语言 |
| GPT 文字润色 | ✅ | 自动去除语气词、修复语法、提升流畅度 |
| 全局文字注入 | ✅ | Win32 SendInput Unicode 方案,支持中文/Emoji |
| 悬浮状态窗口 | ✅ | 5 种状态(待机/录音/处理中/完成/错误)平滑动画 |
| 系统托盘常驻 | ✅ | 不占任务栏,双击托盘图标打开设置 |
| 设置界面 | ✅ | API Key、热键、模型、注入方式均可配置 |
- Windows 10 / 11(文字注入功能仅限 Windows)
- Python 3.10 或更高版本
- 麦克风设备
- OpenAI API Key(获取地址)
:: 1. 克隆或下载项目
cd typeless
:: 2. 创建虚拟环境
python -m venv .venv
.venv\Scripts\activate
:: 3. 安装依赖
pip install -r requirements.txt
:: 4. 配置 API Key
copy .env.example .env
notepad .env在 .env 文件中填入你的 OpenAI API Key:
OPENAI_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxx:: 5. 启动
python main.py或者直接双击 run.bat,它会自动完成环境创建和启动。
所有配置通过项目根目录的 .env 文件管理,修改后重启生效(部分配置支持通过设置界面热重载)。
# ── 必填 ──────────────────────────────────────────────────────
OPENAI_API_KEY=sk-your-key-here
# ── 热键 ──────────────────────────────────────────────────────
# 使用 keyboard 库的按键名称
# 推荐:right alt(不与大多数应用冲突)
# 其他示例:f9 | ctrl+shift+space | caps lock
TYPELESS_HOTKEY=right alt
# ── Whisper 语音识别 ──────────────────────────────────────────
WHISPER_MODEL=whisper-1
# 语言提示(留空自动检测,填写可提升速度和准确率)
# 可选值:zh | en | ja | ko | fr | de | es ...
WHISPER_LANGUAGE=
# ── GPT 文字润色 ──────────────────────────────────────────────
POLISH_ENABLED=true
# 推荐模型:gpt-4o-mini(便宜快速)| gpt-4o(质量最高)
POLISH_MODEL=gpt-4o-mini
# ── 文字注入方式 ──────────────────────────────────────────────
# sendinput:Win32 SendInput,逐字符注入,不影响剪贴板(推荐)
# clipboard:复制到剪贴板再模拟 Ctrl+V,速度更快但会覆盖剪贴板
INJECTION_METHOD=sendinput
# ── 调试 ──────────────────────────────────────────────────────
LOG_LEVEL=INFO.env 文件 → 环境变量 → config.py 中的默认值
① 启动程序 → 系统托盘出现麦克风图标
② 切换到任意应用(Word、浏览器、微信、IDE...)
③ 按住 Right Alt(或你配置的热键)
④ 说话 — 悬浮窗显示红色波形,表示正在录音
⑤ 松开按键
⑥ 悬浮窗变为蓝色旋转动画,表示正在转录/润色
⑦ 润色后的文字自动输入到当前光标位置
⑧ 悬浮窗短暂显示绿色确认,2 秒后自动消失
🔴 红色波形 + "Listening…" → 正在录音,波形高度反映麦克风音量
🔵 蓝色旋转 + "Processing…" → 调用 Whisper + GPT,通常 1-3 秒
🟢 绿色对勾 + 文字预览 → 注入完成,2 秒后自动消失
🔴 红色叉号 + 错误信息 → 出错(API 失败、无麦克风等),3 秒后消失
| 操作 | 效果 |
|---|---|
| 左键单击 | 无操作(避免误触) |
| 左键双击 | 打开设置界面 |
| 右键 | 显示菜单(设置 / 退出) |
录音时托盘图标变为红色,提供视觉反馈。
┌─────────────────────────────────────────────────────────────┐
│ 用户操作 │
│ 按住 Right Alt │
└────────────────────────┬────────────────────────────────────┘
│ keyboard hook (后台线程)
▼
HotkeyManager
press / release
(Qt Signal 跨线程安全)
│
┌──────────────┴──────────────┐
▼ ▼
recorder.start() overlay.show_recording()
│
sounddevice.InputStream
(QThread, 16kHz mono)
│ level_updated (每 40ms)
├──────────────────────────► overlay 波形动画
│
[松开热键]
│
recorder.stop()
│ finished(wav_path)
▼
overlay.show_processing()
Transcriber (QThread)
POST /audio/transcriptions
│ finished(raw_text)
▼
TextPolisher (QThread)
POST /chat/completions
│ finished(polished_text)
▼
TextInjector
Win32 SendInput ──────────────► 当前焦点输入框
│ done
▼
overlay.show_done(preview)
主线程 (Qt Event Loop)
├── UI 更新(Overlay 动画、托盘图标)
├── 信号分发
└── 设置对话框
keyboard 内部线程
└── WH_KEYBOARD_LL hook → emit Qt Signal → 主线程处理
QThread: _RecordWorker
└── sounddevice 采集循环 → emit level_updated, finished
QThread: _TranscribeWorker
└── HTTP POST to Whisper API → emit finished
QThread: _PolishWorker
└── HTTP POST to Chat API → emit finished
主线程(注入)
└── SendInput(同步,速度足够快无需线程)
所有跨线程通信均通过 Qt Signal/Slot 完成,线程安全,无需手动加锁。
from config import cfg
cfg.openai_api_key # str
cfg.hotkey # str,如 "right alt"
cfg.polish_enabled # bool
cfg.is_valid() # -> bool,检查最低配置是否满足
cfg.save_to_env() # 将当前配置写回 .env 文件class HotkeyManager(QObject):
press = pyqtSignal() # 按下时触发
release = pyqtSignal() # 松开时触发
def start(self) -> None # 安装钩子(非阻塞)
def stop(self) -> None # 卸载钩子
def set_hotkey(key: str) # 运行时切换热键,无需重启实现原理:使用 keyboard.on_press / keyboard.on_release 安装 WH_KEYBOARD_LL 低级键盘钩子。该 Hook 在 Windows 用户模式下无需管理员权限。通过内部 _recording 标志防止按键重复触发。
class AudioRecorder(QObject):
level_updated = pyqtSignal(float) # RMS 电平 [0.0, 1.0],每 40ms 一次
finished = pyqtSignal(str) # WAV 临时文件路径
error = pyqtSignal(str)
def start_recording(self) -> None
def stop_recording(self) -> None
@property
def is_recording(self) -> bool参数:16kHz,单声道,float32(Whisper 最佳输入格式)。录音结束后写入系统临时目录的 .wav 文件,转录完成后自动删除。
class Transcriber(QObject):
finished = pyqtSignal(str) # 原始转录文本
error = pyqtSignal(str)
def transcribe(self, wav_path: str) -> None # 异步,结果通过信号返回调用 OpenAI.audio.transcriptions.create,使用 response_format="text" 直接获取字符串。支持通过 WHISPER_LANGUAGE 提示语言以提升准确率和速度。
class TextPolisher(QObject):
finished = pyqtSignal(str) # 润色后文本(失败时 fallback 到原文)
error = pyqtSignal(str) # 非致命,finished 仍会触发
def polish(self, raw_text: str) -> None默认 Prompt(可在 .env 中覆盖):
你是专业的文字助手。将语音转录文本改写为流畅自然的书面语,修正语法,去除语气词(嗯、啊、那个),保持原意和语气。只输出润色后的文本,不加任何说明。
若 POLISH_ENABLED=false,原文直接透传,跳过 API 调用。API 失败时自动 fallback 到原始转录文本,用户总能得到结果。
class TextInjector(QObject):
done = pyqtSignal()
error = pyqtSignal(str)
def inject(self, text: str) -> None对每个 Unicode 字符:
SendInput([KEY_DOWN(KEYEVENTF_UNICODE, codepoint),
KEY_UP (KEYEVENTF_UNICODE, codepoint)])
- 支持全部 Unicode:中文、日文、Emoji、特殊符号
- 不影响剪贴板
- 注入速度:约 5000 字符/秒
保存当前剪贴板 → 写入新文本 → 模拟 Ctrl+V → 恢复剪贴板
- 适合部分不响应 SendInput 的应用(如某些游戏输入框)
- 速度与文本长度无关,始终毫秒级
class OverlayWindow(QWidget):
clicked = pyqtSignal()
def show_recording(self) -> None
def show_processing(self) -> None
def show_done(self, preview: str = "") -> None
def show_error(self, message: str) -> None
def update_level(self, level: float) -> None # 喂入音量电平窗口属性:
- 尺寸:320×64 像素,圆角药丸形
- 位置:主屏幕底部居中,距底边 48px
- 层级:
WindowStaysOnTopHint+WA_ShowWithoutActivating(不抢焦点) - 透明度:0.93,深色玻璃背景
rgba(18,18,24,235) - 淡入/淡出动画:150ms EaseInOut
SystemTrayApp 是应用的"大脑",负责:
- 创建所有核心对象
- 连接完整信号链
- 管理托盘图标和菜单
- 热重载设置
app = SystemTrayApp(q_application)
app.initialize() # 一次性启动全部功能三个 Tab 页:
- General:API Key、热键
- AI / Models:Whisper 模型、语言、GPT 模型、润色开关
- Text Injection:注入方式选择及说明
点击 OK 后调用 cfg.save_to_env() 持久化,并通过 settings_saved 信号触发热重载。
| 需求 | 方案 | 原因 |
|---|---|---|
| Windows 系统级热键 | keyboard 库 |
支持 push-to-talk(按住/松开),无需管理员,跨会话 |
| Unicode 文字注入 | ctypes + Win32 SendInput |
官方 Windows API,支持全部 Unicode,无剪贴板副作用 |
| 无边框透明悬浮窗 | PyQt6 | WA_TranslucentBackground + 自定义 paintEvent,渲染质量高 |
| 异步不阻塞 UI | PyQt6 QThread | Qt 信号天然线程安全,避免手动锁 |
| 系统托盘 | QSystemTrayIcon |
与 Qt 事件循环无缝集成,避免 pystray 的线程冲突 |
| 音频采集 | sounddevice |
低延迟,支持实时回调获取电平数据 |
| API 调用 | openai SDK |
官方 SDK,维护好,Whisper + Chat 统一接口 |
- Electron:文字注入复杂,需要额外 native addon,包体积 200MB+
- C# WPF:开发速度慢,与 Python AI 生态割裂
- Tauri:Rust 学习成本高,Windows 系统 API 绑定不成熟
- tkinter:动画能力差,无法实现半透明效果
部分以管理员权限运行的应用(如任务管理器、某些游戏反作弊)会屏蔽低权限进程的键盘钩子。解决方法:以管理员身份运行 Typeless。
:: 右键 run.bat → 以管理员身份运行
:: 或在终端中:
runas /user:Administrator "python main.py"- 在
.env中明确指定语言:WHISPER_LANGUAGE=zh - 检查麦克风采样率是否为 16kHz(
sounddevice会自动重采样,但某些驱动例外) - 确保录音环境安静,距麦克风 20-40cm
切换为剪贴板模式:
INJECTION_METHOD=clipboard部分应用(如远程桌面、某些 UWP 应用)不响应 SendInput,剪贴板粘贴方式更兼容。
检查 .env 中的 OPENAI_API_KEY 是否正确,注意不要有多余空格。也可以通过系统托盘 → 设置 → General 重新填写。
确认 PyQt6 安装成功,且 Windows 通知区域未隐藏该图标(任务栏右键 → 任务栏设置 → 其他系统托盘图标)。
核心逻辑(录音、转录、润色)跨平台,但 文字注入仅限 Windows(使用 Win32 SendInput)。在非 Windows 系统上启动时,injector.py 会自动降级为打印到控制台,方便开发调试。macOS/Linux 的注入支持(xdotool / Accessibility API)列入路线图。
- Whisper 本地模型支持(
faster-whisper),离线可用 - GPT 流式输出 — 边生成边注入,减少等待感
- 自动标点符号(句号、问号根据语气自动添加)
- 录音时长限制与提示(防止意外超长录音)
- 注入超长文本(>500 字)异步化,避免主线程卡顿
- 设置界面:快捷键录制 UI(点击按钮录制新热键)
- 设置界面:自定义润色 Prompt 编辑框
- 设置界面:按应用配置注入方式的规则列表
- 历史记录面板(最近 N 条转录记录,支持复制)
- 悬浮窗位置可拖动并记忆
- 多语言 UI(中文/英文界面切换)
- 自定义词汇表(提升专有名词识别率)
- 快捷短语模板(说"发邮件模板"自动填充)
- 声纹识别多用户配置
- 开机自启动配置
- 自动更新机制
typeless/
├── main.py # 入口,QApplication 初始化
├── config.py # 配置单例(读取 .env)
├── requirements.txt # Python 依赖
├── run.bat # Windows 一键启动脚本
├── .env.example # 配置模板(复制为 .env 后填写)
│
├── core/ # 业务逻辑(无 UI 依赖)
│ ├── hotkey.py # 全局热键监听
│ ├── recorder.py # 麦克风音频采集
│ ├── transcriber.py # Whisper API 调用
│ ├── polisher.py # GPT 润色
│ └── injector.py # Win32 文字注入
│
├── ui/ # 界面层(PyQt6)
│ ├── overlay.py # 悬浮状态药丸窗口
│ ├── settings_dialog.py # 设置对话框
│ └── tray.py # 系统托盘 + 信号连线(主控制器)
│
├── utils/
│ └── logger.py # 日志配置
│
└── logs/ # 运行日志(自动创建)
└── typeless.log
MIT License — 自由使用、修改和分发。