如何实现Python程序按ESC键随时退出循环(解决msvcrt不稳定问题)
餐饮模拟程序ESC退出问题解决方案
问题背景
开发Python餐饮模拟程序,需求是按下ESC键随时退出程序循环:
- 尝试
keyboard库,因在命令提示符运行而非IDE,功能失效 - 改用
msvcrt库,仅偶然触发退出,无法稳定工作
问题根源
代码仅在每次客户流程结束后检查ESC键,但程序大部分时间卡在input()等待用户输入,此时msvcrt.kbhit()无法检测到按键。要实现随时响应ESC,必须在所有等待输入的环节加入实时按键检测。
解决方案
1. 自定义支持ESC的输入函数
编写input_with_esc()替代原生input(),在等待输入时实时检测ESC键,按下则直接终止程序:
def input_with_esc(prompt=""): print(prompt, end='', flush=True) user_input = [] while True: if msvcrt.kbhit(): char = msvcrt.getch() if char == b'\x1b': # ESC键 print("\n程序已退出") sys.exit() elif char == b'\r': # 回车键 print('') return ''.join(user_input) elif char == b'\x08': # 退格键 if user_input: user_input.pop() print('\b \b', end='', flush=True) else: # 普通字符 user_input.append(char.decode('utf-8')) print(char.decode('utf-8'), end='', flush=True) time.sleep(0.01)
2. 全局循环中添加ESC检测
在time.sleep()等可能阻塞的操作前后,加入ESC检测逻辑,确保全程可响应:
def check_esc(): if msvcrt.kbhit() and msvcrt.getch() == b'\x1b': print("\n程序已退出") sys.exit()
修改后的完整代码
import subprocess import sys import time import random import msvcrt def print_with_typing(text): for char in text: print(char, end='', flush=True) check_esc() # 打字过程中检测ESC time.sleep(0.01) def check_esc(): if msvcrt.kbhit() and msvcrt.getch() == b'\x1b': print("\n程序已退出") sys.exit() def input_with_esc(prompt=""): print(prompt, end='', flush=True) user_input = [] while True: check_esc() # 实时检测ESC if msvcrt.kbhit(): char = msvcrt.getch() if char == b'\r': # 回车键 print('') return ''.join(user_input) elif char == b'\x08': # 退格键 if user_input: user_input.pop() print('\b \b', end='', flush=True) else: user_input.append(char.decode('utf-8')) print(char.decode('utf-8'), end='', flush=True) time.sleep(0.01) def start_new_window(): subprocess.Popen(['start', 'cmd', '/k', 'python', 'CoffeeShop.py', 'new_window'], shell=True) sys.exit() if len(sys.argv) > 1 and sys.argv[1] == "new_window": print("running in new window\n\n") else: print("Opening a new window...") start_new_window() running = True while running: check_esc() daily_income = 0 wallet_amount = random.randint(5, 100) print_with_typing("Your wallet amount: $" + str(wallet_amount) + "\n") print_with_typing("Hello, \nWelcome to That Food Place!\n") check_esc() time.sleep(1) name = input_with_esc("What is your name?\n") print_with_typing("Hello, " + name + "! \nThank you so much for coming!") check_esc() time.sleep(1) entree = [ ("Dino Nuggets", "5 minutes", 5), ("Veggie Fries", "10 minutes!", 6), ("Impossible Steak", "20 minutes!", 25), ("Cheese Log", "15 minutes!", 10), ("Lobster Wings", "25 minutes!", 50), ] sides = [ ("French onion sticks", 2), ("Frog legs", 4), ("Buttered toast", 1), ("Crunch rat", 2), ("Apple", 1), ("Roasted stank", 10), ] drinks = [ ("Water", ""), ("Four loko", ""), ("Liquid grass", ""), ("Dirty water", ""), ("Coka", ""), ] print_with_typing("\nOur menu today consists of the following options:\n") for item in entree: print_with_typing("- " + item[0] + "\n") check_esc() time.sleep(2) order_list = [] while running: check_esc() order = input_with_esc("\nWhat would you like to order? (Enter 'done' to finish ordering)\n") if order.lower() == "done": break found_entree = None found_side = None found_drink = None for item in entree: if item[0].lower() in order.lower(): found_entree = item break for item in sides: if item[0].lower() in order.lower(): found_side = item break for item in drinks: if item[0].lower() in order.lower(): found_drink = item break if found_entree: item_price = found_entree[2] if item_price <= wallet_amount: side_order = input_with_esc("Would you like a side and drink with that?\n") if side_order.lower() == "yes": print_with_typing("Please choose a side from the following options:\n") for side in sides: print_with_typing("- " + side[0] + "\n") selected_side = input_with_esc() found_side = None for side in sides: if side[0].lower() == selected_side.lower(): found_side = side break if found_side: order_list.append(found_side[0]) print_with_typing("Great! One " + found_side[0] + " side added to your order.\n") else: print_with_typing("I'm sorry, we don't carry that side. Please choose from the listed options.\n") drink_order = input_with_esc("Would you like a drink as well?\n") if drink_order.lower() == "yes": print_with_typing("Please choose a drink from the following options:\n") for drink in drinks: print_with_typing("- " + drink[0] +"\n") selected_drink = input_with_esc() found_drink = None for drink in drinks: if drink[0].lower() == selected_drink.lower(): found_drink = drink break if found_drink: order_list.append(found_drink[0]) print_with_typing("Great! One " + found_drink[0] + " drink added to your order.\n") else: print_with_typing("I'm sorry, we don't carry that drink. Please choose from the listed options.\n") else: print_with_typing("Alrighty then!\n") print_with_typing("That will be $" + str(found_entree[2]) + ".\n") wallet_amount -= item_price daily_income += item_price order_list.append(found_entree[0]) check_esc() time.sleep(1) print_with_typing("Thank you, " + name + ". Your " + found_entree[0] + " will be out in " + found_entree[1] + "\n") check_esc() time.sleep(1) print_with_typing("$" + str(wallet_amount) + " remaining\n") else: check_esc() time.sleep(1) print_with_typing("I'm sorry, you don't have enough for this item. Please choose another item!\n") else: check_esc() time.sleep(1) print_with_typing("I'm sorry, we are not serving " + order + " today. Please choose from an item listed!\n") continue order_more = input_with_esc("Would you like to order something else?\n") if order_more.lower() == "no": break check_esc() time.sleep(3) print_with_typing("\nHere are your orders. Enjoy!\n") for item in order_list: print_with_typing("- " + item + "\n") check_esc() time.sleep(1) opinion = input_with_esc("Did you enjoy your meal, " + name + "?\n") if "no" in opinion.lower(): print_with_typing("I am sorry to hear that. Please enjoy a refund on your order!\n") wallet_amount += item_price print("Refunded " + str(item_price)) print("Current wallet amount = $" + str(wallet_amount)) elif "yes" in opinion.lower(): print_with_typing("I am happy to hear that! Enjoy the rest of your day!\n") check_esc() time.sleep(2) print_with_typing("Next customer, please!\n\n\n") print_with_typing("Thank you for visiting That Food Place. Have a great day!\n") print_with_typing("Daily income = $" + str(daily_income)) check_esc() time.sleep(5) sys.exit()
实用Python编程技巧
- 实时按键处理:Windows下用
msvcrt,Linux/macOS用termios,跨平台可封装统一接口 - 避免嵌套循环:把客户点餐流程封装成函数,用
return替代多层break,代码更清晰 - 复用查找逻辑:将查找菜单项目的代码封装成通用函数:
def find_item(query, menu): for item in menu: if item[0].lower() == query.lower(): return item return None - 状态集中管理:把
wallet_amount、daily_income等变量放在字典或类中,便于维护和修改
内容的提问来源于stack exchange,提问作者FalafelWaffle
相关产品推荐
相关产品推荐

