Python 线程被键盘库阻塞?

Python threading blocked by keyboard library?

正在构建可切换为垃圾邮件的脚本。我遇到了以下问题。 首先是脚本的正常运行版本:

import keyboard
import threading

def spam_this():
    status = 0
    while True:
        if keyboard.is_pressed("F9") and status == 0:
            status = 1
            event.wait(1)
        if keyboard.is_pressed("F9") and status == 1:
            status = 0
            event.wait(1)
        while status == 1:
            if keyboard.is_pressed("F9") and status == 1:
                status = 0
                event.wait(1)
            print("test")

event = threading.Event()
threading.Thread(target=spam_this).start()

上面的脚本完美运行。但是,当我将行 print("test") 更改为 keyboard.write("test") 时。脚本中断。

import keyboard
import threading

def spam_this():
    status = 0
    while True:
        if keyboard.is_pressed("F9") and status == 0:
            status = 1
            event.wait(1)
        if keyboard.is_pressed("F9") and status == 1:
            status = 0
            event.wait(1)
        while status == 1:
            if keyboard.is_pressed("F9") and status == 1:
                status = 0
                event.wait(1)
            keyboard.write("test")

event = threading.Event()
threading.Thread(target=spam_this).start()

具有keyboard.write()功能的这个版本的脚本可以用隐含的切换键“F9”启动,但是当我尝试再次按下“F9”来关闭开关时,它不会停止就像它自己的 print("test") 版本。

注意:我不知道如何在标题中表达这个问题。我使用术语“阻塞”是因为效果类似于 time.sleep() 等阻塞方法在尝试创建带有切换的 while True: 循环时所做的事情。

keyboard 可能 运行 也拥有 thread 中的代码,并且两个线程之间可能存在冲突。 Python 一次只使用一个线程 GIL 到 运行 - 所以当你的线程 运行ning 时它可能会阻塞应该在屏幕上写文本的线程。如果我在 write() 之后使用 even.wait(0.1) 代码效果会更好,因此 Python 可以切换线程并在屏幕上发送文本。如果你使用更长的值那么它也可以工作但是如果你按键非常快那么它可能仍然 运行 writewait 并且它无法检查 is_pressed("F9") 并且不会停止它 - 如果你在 write

之后使用 event.wait(1) 你应该会看到它

还有其他问题 - 它可能会在按下 key 后写最后一个文本,它应该使用 break 退出 while 并跳过 write

import threading
import keyboard

def spam_this():
    status = 0
    while True:
        if keyboard.is_pressed("F9") and status == 0:
            status = 1
            event.wait(1)
        if keyboard.is_pressed("F9") and status == 1:
            status = 0
            event.wait(1)
        while status == 1:
            if keyboard.is_pressed("F9") and status == 1:
                print('stop')
                status = 0
                event.wait(1)
                break
            keyboard.write("test")
            event.wait(0.1)

event = threading.Event()
threading.Thread(target=spam_this).start()

但我会减少到

import threading
import keyboard

def spam_this():
    print('start spam_this')
    status = False
    while True:
        if keyboard.is_pressed("F9"):
            status = not status
            print('status:', status)
            event.wait(0.1)
        if status is True:
            keyboard.write("test")
            event.wait(0.1)

event = threading.Event()
threading.Thread(target=spam_this).start()

但它还有另一个问题——如果你按住按键的时间更长,那么它会切换status很多次。我添加了 print('status:', status) 来显示它。

我想像这样使用 keyboard.add_hotkey

import threading
import keyboard

# global variable
status = False

def spam_this():
    print('start: spam_this')
    while True:
        if status is True:
            keyboard.write("test")
            event.wait(0.1)

def test():
    global status
    
    status = not status
    print('change:', status)
    event.wait(0.5)
    
event = threading.Event()
threading.Thread(target=spam_this).start()
    
keyboard.add_hotkey("F9", test)
keyboard.wait()

但是当您按住该键时,系统可能会再次开始发送相同的键。

我没有测试 keyboard.on_press_key() - 也许它会解决它。

文档:keyboard

import threading
import keyboard

# global variable
status = False

def spam_this():
    print('start: spam_this')
    while True:
        if status is True:
            print("test")
            keyboard.write("test")
            event.wait(0.1)
        
            
def test(event=None):
    global status
    status = not status
    print('change:', status)

event = threading.Event()
keyboard.add_hotkey("F9", test)
#alternatively:
#keyboard.on_key_press("F9", test)
threading.Thread(target=spam_this).start()

对@furas 的回答进行了一些修改。

  1. test函数添加了一个参数event=None,否则keyboard.on_press_key会将F9按键事件传递给测试函数,结果为 typeError.

  2. test函数中,由于错误删除了event.wait(),经测试,keyboard.on_press_keykeyboard.add_hotkey函数确实有内置延迟,所以只要 F9 键没有被按住,内置延迟就足够了。

  3. 无论使用on_key_press还是add_hotkey,都应该在调用所需的“热键方法”后初始化线程。否则线程将阻塞 python 脚本的主循环。(这部分我无法解释为什么,这只是导致这个结论的反复试验。)

  4. 删除了 keyboard.wait(),因为没有使用此功能。

数字 1 和数字 3 是最重要的变化 (对于不想通读次要部分的人)