Am trying to create a hotkey to stop my script, this is my code so far.
import time
import keyboard
running = True
def stop(event):
global running
running = False
print("stop")
# press ctrl+esc to stop the script
keyboard.add_hotkey("ctrl+esc", lambda: stop)
while running:
time.sleep(2)
print("Hello")
time.sleep(2)
add_hotkey expects a callback as the second argument, so you must pass it the stop function, on the other hand, when the callback is invoked, no event is passed.
A better solution than using a boolean variable is to use threading.Event since this is thread-safe since the callback is invoked in a secondary thread.
import threading
import time
import keyboard
event = threading.Event()
def stop():
event.set()
print("stop")
keyboard.add_hotkey("ctrl+esc", stop)
while not event.is_set():
time.sleep(2)
print("Hello")
time.sleep(2)
Related
The following code works perfectly, the loop can be stop by pressing esc:
import time
import keyboard
run = 1
def end():
global run
run = 0
print(run)
def do_stuff():
while run:
print('running')
time.sleep(0.5)
keyboard.add_hotkey('esc', end)
do_stuff()
But if I start this loop with another add_hotkey, I cannot stop it with esc anymore.
import time
import keyboard
run = 1
def end():
global run
run = 0
print(run)
def do_stuff():
while run:
print('running')
time.sleep(0.5)
keyboard.add_hotkey('esc', end)
# do_stuff()
keyboard.add_hotkey('enter', do_stuff)
keyboard.wait()
What should I do to stop this loop? I tried to replace the while run: with while not keyboard.is_pressed('esc'):. It can stop the loop if I hold the esc for a while. But it doesn't seem like a good solution.
=======================
updates:
the following works:
import keyboard
import threading
run = 1
def end():
global run
run = 0
print(run)
def do_stuff():
while run:
print('running')
time.sleep(0.5)
def new_do_stuff():
t = threading.Thread(target=do_stuff, name='LoopThread')
t.start()
keyboard.add_hotkey('esc', end)
keyboard.add_hotkey('enter', new_do_stuff)
keyboard.wait('esc')
Since in the second example you enter the do_stuff() loop through the hotkey and never leave the do_stuff() loop, the system is still captured in the hotkey command and is not listening for hotkeys anymore. You would have to find a way to leave the loop after the keyboard.add_hotkey('enter', do_stuff) command and enter it externally through another way, so the system listens for hotkey-entries again.
I'm not aware of the context you're using this in, but using some sort of a main-loop, that does nothing but wait for a flag to be set (it should be set when you get the hotkey interrupt) and then enters the do_stuff() loop seems like a way to solve it.
I have thrown together a quick threading test:
import threading
def test():
print "it don't work"
while True:
threading.Timer(1, test).start()
It runs test, but it doesn't wait. What's wrong?
In each loop iteration, you start a new thread. Therefore you will reach the limit of allowed thread and you will get an exception : can't start new thread.
while True:
threading.Timer(1, test).start()
You can add global flag and wait until the function is executed - You should use time.sleep to avoid busy waiting.
a = False
def test():
global a
print("hallo")
a = True
threading.Timer(10, test).start()
while not a:
time.sleep(1)
print('done')
I'm using Selenium Webdriver in my program in order to try and automate something. I am then parsing th resulting page, and checking for a specific element in the page. If the page doesn't have the specific element, then I use sched.scheduler to re-automate the task, by having the user click a button (in the Tkinter GUI). The button runs a function, which schedules a task for sched.scheduler, and has the task be sent to a function in which I created a new process from the multiprocessing module.
This is basically what it is:
import time
import sched
from multiprocessing import Process
#the function needs to run for the first time, then waits for user input if an error shows up
#if it's the second time around, the worker function runs the scheduler
global first_time_happening
first_time_happening = True
terminate = False
scheduler = sched.scheduler(time.time, time.sleep)
def worker():
#insert some working process here using selenium webdriver
print("Worker happened!")
global first_time_happening
if first_time_happening:
first_time_happening = False
elif not first_time_happening:
global relay_to_timer
relay_to_timer = scheduler.enter(5, 2, timer)
scheduler.run()
def process():
p = Process(target=worker)
#p.daemon = True
p.start()
def timer():
if not terminate:
global relay_to_process
relay_to_process = scheduler.enter(5, 2, process)
scheduler.run()
if terminate:
scheduler.cancel(relay_to_process)
scheduler.cancel(relay_to_timer)
def quit_button():
global terminate
terminate = True
if scheduler.empty:
print("The line is empty")
elif not scheduler.empty:
print("Something in the queue!")
while not scheduler.empty:
scheduler.cancel(relay_to_process)
scheduler.cancel(relay_to_timer)
worker()
#simulating where the GUI asks a question, person presses a button, and the button redirects them
#to function worker()
worker()
#simulating a user press the quit button
quit_button()
It keeps running even after I "hit" quit (or call the quit function in this case). I keep getting the queue is empty, but I'm not sure why it isn't working? Any help is appreciated, thanks!!
The scheduler keeps running even with an empty queue just in case somebody (presumably another thread) entered something again. I believe the way to make it end is to raise an exception (whether from the action or delay function) -- .run will propagate it and you can catch it.
To wit...
class AllDoneException(Exception): pass
def worker():
#insert some working process here using selenium webdriver
print("Worker happened!")
global first_time_happening
if first_time_happening:
first_time_happening = False
elif not first_time_happening:
global relay_to_timer
relay_to_timer = scheduler.enter(5, 2, timer)
try:
scheduler.run()
except AllDoneException:
pass
and in function timer
if terminate:
raise AllDoneException
I have a timer function which I am calling it in another function like this
import time
import threading
def f():
while(True):
print "hello"
time.sleep(5)
def execute():
t = threading.Timer(5,f)
t.start()
command = ''
while command != 'exit':
command = raw_input()
if command == 'exit':
t.cancel()
Even if after entering "exit" command, the function is printing "hello"
I am not able to figure out Whats wrong with the code
class threading.Timer - cancel() - Doc-Link
Stop the timer, and cancel the execution of the timer’s action. This will only work if the timer is still in its waiting stage.
A very simple Version of what you are trying to accomplish could look like this.
import threading
_f_got_killed = threading.Event()
def f():
while(True):
print "hello"
_f_got_killed.wait(5)
if _f_got_killed.is_set():
break
def execute():
t = threading.Timer(5,f)
t.start()
command = ''
while command != 'exit':
command = raw_input()
if command == 'exit':
_f_got_killed.set()
t.cancel()
execute()
For forcefully killing a thread look at this:
Is there any way to kill a Thread in Python?
You are using cancel wrong. In http://docs.python.org/2/library/threading.html, it states: "Timers are started, as with threads, by calling their start() method. The timer can be stopped (before its action has begun) by calling the cancel() method. The interval the timer will wait before executing its action may not be exactly the same as the interval specified by the user."
In your code, if you try to use cancel after the timed thread has already begun its execution (it will in 5 seconds), cancel accomplishes nothing. The thread will remain in the while loop in f forever until you give it some sort of forced interrupt. So typing "exit" in the first 5 seconds after you run execute works. It will successfully stop the timer before the thread even begins. But after your timer stops and your thread starts executing the code in f, there will be no way to stop it through cancel.
I have a python application in which a function runs in a recursive loop and prints updated info to the terminal with each cycle around the loop, all is good until I try to stop this recursion.
It does not stop until the terminal window is closed or the application is killed (control-c is pressed) however I am not satisfied with that method.
I have a function which will stop the loop and exit the program it just never has a chance to get called in the loop, so I wish to assign it to a key so that when it is pressed it will be called.
What is the simplest method to assign one function to one or many keys?
You can intercept the ctrl+c signal and call your own function at that time rather than
exiting.
import signal
import sys
def exit_func(signal, frame):
'''Exit function to be called when the user presses ctrl+c.
Replace this with whatever you want to do to break out of the loop.
'''
print("Exiting")
sys.exit(0) # remove this if you do not want to exit here
# register your exit function to handle the ctrl+c signal
signal.signal(signal.SIGINT, exit_func)
#loop forever
while True:
...
You should replace sys.exit(0) with something more useful to you. You could raise an exception and that except on it outside the loop body (or just finally) to perform your cleanup actions.
import keyboard
import sys
from time import sleep
def kb():
while True:
if keyboard.is_pressed("a"):
print("A key was pressed")
sys.exit(0)
def main():
kb()
if __name__ == "__main__":
main()
Here is some code
import keyboard
import sys
def kb():
while True:
#your code here
if keyboard.is_pressed("a"): #replace with your key
print("Key interrupt detected")
#cleanup here
sys.exit()
#or here
if __name__ == "__main__":
kb()
This program checks if you have pressed the key "A" every cycle. If you have, it exits.
import keyboard
def mywait():
keyboard.read_key()
def my_function():
print("hello")
def my_exit():
quit()
keyboard.add_hotkey('h', my_function)
keyboard.add_hotkey('esc', my_exit)
while True:
mywait()