我正在尝试用 Python 3 制作一种基于文本的游戏。对于该游戏,我需要监听键盘输入,特别是测量按住按键的时间,同时将内容打印到屏幕上。我试图从制作一个最小的工作示例开始。
首先,以下代码使用 pynput
似乎成功测量了用户按住某个键的时间长度:
from pynput import keyboard
import time
print("Press and hold any key to measure duration of keypress. Esc ends program")
# A dictionary of keys pressed down right now and the time each was pressed down at
keys_currently_pressed = {}
def on_press(key):
global keys_currently_pressed
# Record the key and the time it was pressed only if we don't already have it
if key not in keys_currently_pressed:
keys_currently_pressed[key] = time.time()
def on_release(key):
global keys_currently_pressed
if key in keys_currently_pressed:
animate = False
duration = time.time() - keys_currently_pressed[key]
print("The key",key," was pressed for",str(duration)[0:5],"seconds")
del keys_currently_pressed[key]
if key == keyboard.Key.esc:
# Stop the listener
return False
with keyboard.Listener(on_press = on_press, on_release=on_release, suppress=True) as listener:
listener.join()
现在我想做的是,仅当用户按下某个键时,才将基于文本的“动画”打印到屏幕上。在下面的示例中,我的“动画”只是每半秒打印一次 "*"
。
到目前为止,我已经尝试让第二个线程处理“动画”,但在多线程方面我完全是新手。以下代码将在正确的时间启动动画,但不会停止它。
from pynput import keyboard
import sys
import time
import threading
print("Press and hold any key to measure duration of keypress. Esc ends program")
# A dictionary of keys pressed down right now and the time each was pressed down at
keys_currently_pressed = {}
def my_animation():
# A simple "animation" that prints a new "*" every half second
limit = 60 # just in case, don't do more than this many iterations
j = 0
while j<limit:
j += 1
sys.stdout.write("*")
time.sleep(0.5)
anim = threading.Thread(target=my_animation)
def on_press(key):
global keys_currently_pressed
# Record the key and the time it was pressed only if we don't already have it
if key not in keys_currently_pressed:
keys_currently_pressed[key] = time.time()
anim.start()
def on_release(key):
global keys_currently_pressed
if key in keys_currently_pressed:
animate = False
duration = time.time() - keys_currently_pressed[key]
print("The key",key," was pressed for",str(duration)[0:5],"seconds")
del keys_currently_pressed[key]
if key == keyboard.Key.esc:
# Stop the listener
return False
with keyboard.Listener(on_press = on_press, on_release=on_release, suppress=True) as listener: listener.join()
这是一种方法(遵循@furas的评论),其中动画在with
语句之后进行编码,但是我无法让它为我工作:
from pynput import keyboard
import time
print("Press and hold any key to measure duration of keypress. Esc ends program")
# A dictionary of keys pressed down right now and the time each was pressed down at
keys_currently_pressed = {}
# animation flag
anim_allowed = False
def on_press(key):
global keys_currently_pressed
global anim_allowed
# Record the key and the time it was pressed only if we don't already have it
if key not in keys_currently_pressed:
keys_currently_pressed[key] = time.time()
anim_allowed = True
def on_release(key):
global keys_currently_pressed
global anim_allowed
if key in keys_currently_pressed:
animate = False
duration = time.time() - keys_currently_pressed[key]
print("The key",key," was pressed for",str(duration)[0:5],"seconds")
del keys_currently_pressed[key]
anim_allowed = False
if key == keyboard.Key.esc:
# Stop the listener
return False
with keyboard.Listener(on_press = on_press, on_release=on_release, suppress=True) as listener:
while anim_allowed:
sys.stdout.write("*")
time.sleep(0.5)
listener.join()
最终我希望能够通过更复杂的动画来做到这一点。例如
def mysquare(delay):
print("@"*10)
time.sleep(delay)
for i in range(8):
print("@" + " "*8 + "@")
time.sleep(delay)
print("@"*10)
解决这个问题的正确方法是什么?非常感谢!
最佳答案
Listener
已使用thread
所以不需要在单独的线程中运行动画。您可以在当前线程中运行它
with keyboard.Listener(on_press = on_press, on_release=on_release, suppress=True) as listener:
#... your code ...
listener.join()
或没有 with ... as ...
listener = keyboard.Listener(on_press = on_press, on_release=on_release, suppress=True)
listener.start()
#... your code ...
#listener.wait()
listener.join()
您甚至可以在那里运行长时间运行的代码 - 即。无尽while
循环将检查变量是否 animate
是 True
并写入新的 *
.
我必须添加sys.stdout.flush()
在我的 Linux 上看到 *
在屏幕上。
我的版本:
当您按下任何按钮时,它会始终运行动画,但也有带有变量 counter
的代码将动画限制为 6 个 Action 。如果您在运行动画时按新键,则会重置此计数器并且动画会更长。
这个循环必须一直运行以检查是否有新的动画 - 当动画完成时你无法完成这个循环
from pynput import keyboard
import sys
import time
# --- functions ---
def on_press(key):
global keys_currently_pressed
global animate
#global counter
# Record the key and the time it was pressed only if we don't already have it
if key not in keys_currently_pressed and key != keyboard.Key.esc:
keys_currently_pressed[key] = time.time()
animate = True
#counter = 0 # reset counter on new key
def on_release(key):
global keys_currently_pressed
global animate
if key in keys_currently_pressed:
duration = time.time() - keys_currently_pressed[key]
print("The key", key, "was pressed for", str(duration)[0:5], "seconds")
del keys_currently_pressed[key]
if not keys_currently_pressed:
animate = False
if key == keyboard.Key.esc:
# Stop the listener
return False
# --- main ---
print("Press and hold any key to measure duration of keypress. Esc ends program")
# A dictionary of keys pressed down right now and the time each was pressed down at
keys_currently_pressed = {}
animate = False # default value at start (to use in `while` loop)
#limit = 6 # limit animation to 6 moves
#counter = 0 # count animation moves
with keyboard.Listener(on_press = on_press, on_release=on_release, suppress=True) as listener:
while listener.is_alive(): # infinite loop which runs all time
if animate:
#sys.stdout.write("\b *") # animation with removing previous `*`
sys.stdout.write("*") # normal animation
sys.stdout.flush() # send buffer on screen
#counter += 1
#if counter >= limit:
# counter = 0
# animate = False
time.sleep(0.5)
listener.join()
关于python - python 中的键盘输入(通过 pynput)和线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65222895/