简体   繁体   English

切换键运行线程然后再次按下时停止线程?

[英]Toggle key to run a thread then stop the thread when pressed again?

I'm currently working on a program and setup a toggle key that starts a thread when it's first clicked, and I want it to stop the thread when the key is pressed again.我目前正在开发一个程序并设置一个切换键,该键在第一次单击时启动线程,并且我希望它在再次按下该键时停止线程。 Currently I've tried this piece of code, but it came out with an error.目前我已经尝试了这段代码,但它出现了错误。

def on_press(key):
    try: k = key.char
    except: k = key.name
    if key == (KeyCode(char='e')):
        print("Key Pressed")
        clicker = threading.Thread(target=run)
        if running == False:
            print("Starting")
            clicker.start()
        else:
            print("Stopping")
            clicker.join()

lis = keyboard.Listener(on_press=on_press)
lis.start() # start to listen on a separate thread
lis.join() # no this if main thread is polling self.keys

The error I get is:我得到的错误是:

raise RuntimeError("cannot join thread before it is started")
  RuntimeError: cannot join thread before it is started**

You should check what you have in variable running - ie.您应该检查变量running中的内容 - 即。 print(running) - because it decides when to execute code in if/else and it runs click.join() directly after creating Thread before you run clicker.start()` print(running) - because it decides when to execute code in and it runs directly after creating Thread before you run clicker.start()`

As for me you should create therad inside if running == False至于我, if running == False ,你应该在里面创建 therad

You should also use global to assing values to external variable - now you create local clicker which will be removed when it exits on_press and you will not have access to this thread.您还应该使用global将值分配给外部变量 - 现在您创建本地点击器,当它退出clicker时将被on_press ,您将无法访问该线程。 You should also change value running when you started thead`您还应该在启动 thead 时更改running值`


BTW:顺便提一句:

For True/False you should rather use is instead of ==对于True/False ,您应该使用is而不是==

You can also use more readable if not running instead of if running is False: if not running而不是if running is False:


I didn't test it我没有测试它

# from ... import ...

# --- functions ---

def on_press(key):
    global running   # inform function that it has to assign value to external variable 
    global clicker
    
    try:    # PEP8: don't put it in one line - it make code unreadable for human 
        k = key.char
    except: 
        k = key.name
        
    if key == KeyCode(char='e'):
        print("Key Pressed")
        
        if not running: # the same as `if running == False:` 
            print("Starting")
            clicker = threading.Thread(target=run)
            clicker.start()
            running = True
        else:
            print("Stopping")
            clicker.join()
            running = False

# --- main ---

running = False  # default value at start

lis = keyboard.Listener(on_press=on_press)
lis.start()
lis.join()

if you don't use running in other places in code then you could use clicker = None to control if thread is running.如果您不在代码中的其他地方使用running ,那么您可以使用clicker = None来控制线程是否正在运行。

# from ... import ...

# --- functions ---

def on_press(key):
    global clicker   # inform function that it has to assign value to external variable 
    
    try:    # PEP8: don't put it in one line - it make code unreadable for human 
        k = key.char
    except: 
        k = key.name
        
    if key == KeyCode(char='e'):
        print("Key Pressed")
        
        if not clicker:
            print("Starting")
            clicker = threading.Thread(target=run)
            clicker.start()
        else:
            print("Stopping")
            clicker.join()
            clicker = None

# --- main ---

clicker = None  # default value at start

lis = keyboard.Listener(on_press=on_press)
lis.start()
lis.join()

BTW:顺便提一句:

If thread is running then join() waits for its end - but it doesn't kill it so you may wait forever.如果线程正在运行,则join()等待它的结束 - 但它不会杀死它,所以你可能会永远等待。

You should use value running also inside run() to stop it.您应该在run()中使用running值来停止它。

def run():

    while running:
        #... code ...

or或者

def run():

    while True:
        #... code ...
   
        if not running:
             return

        #... code ...

and then you have to set running before start() and join()然后你必须在start()join()之前设置running

    if not running:
        print("Starting")
        clicker = threading.Thread(target=run)
        running = True  # it has to be before `start()` 
        clicker.start()
    else:
        print("Stopping")
        running = False  # it has to be before `join()` 
        clicker.join()

EDIT:编辑:

Minimal working code - I tested it.最少的工作代码 - 我测试了它。

from pynput import keyboard
import datetime
import time
import threading

# --- functions ---

def run():

    print('Running thread')
    while running:
        print(datetime.datetime.now())
        time.sleep(1)
    print('Exiting thread')

def on_press(key):
    global running   # inform function that it has to assign value to external variable
    global clicker

    try:    # PEP8: don't put it in one line - it make code unreadable for human
        k = key.char
    except:
        k = key.name

    if key == keyboard.KeyCode(char='e'):
        print("Key Pressed")

        if not running: # the same as `if running == False:`
            print("Starting thread")
            clicker = threading.Thread(target=run)
            running = True  # it has to be before `start()`
            clicker.start()
        else:
            print("Stopping thread")
            running = False  # it has to be before `join()`
            clicker.join()

    # press `q` to exit
    if key == keyboard.KeyCode(char='q'):
        return False

# --- main ---

running = False  # default value at start

try:
    print("Starting program")
    print("- press E to start/stop thread")
    print("- press Q to exit")
    print("- press Ctrl+C to exit")
    
    lis = keyboard.Listener(on_press=on_press)
    lis.start()
    print("Listening ...")
    
    lis.join()
    print("Exiting program")
except KeyboardInterrupt:
    print("Stoped by Ctrl+C")
else:
    print("Stoped by Q")
finally:
    if running:
        running = False
        clicker.join()

If the key pressed is the same as the start_stop_key, stop clicking if the running flag is set to true in the thread otherwise start it.如果按下的键与 start_stop_key 相同,则在线程中将运行标志设置为 true 时停止单击,否则启动它。 If the key pressed is the exit key, call the exit method in the thread and stop the listener如果按下的键是退出键,则调用线程中的exit方法并停止监听

start_stop_key = KeyCode(char='s')
exit_key = KeyCode(char='e')
...............
def on_press(key):
if key == start_stop_key:
    if click_thread.running:
        click_thread.stop_clicking()
    else:
        click_thread.start_clicking()
elif key == exit_key:
    click_thread.exit()
    listener.stop()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM