PySimpleGUI与Pygame合并时无法捕获键盘按下事件问题求助
问题根源
PySimpleGUI和Pygame默认都会独立抢占系统输入事件,你开启了PySimpleGUI的return_keyboard_events=True参数后,窗口会优先捕获所有键盘按下事件,处理完成后仅会释放按键抬起事件传递给Pygame的事件队列,因此你在Pygame侧只能监听到KEYUP。同时两套独立的阻塞式事件循环没有做同步,就会出现无限阻塞或程序崩溃。
可行解决方案
方案1:统一使用PySimpleGUI事件循环(推荐)
完全放弃Pygame的独立事件监听,将Pygame的渲染结果嵌入到PySimpleGUI控件中,所有输入逻辑走PySimpleGUI的事件体系。
步骤1:调整布局和窗口配置
在PySimpleGUI的布局中新增一个Image控件用于承载Pygame的渲染画面,窗口参数保持原有配置即可:
Win_layout = [ # 原有布局控件 [sg.Image(key='-PYGAME-VIEW-', size=(你需要的Pygame画面宽度, 你需要的Pygame画面高度))], # 其他布局控件 ] window = sg.Window('UNICHALL V2.0', Win_layout, border_depth =20, resizable = True, keep_on_top = False, finalize=True, return_keyboard_events=True, use_default_focus=False)
步骤2:调整Pygame初始化逻辑
不要让Pygame创建独立窗口,仅初始化渲染模块即可,避免抢焦点:
pygame.display.init() pygame.display.set_mode((1,1), pygame.NOFRAME) # 创建最小无框隐藏窗口,仅作为渲染载体
步骤3:合并主循环逻辑
统一使用PySimpleGUI的非阻塞事件循环,按键逻辑直接从PySimpleGUI的返回事件中获取,Pygame渲染结果转成图片流更新到Image控件:
while True: # timeout设为10ms,非阻塞读取避免卡住渲染 event, values = window.read(timeout=10) if event == sg.WIN_CLOSED: break # 替代原Pygame的KEYDOWN监听逻辑 if event.startswith('KEYDOWN:'): key_code = int(event.split(':')[1].strip()) stop = pygame.time.get_ticks() delta = stop - start if key_code == 27: sys.exit(1) # 其他原有按键逻辑直接复用即可 # 原有Pygame的绘制逻辑保持不变 # pygame.draw.xxx 等绘制代码 # 绘制完成后将surface转成图片流更新到PySimpleGUI pygame_surface = pygame.Surface((你需要的Pygame画面宽度, 你需要的Pygame画面高度)) view = pygame.surfarray.array3d(pygame_surface) view = view.swapaxes(0,1) # 用PIL转换不需要额外安装OpenCV from PIL import Image import io img = Image.fromarray(view) bio = io.BytesIO() img.save(bio, format='PNG') imgbytes = bio.getvalue() window['-PYGAME-VIEW-'].update(data=imgbytes)
方案2:保留Pygame事件循环,关闭PySimpleGUI的按键抢占
如果你更习惯用Pygame的事件体系,可以关闭PySimpleGUI的按键捕获权限,让事件优先传递给Pygame:
步骤1:修改窗口配置
window = sg.Window('UNICHALL V2.0', Win_layout, border_depth =20, resizable = True, keep_on_top = False, finalize=True, return_keyboard_events=False, # 关闭PySimpleGUI的按键事件捕获 use_default_focus=False)
步骤2:合并主循环逻辑
PySimpleGUI事件用非阻塞读取,避免阻塞Pygame的事件处理:
while True: # timeout=0完全非阻塞读取PySimpleGUI事件 event, values = window.read(timeout=0) if event == sg.WIN_CLOSED: break # 原有Pygame的事件处理逻辑完全保持不变 for event in pygame.event.get(): if event.type == pygame.KEYDOWN: stop = pygame.time.get_ticks() delta = stop - start key = event.key if key == 27: sys.exit(1) # 其他Pygame事件逻辑 # 原有Pygame渲染逻辑保持不变 pygame.display.flip()
注意:该方案需要保证Pygame窗口处于焦点状态,否则按键事件依然会被PySimpleGUI拦截。
内容的提问来源于stack exchange,提问作者Gadgettyke
相关产品推荐
相关产品推荐

