Python终端UI游戏:如何优雅修改指定位置打印字符
终端UI游戏菜单的光标(¤)移动优化方案
需求说明
我正在开发一款终端UI游戏,现有Python代码实现了基础菜单,但希望实现符号¤在菜单选项前的移动效果(如示例所示),不想用清屏重绘的 brute-force方式,想找类似BASIC中直接设置网格特定位置字符的实现方法,暂不想使用tkinter,寻求其他优雅方案。
现有基础菜单代码:
from ast import Pass from re import findall from time import sleep from ScrFX import invalidOption from os import system from getkey import getkey, keys def main_menu(): hobo = "**********************************************\n" #ho horizontal, bo border vebo = "** **\n" #ve vertical, bo border op1 = "** 1) Start Game **\n" op2 = "** 2) Load Game **\n" op3 = "** 3) Quit Game **\n" s = "\n" mainmenuscreen = (hobo+(vebo*5)+op1+vebo+op2+vebo+op3+vebo+(vebo*5)+hobo) print(mainmenuscreen) print("Please choose an option\n") def userkey(): usse = getkey() #us-er, se-lection if usse == '1': print("Start game") elif usse == '2': print("load game") elif usse == '3': system('cls') print("Goodbye!") quit() elif usse == '0': print("Secret Menu yay)") else: invalidOption() system('cls') print(mainmenuscreen) userkey() return(usse) userkey() main_menu()
期望的光标效果示例:
op1 = "** 1) Start Game **\n" op2 = "** ¤ 2) Load Game **\n" op3 = "** 3) Quit Game **\n"
解决方案
方案1:原生ANSI转义序列(无需额外库)
终端支持ANSI转义序列,可以直接控制光标位置并修改指定位置的字符,无需清屏重绘。核心是利用\033[<行>;<列>H移动光标到目标位置,再打印字符覆盖原有内容。
修改后的代码示例:
from time import sleep from os import system from getkey import getkey, keys def main_menu(): hobo = "**********************************************\n" vebo = "** **\n" op1 = "** 1) Start Game **\n" op2 = "** 2) Load Game **\n" op3 = "** 3) Quit Game **\n" mainmenuscreen = hobo + (vebo*5) + op1 + vebo + op2 + vebo + op3 + vebo + (vebo*5) + hobo print(mainmenuscreen) print("Please choose an option\n") # 定义每个选项对应的光标位置(行/列从1开始计数) option_positions = { '1': (7, 10), # op1的¤位置 '2': (9, 10), # op2的¤位置 '3': (11, 10) # op3的¤位置 } current_selected = '1' # 初始绘制光标 print(f"\033[{option_positions[current_selected][0]};{option_positions[current_selected][1]}H¤", end='', flush=True) def userkey(): nonlocal current_selected usse = getkey() # 清除当前选中项的光标 print(f"\033[{option_positions[current_selected][0]};{option_positions[current_selected][1]}H ", end='', flush=True) if usse in ['1', '2', '3']: current_selected = usse # 在新选中项绘制光标 print(f"\033[{option_positions[current_selected][0]};{option_positions[current_selected][1]}H¤", end='', flush=True) # 执行对应操作 if usse == '1': print("\nStart game") elif usse == '2': print("\nLoad game") elif usse == '3': system('cls') print("Goodbye!") quit() elif usse == '0': print("\nSecret Menu yay!") elif usse == keys.UP: # 上方向键切换选项 current_selected = '3' if current_selected == '1' else str(int(current_selected)-1) print(f"\033[{option_positions[current_selected][0]};{option_positions[current_selected][1]}H¤", end='', flush=True) elif usse == keys.DOWN: # 下方向键切换选项 current_selected = '1' if current_selected == '3' else str(int(current_selected)+1) print(f"\033[{option_positions[current_selected][0]};{option_positions[current_selected][1]}H¤", end='', flush=True) else: # 无效输入,恢复光标位置 print(f"\033[{option_positions[current_selected][0]};{option_positions[current_selected][1]}H¤", end='', flush=True) userkey() userkey() main_menu()
方案2:使用标准库curses(专业终端UI控制)
Python内置的curses库专门用于终端UI开发,无需额外安装,能直接管理窗口、光标位置和键盘输入,比手动编写ANSI序列更稳定、易维护。
简单实现示例:
import curses def main_menu(stdscr): # 清除屏幕 stdscr.clear() # 获取屏幕尺寸 height, width = stdscr.getmaxyx() # 绘制菜单边框和内容 hobo = "*" * 46 vebo = "*" + " " * 44 + "*" stdscr.addstr(0, 0, hobo) for i in range(1, 6): stdscr.addstr(i, 0, vebo) options = [ ("1) Start Game", 6), ("2) Load Game", 8), ("3) Quit Game", 10) ] for text, row in options: stdscr.addstr(row, 12, text) for i in range(12, 17): stdscr.addstr(i, 0, vebo) stdscr.addstr(17, 0, hobo) stdscr.addstr(19, 0, "Please choose an option") # 初始选中项索引 current_idx = 0 # 绘制初始光标 cursor_row = options[current_idx][1] stdscr.addstr(cursor_row, 8, "¤") stdscr.refresh() while True: key = stdscr.getch() # 清除当前光标 stdscr.addstr(cursor_row, 8, " ") if key == curses.KEY_UP and current_idx > 0: current_idx -= 1 elif key == curses.KEY_DOWN and current_idx < len(options)-1: current_idx += 1 elif key == ord('1'): current_idx = 0 elif key == ord('2'): current_idx = 1 elif key == ord('3'): current_idx = 2 elif key == ord('\n'): # 确认选择后执行对应操作 if current_idx == 0: stdscr.clear() stdscr.addstr(0,0,"Start game") stdscr.refresh() curses.napms(1000) break elif current_idx == 1: stdscr.clear() stdscr.addstr(0,0,"Load game") stdscr.refresh() curses.napms(1000) break elif current_idx == 2: stdscr.clear() stdscr.addstr(0,0,"Goodbye!") stdscr.refresh() curses.napms(1000) return # 更新光标位置并绘制 cursor_row = options[current_idx][1] stdscr.addstr(cursor_row, 8, "¤") stdscr.refresh() if __name__ == "__main__": curses.wrapper(main_menu)
内容的提问来源于stack exchange,提问作者Remi
相关产品推荐
相关产品推荐

