逐字符模拟真实键盘输入的 Windows 小工具 —— 目标程序收到的是按键,不是剪贴板粘贴。 用于解决学习通、头歌(EduCoder)等平台禁止粘贴的问题:平台屏蔽的是剪贴板, 挡不住真实按键。
A Windows tool that types text by simulating real keystrokes, character by character — the target application receives key events, not clipboard paste. Works around online platforms that block pasting (Chaoxing Xuexitong, EduCoder, exam systems, etc.).
把文字「一个字一个字敲出来」的小工具。通过 Windows 的 SendInput 接口向系统投递
真实的键盘事件,而不是把内容塞进剪贴板再粘贴。
典型用途:解决学习通 / 头歌等平台不可粘贴的问题
学习通、头歌(EduCoder)等在线教育平台的答题框和代码编辑器,以及不少考试系统、 网课作业页,都会屏蔽粘贴 —— Ctrl+V 无效、右键菜单被禁用,有些还会检测粘贴行为。 这些限制针对的是剪贴板,而本工具根本不碰剪贴板:它模拟真实键盘逐字符敲入, 平台收到的是货真价实的按键事件,这类限制对它天然免疫。
用法:把内容粘进本工具(这里是允许粘贴的)→ 设好速度 → 点「开始输入」→ 倒计时期间切到答题页面的输入框即可。
其他同类场景:
- 只允许键盘输入、禁用右键的在线代码编辑器
- 需要「看起来像人在打字」的演示、录屏
- 任何禁止粘贴的网页表单、考试系统
到本仓库的 Releases 页面下载 KeySim-v1.1.exe:
- 单文件,双击即用,无需安装 Python 或任何依赖
- 仅支持 Windows(依赖 Win32 API)
为什么 exe 不放在仓库里?每次发版都会新增一份 10MB 二进制,会让 git 历史迅速膨胀。 放在 Releases 里既能下载,又能让仓库保持轻量。
| 项目 | 说明 |
|---|---|
| 输入方式 | 逐字符模拟按键(非剪贴板)。Unicode 注入 / 虚拟键码两种模式 |
| 速度范围 | 40 ~ 1200 字符/分钟,滑块可调,实时显示基准间隔 |
| 速度模式 | 8 种,见下表 |
| 附加节奏 | 标点后加长停顿(倍数可调)、按键按住时长、波动幅度 |
| 括号处理 | 识别目标程序自动补全的右括号,改为右方向键越过 / 直接跳过 |
| 安全 | 输入过程中按 ESC 立即停止;起始倒计时留出切换窗口的时间 |
| 模式 | 曲线形态 |
|---|---|
| 固定匀速 | 所有字符间隔完全一致 |
| 随机抖动 | 间隔在基准值上下均匀随机浮动 |
| 人类打字模拟 | 高斯分布 + 偶发犹豫停顿(默认) |
| 慢-快-慢 | S 曲线,中段最快 |
| 渐快 | 单调加速 |
| 渐慢 | 单调减速 |
| 波浪起伏 | 正弦波周期性快慢交替 |
| 突发式 | 每 6 字符一组快打,组间停顿 |
界面上有速度曲线实时预览图,切换模式/拖动滑块即可看到节奏形态。
很多编辑器 / IDE 会自动补全右括号:敲一个 (,它立刻补一个 ) 并把光标夹在中间。
这时工具再照着原文敲 ),就会变成 (hi)) 这种重复右括号。
开启「括号处理」即可解决。工具会先扫描文本、把成对的括号配对好, 然后对「由目标程序自动生成的那个右括号」做特殊处理:
| 模式 | 行为 | 适用 |
|---|---|---|
| 右方向键(推荐) | 不发右括号,改按 → 把光标移过它 |
绝大多数编辑器 |
| 直接跳过 | 不发右括号,也不移光标 | 补全后光标自动停在括号右侧的编辑器 |
| 关闭 | 原样输出 | 目标程序不自动补全时 |
为什么不是简单地「不输出右括号」? 那样光标会停在自动补全的右括号左边,后面还有文字时会整体串位。 正确做法是发一个右方向键越过它。
范围可勾选:圆 () / 方 [] / 花 {} 默认开启;尖 <> 和引号默认关闭 ——
因为 5 < 10 和 don't 里的符号会被误判成括号。
若文本里有未闭合的左括号(如只写了 print(),目标程序补出的右括号会留在末尾,
可勾选「结束时清理」让工具在输入完成后自动按 Delete 删除。
界面下方会实时提示识别结果,例如「已识别 3 个配对右括号,输出时改为右方向键」。
需要一个带 tkinter 的 Python 3.9+(Windows 官方安装包默认包含)。
python -m venv .venv
.venv\Scripts\activate # Windows
pip install pyinstaller pillow
cd 源码
pyinstaller --noconfirm --onefile --noconsole --clean \
--name KeySim --icon app.ico keysim.py产物在 源码/dist/KeySim.exe。
若
import tkinter报错,说明当前 Python 未包含 tkinter。 部分包管理器(如某些 Linux 发行版、conda 精简版)需要单独安装python3-tk。
cd 测试
python selftest.py # 核心逻辑自检(结构体/键码/速度曲线/定时精度)
python test_gui.py # 界面冒烟测试(遍历所有控件回调)
python test_e2e.py # 端到端真实注入(读回目标控件内容比对)
python test_brackets.py # 括号配对逻辑 + 实际按键序列
python test_e2e_bracket.py # 模拟「自动补全右括号」的编辑器做真实注入
python test_perf.py # 长文本性能
python verify_exe.py # 启动打包好的 EXE、校验窗口并截图实测结果:
核心逻辑自检 全部通过
sizeof(INPUT) = 40(x64 正确)· ASCII 键码覆盖 100%
定时精度:5~200ms 各档平均偏差 < 0.35ms
节拍累计:90 字符 @300/分钟 → 预期 18.00s,实测 18.03s(+0.16%)
界面冒烟测试 通过(8 种模式、速度全量程、所有开关、3 种括号模式)
括号端到端测试 通过(模拟「自动补全且不支持越过」的编辑器)
关闭模式 '(hi)' → '(hi))' ← 复现问题
右方向键 '(hi)' → '(hi)' ← 修复成功
右方向键 '(hi) world' → '(hi) world' (后续内容不串位)
右方向键 'f(a[0], {b})' → 'f(a[0], {b})'(嵌套括号)
右方向键 'ok :)' → 'ok :)' (未配对右括号不受影响)
端到端注入测试 通过(Unicode / 虚拟键码两种模式,字符完全一致)
长文本性能测试 通过
括号分析 10 万字符 3.21ms / 界面回调 4.88ms
逐字输入最坏情况平均 0.51ms/次 —— 远低于 50ms 卡顿线
小窗口降级测试 通过(1000 / 880 / 700px 三档高度下按钮均可见)
EXE 验证 通过(窗口正常创建,标题 v1.1)
键盘注入 —— user32.SendInput + KEYBDINPUT 结构体。
Unicode 模式用 KEYEVENTF_UNICODE 直接投递字符码元(正确拆分 UTF-16 代理对,
支持 emoji),绕过输入法;虚拟键码模式构造真实键码 + Shift 组合。
节奏精度 —— winmm.timeBeginPeriod(1) 把系统定时器精度从默认 15.6ms 提到
1ms,配合基于 perf_counter() 的截止时间等待循环,实测累计误差 < 0.2%。
等待循环每 40ms 轮询一次 GetAsyncKeyState(VK_ESCAPE) 实现紧急停止。
输入法陷阱 —— 虚拟键码模式会经过 IME:目标窗口若处于中文输入状态,
字母会被拼成汉字(实测 "line" → "里呢")。因此提供
ImmGetDefaultIMEWnd + WM_IME_CONTROL/IMC_SETOPENSTATUS 在输入前临时关闭
目标窗口输入法、结束后恢复;Unicode 模式则天然免疫。
括号自动补全 —— 配对用栈扫描实现:( 入栈、) 出栈并标记跳过;
未配对的右括号(如笑脸 :))不入栈、正常输出。右方向键与 Delete
都必须带 KEYEVENTF_EXTENDEDKEY 标志,否则部分程序不响应。
界面 —— tkinter/ttk,clam 主题。复选框指示器为自绘 PNG(base64 内嵌,
运行时无需 Pillow),因为 clam 默认会把对勾渲染成 "✗",用户无法分辨开关状态。
设置区放在可滚动 Canvas 里,底部按钮区用 side="bottom" 优先占位,
保证窗口被压矮时「开始输入」始终可见。
- 使用 DirectInput / Raw Input 直接读硬件的游戏会忽略
SendInput,无法输入。 这类场景需要硬件级方案(如 Arduino 模拟 HID 键盘)。 - 目标程序以管理员权限运行时,本工具也需以管理员身份启动,否则会被 UIPI 拦截。
- PyInstaller 单文件打包可能被杀毒软件误报,源码就在
源码/keysim.py,可自行查阅。 - 纯本地运行,不联网,不读写任何文件。
keysim/
├── 键盘模拟输入器.exe 编译产物(本地用,不入库)
├── LICENSE MIT
├── README.md 本文件
├── 使用说明.txt 面向使用者的中文说明
├── 界面预览.png EXE 实际运行截图
├── 源码/
│ ├── keysim.py 完整程序(约 1200 行,单文件)
│ ├── app.ico 应用图标
│ ├── make_icon.py 生成 app.ico
│ ├── gen_assets.py 生成复选框指示器素材(base64)
│ └── KeySim.spec PyInstaller 配置
└── 测试/ 7 个测试脚本,见「测试」一节
A small Windows utility that types text one character at a time by injecting real
keyboard events through the Win32 SendInput API — the target application sees
keystrokes, not a clipboard paste.
Typical use: working around platforms that block pasting
Chinese online learning platforms such as Chaoxing Xuexitong (学习通) and EduCoder (头歌), along with many exam systems and homework portals, block pasting in their answer boxes and code editors — Ctrl+V does nothing, the context menu is disabled, and some even detect paste events. Those restrictions target the clipboard; this tool never touches it. It simulates real keystrokes character by character, so the platform receives genuine key events and the restriction simply does not apply.
Usage: paste your text into this tool (pasting is allowed here) → set the speed → click Start → switch to the answer box during the countdown.
Other scenarios:
- Online code editors that only accept typed input and disable right-click
- Demos and screen recordings where typing needs to look human
- Any web form or exam system that blocks paste
Grab KeySim-v1.1.exe from the Releases page of this repository.
- Single file, no installation, no Python required
- Windows only (Win32 API)
The binary lives in Releases rather than in the repo: committing a fresh 10 MB executable on every release would bloat the git history fast.
| Input method | Per-character keystroke injection (not clipboard). Unicode or virtual-keycode mode |
| Speed | 40 – 1200 characters/minute, live slider with real-time interval display |
| Speed curves | 8 profiles — see below |
| Extra rhythm | Extra pause after punctuation, key hold duration, jitter amount |
| Bracket handling | Detects brackets auto-inserted by the target editor and skips past them |
| Safety | Press ESC to abort instantly; a start countdown gives you time to focus the target window |
| Profile | Curve |
|---|---|
| Constant | Identical interval for every character |
| Jitter | Interval randomly varies around the base value |
| Human-like | Gaussian distribution + occasional hesitation (default) |
| Ease in-out | S-curve, fastest in the middle |
| Accelerate | Monotonically speeding up |
| Decelerate | Monotonically slowing down |
| Wave | Sinusoidal fast/slow alternation |
| Burst | Groups of 6 fast characters separated by pauses |
A live curve preview in the UI shows the rhythm as you switch profiles.
Most editors and IDEs auto-close brackets: type ( and they immediately insert ),
placing the cursor between them. The tool then types the ) from your text as well,
producing duplicates like (hi)).
Enabling bracket handling fixes this. The tool scans the text, matches up bracket pairs, and treats the auto-generated closing bracket specially:
| Mode | Behaviour |
|---|---|
| Arrow key (recommended) | Skip the closing bracket, press → to move the cursor past it |
| Skip | Just don't type the closing bracket |
| Off | Type everything verbatim |
Why not simply "don't output closing brackets"? The cursor would sit left of the auto-inserted bracket, so any following text would land in the wrong place. Moving past it with the right arrow key is correct.
Bracket groups are selectable: (), [], {} are on by default; angle brackets <>
and quotes are off by default because 5 < 10 and don't would be misdetected.
Unclosed openers (e.g. a lone print() leave a stray closing bracket behind —
enable cleanup to have the tool press Delete when it finishes.
Requires Python 3.9+ with tkinter (included in the official Windows installer).
python -m venv .venv
.venv\Scripts\activate # Windows
pip install pyinstaller pillow
cd 源码
pyinstaller --noconfirm --onefile --noconsole --clean \
--name KeySim --icon app.ico keysim.pyOutput: 源码/dist/KeySim.exe.
Injection — user32.SendInput with KEYBDINPUT structures. Unicode mode uses
KEYEVENTF_UNICODE to post UTF-16 code units directly (splitting surrogate pairs
correctly, so emoji work), bypassing the IME entirely. Virtual-keycode mode builds
real keycode + Shift combinations for better compatibility with games and RDP.
Timing accuracy — winmm.timeBeginPeriod(1) raises the system timer resolution
from the default 15.6 ms to 1 ms; combined with a deadline loop built on
perf_counter(), measured cumulative drift stays under 0.2 %. The wait loop polls
GetAsyncKeyState(VK_ESCAPE) every ~40 ms for instant abort.
IME trap — Virtual-keycode mode goes through the IME: if the target window is in
Chinese input mode, letters get composed into characters (measured: "line" → "里呢").
The tool therefore disables the target window's IME via ImmGetDefaultIMEWnd +
WM_IME_CONTROL/IMC_SETOPENSTATUS for the duration of typing. Unicode mode is immune.
Bracket matching — a single-pass stack scan: openers push, closers pop and get
marked as skipped. Unmatched closers (a smiley like :)) never enter the stack and
are typed normally. The right arrow and Delete must carry KEYEVENTF_EXTENDEDKEY,
or some applications ignore them.
UI — tkinter/ttk with the clam theme. Checkbox indicators are self-drawn PNGs (base64-embedded, no Pillow needed at runtime) because clam renders checkmarks as "✗" on some systems, making toggle state unreadable.
- Games reading hardware directly via DirectInput / Raw Input ignore
SendInput. That needs a hardware-level solution (e.g. an Arduino acting as a HID keyboard). - If the target application runs elevated, this tool must run elevated too, otherwise UIPI blocks the injected input.
- PyInstaller one-file builds are sometimes flagged by antivirus. The source is
源码/keysim.py— read it yourself. - Runs entirely offline; no network access, no file I/O.
