Stop While Loop mid execution in python - python

I am trying to stop a while loop mid execution, if I reverse the value of 'runWhile' mid execution it simply waits until it's over.
Problem: I need it to stop immediately whenever I press f10 on the keyboard.
from pynput import keyboard
import threading
import datetime, time
def exec():
while runWhile:
print("There I go")
time.sleep(3)
print("I overtaken")
time.sleep(3)
print("You cant stop me until I finish")
def on_press(key):
global runWhile # inform function to assign (`=`) to external/global `running` instead of creating local `running`
if key == keyboard.Key.f5:
runWhile = True
t = threading.Thread(target=exec)
t.start()
if key == keyboard.Key.f10:
# to stop loop in thread
print("loading STOPPED", datetime.datetime.now()) #, end='\r')
runWhile = False
if key == keyboard.Key.f11:
# stop listener
print("listener TERMINATED", datetime.datetime.now()) #, end='\r')
return False
#--- main ---
with keyboard.Listener(on_press=on_press) as listener:
listener.join()
Im using pynput, docs here
based on #furas code

Here is a solution I made. I created my own delay function as follows:
def delay(amount): #delay time in seconds
for i in range(int(amount*60)):
time.sleep(0.01)
if runWhile == False:
return True
break
You would replace your delay(3) with
if delay(3):
break
This would wait 3 seconds, however if during that time, runWhile was false, it will break out of the loop. Your code would look like this:
from pynput import keyboard
import threading
import datetime, time
def delay(amount): #delay time in seconds
for i in range(int(amount*60)):
time.sleep(0.01)
if runWhile == False:
return True
break
def exec():
while runWhile:
print("There I go")
if delay(3):
break
print("I overtaken")
if delay(3):
break
print("You cant stop me until I finish")
def on_press(key):
global runWhile # inform function to assign (`=`) to external/global `running` instead of creating local `running`
if key == keyboard.Key.f5:
runWhile = True
t = threading.Thread(target=exec)
t.start()
if key == keyboard.Key.f10:
# to stop loop in thread
print("loading STOPPED", datetime.datetime.now()) #, end='\r')
runWhile = False
if key == keyboard.Key.f11:
# stop listener
print("listener TERMINATED", datetime.datetime.now()) #, end='\r')
return False
#--- main ---
with keyboard.Listener(on_press=on_press) as listener:
listener.join()

Related

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. print(running)- because it decides when to execute code inif/elseand it runsclick.join()directly after creatingThreadbefore you runclicker.start()`
As for me you should create therad inside if running == False
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. You should also change value running when you started thead`
BTW:
For True/False you should rather use is instead of ==
You can also use more readable if not running instead of 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.
# 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.
You should use value running also inside run() to stop it.
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()
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. If the key pressed is the exit key, call the exit method in the thread and stop the listener
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()

Keyboard input (via pynput) and threads in python

I'm trying to make a kind of text-based game in Python 3. For the game I will need to listen for keyboard input, in particular measuring how long a key is held down, while printing things to the screen. I'm trying to start by making a working minimal example.
First, the following code, using pynput, appears to successfully measures the length time for which the user holds down a key:
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()
Now what I'd like to do is, only while a key is pressed down by the user, print a text-based "animation" to the screen. In the following example my "animation" is simply printing "*" every half second.
So far I've tried to have the "animation" handled by a second thread but I am a total novice when it comes to multithreading. The following code will start the animation at the correct time but won't stop it.
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()
Here's an approach (following #furas's comment) where the animation is coded after the with statement, however I cannot get this to work for me:
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()
Ultimately I want to be able to do this with more complex animations. For example
def mysquare(delay):
print("#"*10)
time.sleep(delay)
for i in range(8):
print("#" + " "*8 + "#")
time.sleep(delay)
print("#"*10)
What's the right way to approach this? Many thanks!
Listener already uses thread so there is no need to run animation in separated thread. You can run it in current tread in
with keyboard.Listener(on_press = on_press, on_release=on_release, suppress=True) as listener:
#... your code ...
listener.join()
or without with ... as ...
listener = keyboard.Listener(on_press = on_press, on_release=on_release, suppress=True)
listener.start()
#... your code ...
#listener.wait()
listener.join()
You can run there even long runing code - ie. endless while loop which will check if variable animate is True and write new *.
I had to add sys.stdout.flush() on my Linux to see * on screen.
My version:
It runs animation all time when you press any button but there is also code with variable counter to limit animation to 6 moves. If you press new key when it runs animation then it reset this counter and animation will longer.
This loop has to run all time to check if there is new animation - you can't finish this loop when animation is finished
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 - executing two functions in parallel

