python - 键盘输入(通过pynput)和python中的线程
问题描述
我正在尝试在 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
并写入 new *
。
我必须sys.stdout.flush()
在我的 Linux 上添加才能*
在屏幕上看到。
我的版本:
当您按下任何按钮时,它会一直运行动画,但也有带有变量counter
的代码将动画限制为 6 次移动。如果在运行动画时按新键,则会重置此计数器并且动画会更长。
此循环必须一直运行以检查是否有新动画 - 动画完成后您无法完成此循环
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()
推荐阅读
- kubernetes - Kubernetes API 服务器如何在节点上启动新调度的 Pod?
- c++ - CMake:使用项目安装 boost 组件库的正确方法
- react-native - 如何在 React Native 中获取截断的文本值?
- csv - 在bash中按名称(匹配模式)提取列
- python - 有效删除 pandas DataFrame 列中的相似字符串
- aws-lambda - AWS Lambda 上的 Puppeteer 目标关闭错误
- r - 在箱线图中添加线条,如何知道因子在 x 轴上的位置?
- sql - 从匹配模式 postgresql 的字符串中删除字符
- javascript - 在 javascript 中使用 mailchimp API 显示终端中的所有项目
- javascript - 为什么我的 Mongoose 用户导入无效?未捕获的 TypeError:分配给常量变量