python - python 中的键盘输入(通过 pynput)和线程

标签 python multithreading input

我正在尝试用 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循环将检查变量是否 animateTrue并写入新的 * .

我必须添加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/

相关文章:

python - 使用python Mechanize访问时没有显示网站表,我如何找到它?

java - Tomcat 中的 Servlet、过滤器和线程

c# - 在 WPF 中显示 "Wait"屏幕

java - Java 中的 Play 框架异步处理和阻塞 I/O

jquery - 根据输入值运行函数 - jquery

javascript - 从表单字段构建在新窗口中打开的 URL

javascript - 需要帮助通过 JavaScript 和 JQuery 创建基于点击的表单元素

python - 如何处理 Thrift 客户端断开连接问题

python - 如果调用程序结束,Popen 对象会发生什么

python - 不理解 "Longest Substring with At Least K Repeating Characters"的本质