如何在Python终端游戏中实现文本框闪烁光标?
解决终端游戏中阻塞输入下的闪烁光标问题
核心思路
你的get_key()是阻塞式的,导致主线程卡在等待按键时无法处理光标闪烁。解决关键是将输入监听与屏幕渲染解耦,以下两种方案均可实现需求:
方案一:多线程分离输入与渲染
保留现有get_key()函数,用后台线程处理按键监听,主线程负责定时切换光标状态并重绘屏幕,改动最小。
实现步骤
- 用线程安全队列传递按键结果,避免线程冲突。
- 启动后台线程持续监听按键,将结果存入队列。
- 主线程循环:
- 从队列取出按键,更新屏幕内容与光标位置。
- 定时切换光标显示状态(显示/隐藏)。
- 清屏后重绘屏幕,根据光标状态决定是否渲染
█。
代码示例
import threading import queue import time import os # 假设这是你已有的自定义get_key()函数 def get_key(): if os.name == 'nt': import msvcrt return msvcrt.getch().decode('utf-8') else: import sys, termios, tty fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) try: tty.setraw(fd) key = sys.stdin.read(1) finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) return key def main(): # 初始化屏幕参数 WIDTH, HEIGHT = 40, 10 screen = [[' ' for _ in range(WIDTH)] for _ in range(HEIGHT)] cursor_row, cursor_col = 0, 0 cursor_visible = True blink_interval = 0.5 # 光标闪烁间隔 last_blink_time = time.time() # 线程安全队列存储按键 key_queue = queue.Queue() # 后台按键监听线程 def key_listener(): while True: key = get_key() key_queue.put(key) threading.Thread(target=key_listener, daemon=True).start() while True: # 处理队列中的按键 while not key_queue.empty(): key = key_queue.get() # 处理退出、退格、回车等逻辑 if key == '\x1b': # ESC键退出 return elif key in ('\b', '\x7f'): # 退格键 if cursor_col > 0: cursor_col -= 1 screen[cursor_row][cursor_col] = ' ' elif key == '\r': # 回车键 cursor_row += 1 cursor_col = 0 if cursor_row >= HEIGHT: # 屏幕滚动逻辑(可选) screen.pop(0) screen.append([' ' for _ in range(WIDTH)]) cursor_row = HEIGHT - 1 elif len(key) == 1 and cursor_col < WIDTH: # 普通字符输入 screen[cursor_row][cursor_col] = key cursor_col += 1 # 更新光标闪烁状态 current_time = time.time() if current_time - last_blink_time >= blink_interval: cursor_visible = not cursor_visible last_blink_time = current_time # 清屏(ANSI转义序列比os.system更高效) print('\033[H\033[J', end='') # 重绘屏幕 for row_idx in range(HEIGHT): line = ''.join(screen[row_idx]) # 渲染光标 if row_idx == cursor_row and cursor_visible: line = line[:cursor_col] + '█' + line[cursor_col+1:] print(line) # 控制循环频率,降低CPU占用 time.sleep(0.01) if __name__ == '__main__': main()
方案二:非阻塞输入替换阻塞函数
改写get_key()为非阻塞版本,主线程可同时检查输入和处理光标闪烁,无需多线程。
实现步骤
- 利用
select或终端属性设置,将输入改为非阻塞模式。 - 主线程循环:
- 检查是否有按键输入,有则处理。
- 定时切换光标状态,重绘屏幕。
代码示例(Linux/macOS)
import sys import termios import tty import select import time import os def get_key_non_blocking(): fd = sys.stdin.fileno() old_settings = termios.tcgetattr(fd) try: tty.setcbreak(fd) # 0秒超时,立即返回是否有输入 if select.select([sys.stdin], [], [], 0)[0]: return sys.stdin.read(1) return None finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) def main(): WIDTH, HEIGHT = 40, 10 screen = [[' ' for _ in range(WIDTH)] for _ in range(HEIGHT)] cursor_row, cursor_col = 0, 0 cursor_visible = True blink_interval = 0.5 last_blink_time = time.time() while True: # 检查非阻塞输入 key = get_key_non_blocking() if key is not None: if key == '\x1b': return elif key in ('\b', '\x7f'): if cursor_col > 0: cursor_col -= 1 screen[cursor_row][cursor_col] = ' ' elif key == '\r': cursor_row += 1 cursor_col = 0 if cursor_row >= HEIGHT: screen.pop(0) screen.append([' ' for _ in range(WIDTH)]) cursor_row = HEIGHT - 1 elif len(key) == 1 and cursor_col < WIDTH: screen[cursor_row][cursor_col] = key cursor_col += 1 # 更新光标闪烁 current_time = time.time() if current_time - last_blink_time >= blink_interval: cursor_visible = not cursor_visible last_blink_time = current_time # 清屏并渲染 print('\033[H\033[J', end='') for row_idx in range(HEIGHT): line = ''.join(screen[row_idx]) if row_idx == cursor_row and cursor_visible: line = line[:cursor_col] + '█' + line[cursor_col+1:] print(line) time.sleep(0.01) if __name__ == '__main__': main()
注意事项
- 清屏方式:优先用ANSI转义序列
\033[H\033[J,比os.system('cls/clear')更快,减少屏幕闪烁。 - 线程安全:多线程方案必须用线程安全队列传递按键,避免直接共享变量导致冲突。
- 终端兼容性:Windows与Linux/macOS的终端输入API不同,
get_key()需对应调整。 - CPU占用:循环中加入
time.sleep(0.01)避免主线程过度占用资源。
内容的提问来源于stack exchange,提问作者Dhananjoy Bhuyan
相关产品推荐
相关产品推荐