I'm very new to python and I have following problem.
I'm trying to divide a variable (counta) by 3 and get the result every 10 seconds. At the same time I want to add 1 to the counter every time I press 'a'. The problem is that I'm using a method where I can only add 1 every 10 seconds. Please can you advise what could be changed so that I could add 1 whenever I like and i still get the current result (counta/3). Thank you in advance for your help. This is my code so far:
from pynput.keyboard import Listener
import sched, time
counta = 0
Timer = 0
def on_press(key):
if key.char == 'a':
#print("")
global counta
counta += 1
#print("Aktuell" + str(counta))
elif key.char == 'p':
print(int(counta/3))
elif key.char == 's':
Stand(counta, Timer)
else:
print("Falsche Taste!")
print("a = counta")
def Stand(counta, Timer):
while Timer < 10:
print(str(counta/3))
time.sleep(1)
Timer += 1
with Listener(on_press=on_press) as listener:
listener.join()
You can use Listener in different way
listener = Listener(on_press=on_press)
listener.start()
while Timer < 10:
print(str(counta/3))
time.sleep(1)
Timer += 1
#listener.stop()
listener.join()
or using your function
listener = Listener(on_press=on_press)
listener.start()
Stand(counta, Timer)
#listener.stop()
listener.join()
BTW: class Listener already uses thread to listen keys - class Listener(threading.Thread):.
EDIT: I released that you can also use it this way
with Listener(on_press=on_press) as listener:
while Timer < 10:
print(str(counta/3))
time.sleep(1)
Timer += 1
#listener.stop()
listener.join()
or using your function
with Listener(on_press=on_press) as listener:
Stand(counta, Timer)
#listener.stop()
listener.join()
The easiest way is to use a Tread :
import treading # at begin of your file
thread = threading.Thread(target=Stand) # create thread with your function
thread.start() # launch your function
Note that print method can reduce time execution of your function, and print in thread won't be recommended.

How to stop a program when a key is pressed in python?

I have a program that is an endless loop that prints "program running" every 5 seconds and I want to stop it when I press the end key.
So I created a key listener that returns false if the end key is pressed. That should work if I won't have the endless loop. And I want it to work even when I'm in the endless loop.
Here's my code:
from pynput import keyboard
import time
def on_press(key):
print key
if key == keyboard.Key.end:
print 'end pressed'
return False
with keyboard.Listener(on_press=on_press) as listener:
while True:
print 'program running'
time.sleep(5)
listener.join()
from pynput import keyboard
import time
break_program = False
def on_press(key):
global break_program
print (key)
if key == keyboard.Key.end:
print ('end pressed')
break_program = True
return False
with keyboard.Listener(on_press=on_press) as listener:
while break_program == False:
print ('program running')
time.sleep(5)
listener.join()

Change the state of the condition variable for a while loop outside the while loop

I am currently trying to start and stop a while loop by a pressing a key (start) and stopping by releasing the key.
So something like this:
from pynput import keyboard
global condition
condition = False
def on_press(key):
global condition
if key == keyboard.Key.cmd_r:
print('pressed cmd_r'.format(key))
condition = True
else:
print('incorrect character {0}, press cmd_r'.format(key))
def on_release(key):
global condition
print('{0} released'.format(key))
if key == keyboard.Key.cmd_r:
condition = False
#keyboard.Listener.stop
#return False
with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
while condition==True:
print "Condition true"
I am not sure why this is not working?..
It should in my head?
The issue is that when you call listener.join() your code waits at this point for the thread to complete. But it never will complete because it's always listening! Instead you want to call listener.start() so that the thread runs in the background and you are free to do what you want.
Sharing variables between threads is not generally accepted, so here I make a modified listener class that associates the variable key_pressed to itself when a key is pressed, and None when it is released. You can then do what you want with this variable by checking it at any time in a separate loop by calling listener.key_pressed
from pynput import keyboard
import time
class MyListener(keyboard.Listener):
def __init__(self):
super(MyListener, self).__init__(self.on_press, self.on_release)
self.key_pressed = None
def on_press(self, key):
self.key_pressed = key
def on_release(self, key):
self.key_pressed = None
listener = MyListener()
listener.start()
while True:
time.sleep(0.1)
print listener.key_pressed
Note that if you don't include a delay with time.sleep as above, you will overload the buffer and lead to delays in the output. Just put a small delay if you want it fast, but not zero.
You may need something like main loop where you can include your special while loop to achieve this.
Update 1 - How? (wrong)
while True:
# main loop
while condition:
# your special loop
# do something...
time.sleep(0.1) # sleep 0.1 seconds
The "main loop" is an infinite loop and executes included instructions every 0.1 second. Hence you provide the ability to keep checking the condition. If the condition == True your "special loop" is going to execute and it stops when the condition == False, and then the "main loop" continues its execution.
Update 2 - The implementation (correct)
Ok, I've run the code and I see that the "main loop" solution isn't right here. For now, I have quick, tested solution, based on multithreading:
import time
import threading
from pynput import keyboard
condition = False
def my_special_task():
global condition
while condition:
print("condition = %s" % condition)
time.sleep(0.1)
def on_press(key):
global condition
if key == keyboard.Key.ctrl_r:
print('Pressed ctrl_r')
condition = True
thread = threading.Thread(target=my_special_task)
thread.start()
else:
print("Incorrect KEY: %s, press ctrl_r instead" % key)
def on_release(key):
global condition
print("%s released" % key)
if key == keyboard.Key.ctrl_r:
condition = False
with keyboard.Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()

Categories

Resources