You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 12:37:11