第 6 篇:用 PyQt5 实现 1:1 像素级完美复刻 Pelco KBD300A 键盘
真正的“永不磨损的实体键盘”诞生,Windows 7 完美运行,视觉与手感 99.9% 还原
发布时间:2025 年 12 月
一、背景与进化历程
在前五篇文章中,我们已经完成了从 协议解析 → 链式调用类 → 基础 GUI 的逐步演进。
今天正式发布终极形态:
一台可以直接替代真实 KBD300A 的 软件键盘。
连最挑剔的老工程师把真机摆在旁边都分不出来哪个是真的。
核心特性:
- 像素级视觉还原:按钮圆角、边框、阴影与按下动画模拟实体键感。
- 摇杆高精度模拟:鼠标拖动产生
pan/tilt值,范围 (-127…127),自动回中。 - LCD 七段显示与呼吸闪烁:2800ms 周期的微弱呼吸效果,支持缩放。
- 响应式布局:基于基准窗口尺寸按比例缩放所有控件与字体。
- 主题切换:暗色与浅色主题,数字键(0–9)随主题动态变色;C/E 键保留固定强调色以便快速识别。
- 串口占位支持:可选
pyserial集成,保留发送/接收占位点,便于后续实现 Pelco 协议发送。
二、关键代码
下面摘取关键片段,便于直接对照与复用。
样式生成函数
def btn_style_template(bg_color, border_color, font_px, radius_px, border_w=4):
return f"""
QPushButton {{
background: {bg_color};
color: {THEMES['current']['TEXT_PRIMARY']};
font: bold {font_px}px 'Arial';
border: {border_w}px outset {border_color};
border-radius: {radius_px}px;
}}
QPushButton:hover {{
background: {THEMES['current']['ACCENT_SOFT']};
}}
QPushButton:pressed {{
border-style: inset;
background: rgba(255,255,255,0.02);
}}
QPushButton:disabled {{
color: rgba(200,200,200,0.4);
background: rgba(255,255,255,0.02);
}}
"""
创建数字按键时的动态标记
# 在构建数字键时
if ch.isdigit():
default_bg = 'dynamic' # 动态背景,随主题变化
elif ch == "C":
default_bg = "#d9534f" # Clear 红(固定)
elif ch == "E":
default_bg = "#2ecc71" # Enter 绿(固定)
btn.default_bg = default_bg
# 初始样式
initial_bg = THEMES['current']['BTN_BG'] if default_bg == 'dynamic' else default_bg
btn.setStyleSheet(btn_style_template(initial_bg, THEMES['current']['BTN_BORDER'], 28, 14))
在 apply_theme / apply_scaling 中统一应用
# 在 apply_theme 或 apply_scaling 中
for btn in self.num_buttons:
if getattr(btn, 'default_bg', None) == 'dynamic':
bg = THEMES['current']['BTN_BG']
else:
bg = btn.default_bg
btn.setStyleSheet(btn_style_template(bg, THEMES['current']['BTN_BORDER'], font_size_num, radius_num))
AnimatedLCD 呼吸闪烁
class AnimatedLCD(QtWidgets.QLCDNumber):
def __init__(self, digits=4, parent=None):
super().__init__(digits, parent)
self._font_px = 28
self.opacity = 1.0
self.timer = QtCore.QTimer(self)
self.timer.timeout.connect(self.flicker)
self.timer.start(2800)
def _apply_theme(self):
t = THEMES['current']
style = f"""
QLCDNumber {{
background: {t['LCD_BG']};
color: {t['ACCENT']};
border: 2px solid {t['LCD_BORDER']};
border-radius: 8px;
padding: 6px;
font: bold {self._font_px}px 'Consolas';
}}
"""
self.setStyleSheet(style)
def flicker(self):
self.opacity = 0.75 if self.opacity == 1.0 else 1.0
self._apply_theme()
RealJoystick 核心回调
class RealJoystick(QtWidgets.QWidget):
pan_tilt_changed = QtCore.pyqtSignal(int, int)
def mouseMoveEvent(self, e):
if self.dragging:
vec = e.pos() - self.center
length = (vec.x() ** 2 + vec.y() ** 2) ** 0.5
if length > self._max_radius and length != 0:
scale = self._max_radius / length
vec = QtCore.QPoint(int(vec.x() * scale), int(vec.y() * scale))
self.pos = self.center + vec
self.update()
pan = int(vec.x() / self._max_radius * 127)
tilt = int(-vec.y() / self._max_radius * 127)
self.pan_tilt_changed.emit(pan, tilt)
三、运行环境与要求
最低配置:
- 操作系统:Windows 7 SP1(32/64 位均可)
- Python:3.7.9
- 依赖库:
- PyQt5==5.15.2
- pyserial
安装命令:
pip install PyQt5==5.15.2 pyserial
运行方式:
python KBD300A_main.py
打包命令:
pyinstaller --clean --noconfirm KBD300A_main.spec
打包版本文件:
# UTF-8
VSVersionInfo(
ffi=FixedFileInfo(
filevers=(1, 0, 0, 0),
prodvers=(1, 0, 0, 0),
mask=0x3f,
flags=0x0,
OS=0x40004, # Windows NT 32-bit
fileType=0x1,
subtype=0x0,
date=(0, 0)
),
kids=[
StringFileInfo([
StringTable(
'080404b0', # 简体中文
[
StringStruct('CompanyName', '智码电子'),
StringStruct('FileDescription', 'KBD300软键盘控制软件'),
StringStruct('FileVersion', '1.0.0.0'),
StringStruct('InternalName', 'kbd300_tool'),
StringStruct('LegalCopyright', '版权所有 © 2025 我送炭你添花'),
StringStruct('OriginalFilename', 'KBD300A_V1.0.exe'),
StringStruct('ProductName', 'KBD300软键盘'),
StringStruct('ProductVersion', '1.0.0.0')
]
)
]),
VarFileInfo([
VarStruct('Translation', [0x0804, 1200])
])
]
)
双击运行,即刻出现一台 永不磨损、永不掉键 的 Pelco KBD300A!
四、运行界面
暗色主题:

浅色主题:

五、资源
源代码和打包后的exe文件,请点击(稍后上传)
六、下篇预告
下一篇(第 7 篇)将带来更强大的功能:
《内置完整宏脚本编辑器 + 实时解释器 + 无限长度 Pattern 录制与单步调试》
届时,这台“软件键盘”将拥有原装 KBD300A 永远不可能具备的 超级大脑:
1170

被折叠的 条评论
为什么被折叠?



