使用pynput开发TUI时VSCode捕获按键写入代码的问题
自定义TUI按键输入异常问题:VSCode捕获终端按键写入Python文件
我正在开发一个基于pynput的自定义TUI网格游戏,核心功能是通过方向键控制角色移动,但遇到严重异常:运行代码时,终端输入的按键(包括系统终端的按键)会被VSCode捕获并直接写入我的Python源码文件。怀疑是键盘监听器未正确关闭,但因首次使用pynput且VSCode拥有系统级权限,无法确定问题根源。
相关代码
import math from pynput import keyboard from IPython.display import clear_output open_field = " _ " wall = " X " ''' The function, "coordinate_field" creates a grid like the one below, and also prints the location and movement of characters within the grid as the code runs. In future iterations, I intend to make the walls impassable, add momentum and battle functionality. X X X X X X X X X X X X X _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ X X _ _ _ _ _ _ _ _ _ _ X X X X X X X X X X X X X ''' def coordinate_field (p1_location, p2_location, p1_direction, p2_direction): x_coordinate = 0 y_coordinate = 1 field = "" ''' The grid is 12*12. 156 is 12*12+12 because adding each line break takes an iteration as well. ''' for i in range(156): x_coordinate += 1 coordinate_point = [x_coordinate, y_coordinate] if x_coordinate > 12: field += "\n" x_coordinate -= 13 y_coordinate += 1 elif y_coordinate in [1,12] or x_coordinate in [1,12]: field += wall # The following elifs add the characters. elif coordinate_point == p1_location: field += p1_direction elif coordinate_point == p2_location: field += p2_direction else: field += open_field clear_output(wait=True) print(field) p1_location = [4, 7] ''' This following list will be joined before going in to the function. I think that it needs to be a list at this stage because p1_direction += "<" and such didn't seem to work in this context. ''' p1_direction = ["-","-","-"] ''' on_press is probably the source of the issue, unless the keyboard.Listener is. I don't really understand either, and got both from ChatGPT. ''' def on_press(key): try: if key == keyboard.Key.enter: return False # Stop listener elif key == keyboard.Key.left: p1_direction.append("<") elif key == keyboard.Key.right: p1_direction.append(">") elif key == keyboard.Key.up: p1_direction.append("^") elif key == keyboard.Key.down: p1_direction.append("v") except AttributeError: pass def movement (p1_direction): print ("Type three moves: ") with keyboard.Listener(on_press=on_press) as listener: listener.join() # This loop guarantees that only the last three directional # inputs are counted. while len(p1_direction) > 3: p1_direction.remove(p1_direction[0]) # This translates the inputs into motion. for i in range(len(p1_direction)): if p1_direction[i] == "^": p1_location[1] -= 1 if p1_direction[i] == "v": p1_location[1] += 1 if p1_direction[i] == "<": p1_location[0] -= 1 if p1_direction[i] == ">": p1_location[0] += 1 # Finally, the list is joined, used as an argument in the # coordinate_field function, and separated again. p1_direction = "".join(p1_direction) coordinate_field (p1_location, [7,5], p1_direction, "^^<") p1_direction = list(p1_direction) return p1_direction # All this code will someday be part of a larger game, but for # now, I run it endlessly to test it. while True: p1_direction = movement(p1_direction)
解决方案
替换全局监听器为局部终端监听
pynput的keyboard.Listener默认是全局键盘监听,会捕获整个系统的按键事件,这是导致VSCode拦截输入的核心原因。改用pynput.keyboard.Events做局部监听,仅捕获当前终端的按键:from pynput.keyboard import Events, Key def movement(p1_direction): print("Type three moves: ") moves = [] with Events() as events: while len(moves) < 3: event = events.get() if isinstance(event, Events.Press): if event.key == Key.left: moves.append("<") elif event.key == Key.right: moves.append(">") elif event.key == Key.up: moves.append("^") elif event.key == Key.down: moves.append("v") elif event.key == Key.enter: break # 保留最近3次输入,不足补全默认值 p1_direction = moves[-3:] if len(moves)>=3 else moves + ["-"]*(3-len(moves)) # 移动逻辑不变 for i in range(len(p1_direction)): if p1_direction[i] == "^": p1_location[1] -= 1 if p1_direction[i] == "v": p1_location[1] += 1 if p1_direction[i] == "<": p1_location[0] -= 1 if p1_direction[i] == ">": p1_location[0] += 1 p1_direction_str = "".join(p1_direction) coordinate_field(p1_location, [7,5], p1_direction_str, "^^<") return list(p1_direction_str)确保监听器彻底终止
原代码中虽通过return False终止监听器,但循环调用可能导致残留。在with块结束后显式调用stop():with keyboard.Listener(on_press=on_press) as listener: listener.join() listener.stop() # 强制终止监听器调整VSCode终端设置
关闭VSCode的Terminal: Allow Chained Commands选项,或改用系统外部终端运行代码,避免VSCode终端拦截全局按键事件。消除全局变量依赖
原代码中on_press直接修改全局p1_direction,易引发状态混乱。改用类封装角色状态,或通过回调传递参数,减少全局变量使用。
内容的提问来源于stack exchange,提问作者Porter Williams
相关产品推荐
相关产品推荐

