How to stop a program when a key is pressed in python? - 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()

Related

Trouble with pynput

I am trying to make a program that prints out a sentence with pynput by only pressing one key, however if that sentence contains that particular key it just starts typing that sentence infinite times. I would like to find a way to ignore the inputs from pynput, but not from my keyboard. Thanks in advance.
code:
from pynput.keyboard import Key, Listener, Controller
kb = Controller()
def on_press(key):
if not 'char' in dir(key):
return
if key.char == "s":
kb.press(Key.backspace) # deletes "s"
kb.release(Key.backspace)
kb.type("sample")
return
def on_release(key):
if key == Key.esc:
return False
with Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
output: samplsamplsamplsampl....
A simple solution would be to introduce a global variable as a locking mechanism:
from pynput.keyboard import Key, Listener, Controller
kb = Controller()
lock = False
def on_press(key):
global lock
if not hasattr(key, 'char'):
return
if lock:
return
if key.char == "s":
lock = True
kb.press(Key.backspace) # deletes "s"
kb.release(Key.backspace)
kb.type("sample")
lock = False
return
def on_release(key):
if key == Key.esc:
return False
with Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
This way you avoid responding to keypresses while you are in the process of typing keys.

Stop While Loop mid execution in 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()

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 pyautogui working together with pynput

The subject is next, I´m doing some siple clickers with pyautogui but it lacks of control. Basically I want to be able to start and stop different scripts based on pyautogui. My idea was to combine the Listener function from pynput, but it doesn´t work properly . It starts when I press the assigned key, but I cannot stop it, why?
Here is some simle code:
from pynput.keyboard import Key, Controller, Listener
import time
import pyautogui as pg
pg.FAILSAFE = True
kb = Controller()
time.sleep(1)
def on_press(key):
if key == Key.space:
pg.position(500, 500)
x = 20
while key is not Key.enter:
pg.moveRel(x, 0, duration=0.2)
time.sleep(1)
with Listener(on_press=on_press) as listener:
listener.join()
I also have tried this loop:
while True:
if key==Key.enter:
pg.moveRel(x, 0, duration=0.2)
else:
return(False)
time.sleep(1)
but nothing works.
UPD:
Maybe someone can suggest me another module with controlling features, which can be good for clicker?
It's failing to stop because you are in a infinite loop when you do this:
while key is not Key.enter:
due to the fact that your on_press can't be called again and therefore the variable key will never change.
from pynput.keyboard import Key, Controller, Listener
import time
import pyautogui as pg
import threading
pg.FAILSAFE = True
kb = Controller()
time.sleep(1)
threadExitFlag = threading.Event()
threadVar = None
def mouse_move_thread(threadExitFlag):
pg.position(500, 500)
x = 20
while not threadExitFlag.is_set():
pg.moveRel(x, 0, duration=0.2)
time.sleep(1)
def on_press(key):
global threadExitFlag
if key == Key.space:
threadVar = threading.Thread(target=mouse_move_thread, args=[threadExitFlag]).start()
if key == Key.enter:
threadExitFlag.set()
#Turns this macro back on
elif key == Key.esc:
if threadExitFlag.is_set():
threadExitFlag.clear()
with Listener(on_press=on_press) as listener:
listener.join()
To use this, you press space to start your mouse movement, then you can stop it by pressing enter. After this, you need to press esc key to reset the event that stops it which means to do this macro twice in a row you need to press:
space (start the macro)
enter (stop/kill the macro)
esc (reset flag, if you press space after this you can start the macro again)
I've tested it and it works 100%.

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