树莓派Python Pygame项目添加ESC键全局退出功能故障求解
问题根因
- 内层循环未绑定全局运行状态:按下ESC修改
running = False后,内层while GPIO.input(switches[0]) == GPIO.LOW循环没有校验running状态,程序始终卡在内层循环中,无法响应退出逻辑 - 帧率控制失效:
clock.tick(120)仅在主循环外执行了一次,没有在每次循环迭代中调用,既没有实现120fps的控速效果,也会拖慢pygame事件队列的处理效率 - 阻塞调用拖慢输入响应:
time.sleep(0.01)是阻塞式调用,休眠期间pygame无法处理新的输入事件,高频率运行下会出现按键事件丢失、响应延迟的问题
修复方案
核心修改点
- 所有循环判断条件增加
running状态校验,确保全局退出标记触发后可以跳出所有层级的循环 - 将
clock.tick(120)移到循环内部,替换time.sleep()作为帧率控制方式,避免阻塞事件处理 - 高帧率场景下,对于ESC这类全局响应按键,可直接使用
pygame.key.get_pressed()扫描键盘状态,响应速度比遍历事件队列更快
修改后的完整代码
# Importing all libraries import RPi.GPIO as GPIO import sys, time, atexit, pygame # Setup GPIO and Pygame GPIO.setmode(GPIO.BCM) pygame.init() # Define Tuples and Variables leds = (16,17,22,9,5) switches = (19,4,27,10,11) button_pressed = False taskcomplete = False # Pygame visual variables screen = pygame.display.set_mode( (1024,240) ) counterfont = pygame.font.Font('DSEG14Modern-Regular.ttf', 70) # Set Pygame refresh rate variable = clock clock = pygame.time.Clock() FPS = 120 # Clock variables sec_val = 0 sec = 0 mins = 0 hours = 0 # Status variables paused = False running = True # Start the clock start_time = pygame.time.get_ticks() # Defining Functions # Function that renders segment display on screen def time_convert(sec): sec = sec % 60 sec_val = ("Timer: {0}".format(round((sec), 2))) counting_text = counterfont.render(str(sec_val), 3, (134,145,255)) counting_rect = counting_text.get_rect(left = screen.get_rect().left) screen.fill( (0,0,0) ) screen.blit(counting_text, (300,40)) pygame.display.update() # Stopwatch function to compute for a SS:MS based stopwatch def stop_Watch(): end_time = time.time() time_lapsed = end_time - start_time sec_val = time_convert(time_lapsed) # Press Button 1 to start the game def but_3(): # 所有子函数中的循环都要增加running判断 while running and GPIO.input(switches[2]) == GPIO.LOW: GPIO.output(leds[2],True) # 统一用clock.tick控速 clock.tick(FPS) stop_Watch() GPIO.output(leds[2],False) print(" Button 3 is pressed! Exit") start_time = time.time() def buttonPress(channel): # This function gets called every time a button is pressed, if the button pressed is the same as the button # that is illuminated, then we set the "correct_button" variable to True, # otherwise we set the "incorrect_button" variable to True. # We need to set some variables to global so that this function can change their value. button_pressed = True def exit(): # This function gets called when we exit our script, using Ctrl+C print("GPIO Clean Up!") GPIO.cleanup() pygame.quit() sys.exit() # This tells our script to use the "exit()" without this, our "exit()" function would never be called. atexit.register(exit) #Loop through the leds to set them up for led in leds: # Set the led to be an ouput GPIO.setup(led, GPIO.OUT) # Turn the led off GPIO.output(led,False) # Loop through the switches to set them up for switch in switches: # Set the switch to be an input GPIO.setup(switch, GPIO.IN) # Add rising edge detection GPIO.add_event_detect(switch, GPIO.RISING, bouncetime=300) # Add the function "buttonPress" to be called when switch is pressed. GPIO.add_event_callback(switch, buttonPress) # Main sequence code # Start timer start_time = time.time() # Main loop while running: # 全局ESC扫描,优先级最高 keys = pygame.key.get_pressed() if keys[pygame.K_ESCAPE]: print("escape pressed") running = False break # 处理窗口退出事件 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False break # Press Button 1 to start the game # 内层循环增加running判断 while running and GPIO.input(switches[0]) == GPIO.LOW: # 内层循环也要同步检测全局事件和按键 keys = pygame.key.get_pressed() if keys[pygame.K_ESCAPE]: print("escape pressed") running = False break for event in pygame.event.get(): if event.type == pygame.QUIT: running = False break GPIO.output(leds[0],True) # 用clock.tick替代sleep,控制帧率120 clock.tick(FPS) stop_Watch() GPIO.output(leds[0],False) print(" Button 1 is pressed! Exit") running = False exit()
高帧率场景全局按键通用实现规范
- 全局按键检测优先级最高,放在所有业务逻辑之前执行
- 高频响应按键优先用
pygame.key.get_pressed()状态扫描,事件队列检测适合处理单次触发的低频输入(比如空格跳跃、回车确认) - 所有循环逻辑必须绑定全局运行状态,避免出现业务逻辑嵌套循环导致退出标记失效的问题
- 移除所有阻塞式休眠调用,统一用游戏引擎自带的帧率控制接口控速
内容的提问来源于stack exchange,提问作者monchmercader
相关产品推荐
相关产品推荐

