PyInstaller打包Win10执行文件触发ValueError: '0' is not in list问题
问题分析
你遇到的ValueError: '0' is not in list错误,本质是Windows10打包后的EXE中,GetKeyboardLayout返回的KLID经过位运算后得到lid=0,而你的lcid字典里没有这个键,导致key_list.index(str(lid))触发报错。Windows11上正常是因为系统API在切换键盘布局时的行为差异——Win11不会返回临时的0值,而Win10在切换瞬间可能出现这个过渡状态。
另外代码还有几个潜在问题:
- 每次触发都重新初始化
pyttsx3引擎,既影响性能也可能导致资源泄漏 - 用
keyboard.on_press_key('shift')再判断Alt的方式,容易产生重复触发 - 未处理不在预期内的LCID值,容错性不足
修复后的代码
import keyboard import time import ctypes import pyttsx3 from plyer import notification # 全局初始化语音引擎,避免重复创建 engine = pyttsx3.init() lcid = { "1040": "Italian (it_IT)", "1033": "English (en_US)", "1037": "Hebrew (he_IL)", "2058": "Spanish (es_MX)", } def on_layout_switch(): user32 = ctypes.WinDLL('user32', use_last_error=True) curr_window = user32.GetForegroundWindow() if not curr_window: return # 无效窗口直接返回 thread_id = user32.GetWindowThreadProcessId(curr_window, None) klid = user32.GetKeyboardLayout(thread_id) lid = klid & 0xFFFF # 等价于2**16-1,写法更简洁 lid_str = str(lid) # 处理lid=0的临时状态或未知布局,直接跳过 if lid_str not in lcid: return # 语音播报 lang_name = lcid[lid_str].split(' ')[0] engine.say(lang_name) engine.runAndWait() # 发送通知 notification.notify( title="Language Keyboard Switch", message=lcid[lid_str], timeout=1 # 延长超时时间,确保通知能被看到 ) # 直接监听Alt+Shift组合键,避免误触发 keyboard.add_hotkey('alt+shift', on_layout_switch) # 保持脚本运行 try: while True: time.sleep(1) except KeyboardInterrupt: print("脚本已退出")
关键修复点
- 增加容错处理:直接跳过不在
lcid中的值,避免触发索引错误 - 全局初始化语音引擎:只创建一次引擎,提升性能和稳定性
- 改用组合键监听:直接绑定
alt+shift,代替原有的监听Shift再判断Alt的逻辑,减少重复触发 - 优化窗口有效性检查:避免无效窗口导致的API调用异常
- 简化位运算写法:用
0xFFFF替代2**16-1,代码更直观 - 调整通知超时:将原0.1秒延长至1秒,确保用户能看到通知内容
打包注意事项
用PyInstaller打包时,建议添加以下参数避免依赖缺失:
pyinstaller --onefile --windowed --hidden-import plyer.platforms.win.notification --hidden-import pyttsx3.drivers.sapi5 your_script.py
--onefile:生成单个独立EXE文件--windowed:不显示控制台窗口(调试时可去掉)--hidden-import:显式指定plyer和pyttsx3的隐藏依赖,防止打包后运行时找不到模块
内容的提问来源于stack exchange,提问作者Giovanni Lo Bocchiaro
相关产品推荐
相关产品推荐

