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

Python curses库倒计时器与用户输入功能故障排查求助

修复后的完整代码
import time
import threading
import curses
from threading import Lock

def countdown_timer(stdscr, total_seconds, inputs, lock):
    start_time = time.time()
    while True:
        elapsed = time.time() - start_time
        remaining = max(total_seconds - int(elapsed), 0)
        
        # 线程锁保护共享输入变量
        with lock:
            display_text = f"Time Left: {remaining} seconds - Input: {inputs[0]}"
        stdscr.addstr(0, 0, display_text)
        stdscr.refresh()
        
        if remaining == 0:
            break
        
        # 短间隔休眠,平衡计时精度和CPU占用
        time.sleep(0.1)
    
    # 倒计时结束提示
    with lock:
        end_text = f"Time's up! - Input: {inputs[0]}"
    stdscr.addstr(0, 0, end_text)
    stdscr.refresh()

def main(stdscr):
    # Curses 初始化配置
    curses.noecho()
    curses.cbreak()
    stdscr.keypad(True)
    stdscr.nodelay(True)  # 设置非阻塞输入,避免线程阻塞
    curses.curs_set(1)    # 显示光标,明确输入位置
    
    inputs = [""]
    input_lock = Lock()
    total_seconds = 45
    
    # 启动计时器线程
    timer_thread = threading.Thread(
        target=countdown_timer,
        args=(stdscr, total_seconds, inputs, input_lock)
    )
    timer_thread.start()
    
    # 主线程专职处理用户输入
    while timer_thread.is_alive():
        key = stdscr.getch()
        if key == ord('\n'):
            break
        elif key in (curses.KEY_BACKSPACE, 127):  # 处理退格键
            with input_lock:
                inputs[0] = inputs[0][:-1] if inputs[0] else ""
        elif key != -1 and 32 <= key <= 126:  # 仅接受可打印字符
            with input_lock:
                inputs[0] += chr(key)
        
        # 输入后立即更新显示,实现实时反馈
        elapsed = time.time() - start_time
        remaining = max(total_seconds - int(elapsed), 0)
        with input_lock:
            display_text = f"Time Left: {remaining} seconds - Input: {inputs[0]}"
        stdscr.addstr(0, 0, display_text)
        stdscr.refresh()
        time.sleep(0.01)  # 降低CPU占用
    
    # 等待计时器线程结束
    timer_thread.join()
    
    # 等待用户确认退出
    stdscr.addstr(1, 0, "Press Enter to exit...")
    stdscr.nodelay(False)
    stdscr.getch()

if __name__ == "__main__":
    curses.wrapper(main)
问题根源与修复说明

1. 倒计时跳数/不实时问题

  • 原问题:依赖range循环+time.sleep(1)计时,输入触发的getch()会打断sleep,导致计时周期混乱;两个线程抢用getch(),进一步干扰计时节奏。
  • 修复:
    • 改用时间戳计算剩余时间,基于启动时间的差值计算剩余时长,不受sleep打断影响。
    • 计时器线程用短间隔休眠(0.1秒),既保证每秒更新一次显示,又不会阻塞主线程处理输入。

2. 用户输入无法捕获问题

  • 原问题:主线程和计时器线程同时调用getch(),输入事件被随机抢占;输入后没有实时更新显示,导致输入反馈延迟。
  • 修复:
    • 让主线程专职处理输入,计时器线程仅负责更新显示,避免资源竞争。
    • 添加退格键处理逻辑,支持输入修正;限制仅接受可打印字符,避免控制字符干扰显示。
    • 每次输入后立即刷新显示,实现输入内容的实时同步。

3. Curses 初始化/清理问题

  • 原问题:手动初始化/清理容易遗漏终端状态恢复;未设置非阻塞输入,导致线程阻塞。
  • 修复:
    • 使用curses.wrapper()自动处理Curses的初始化和终端状态恢复,避免异常场景下终端无法正常使用。
    • 设置nodelay(True)让getch()非阻塞,主线程不会因等待输入卡住。
    • 显示光标,让用户明确当前输入位置,提升交互体验。
    • 用线程锁Lock()保护共享的输入变量,避免多线程竞争导致的显示异常。

内容的提问来源于stack exchange,提问作者edge selcuk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 13:17:40