How to stop/resume a loop without having to terminate the terminal? - python

This is a small macro for a game.
I would like to stop the program (NOT close it!) with the F9 key and then when I click F9 again, it will resume. If possible without having to exit the game.
F9 - Start/Stop the program
import pyautogui
import time
import keyboard
print("Press F10 to stop and F9 to start")
while keyboard.is_pressed('f10') == False:
if keyboard.is_pressed('f9') == True:
time.sleep(3)
pyautogui.press('w')
time.sleep(1)
pyautogui.press('s')
time.sleep(1)
pyautogui.press('w')
time.sleep(1)
pyautogui.press('s')
time.sleep(1)
pyautogui.press('a')
time.sleep(4)
pyautogui.press('t')

you can use pynput to monitor the keyboard
from time import sleep
from threading import Thread
from pynput.keyboard import Key, Listener, Controller
def on_press(key):
if key == Key.f9: # start and stop the macro
flags['running'] = not flags['running']
elif key == Key.f10: # closes the program
flags['exit'] = True
return False # stop the listener
def macro(flags):
keyboard = Controller()
while not flags['exit']:
if flags['running']: # your macro here
sleep(3)
keyboard.type('w')
# etc...
flags = {'running' : True, 'exit' : False}
macro_thread = Thread(target=macro, args=(flags,))
macro_thread.start()
# Collect events until released
with Listener(on_press=on_press) as listener:
listener.join()
macro_thread.join()
if you want the code to stop immediately you need to change the sleeps inside the macro function:
from time import time
def check_exit(s, flags):
""" checks if the exit flag becomes true for s seconds """
start = time()
while time() < (start + s):
if flags['exit']:
return True
return False
def macro(flags):
keyboard = Controller()
while not flags['exit']:
if flags['running']:
if check_exit(3, flags): return
keyboard.type('w')
# etc...

Related

How to get out of a while loop with a specific key

I am trying to make an auto clicker but when i try to make my code exit it doesnt
here is my code
import mouse
import keyboard
import time
import os
os.system('cls')
def Config():
print("Click every")
hour = int(input("Hour: "))
minute = int(input("Minute: "))
second = int(input("Second: "))
total_time = hour*3600 + minute*60 + second
print("f6 to start and f10 to stop")
keyboard.wait('f6')
while True:
time.sleep(total_time)
mouse.click()
#def Fastest():
print(" Auto clicker!!!")
print(" By ze")
print("-------------------------")
print("Auto click on desired time or Fastest?")
choose = int(input("""
1. Config (No milliseconds)
2. Fastest
"""))
if choose == 1:
Config()
# elif choose == 2:
# Fastest()
#TODO:
# use mouse.click
# make it click with time
# make function fastest start with f1 and stops with f2
# create back up file
i tried an if statement with keyboard.is_pressed('key') thinking it would work but it doesnt my results are that the code exits (if key is pressed then exit)
You need to check if the key is pressed in your infinite loop. If it is pressed, you need to exit
while True:
time.sleep(total_time)
mouse.click()
if keyboard.is_pressed("f10"):
break
But this waits for the sleep function , so you'll need to hold f10 for total_time seconds.
You should use a loop to check for the key, rather than sleeping
import datetime
...
clicking = True
while clicking:
mouse.click()
s = datetime.datetime.now()
while ((datetime.datetime.now()-s).total_seconds() < total_time):
# This runs while the difference in time since you started the loop is less than the time you want to wait
if keyboard.is_pressed("f10"):
clicking = False
break
Use one thread to handle user input and another thread to do the clicking:
from threading import Thread
from msvcrt import getwch
import mouse
done = False
def auto_click():
print("Press \"q\" to quit")
global done
while True:
if getwch() == "q":
done = True
break
Thread( target = auto_click, daemon = True ).start()
while not done: # you can add whatever logic you want including a time gate here
mouse.click()

Python Key Logger with Duration and Multiple Keys

I am trying to write a key logger in python that determines the length of time a key is pressed - key pressed through release - while also detecting if another key is pressed. For example, someone presses w for 4 seconds. While holding W, they also press S and hold S for X amount of time. I think this may need to be multithreaded but I am not sure how to handle this in python.
This is code I found that tracks the length of a key press. It just needs to be threaded, I think.
import time
def callb(key): #what to do on key-release
ti1 = str(time.time() - t)[0:5] #converting float to str, slicing the float
print("The key",key," is pressed for",ti1,'seconds')
return False #stop detecting more key-releases
def callb1(key): #what to do on key-press
return False #stop detecting more key-presses
with keyboard.Listener(on_press = callb1) as listener1: #setting code for listening key-press
listener1.join()
t = time.time() #reading time in sec
with keyboard.Listener(on_release = callb) as listener: #setting code for listening key-release
listener.join()
Use hook method to get all events
import keyboard
HISTORY = {}
def key_recording(e):
if e.name not in HISTORY and e.event_type == keyboard.KEY_DOWN:
HISTORY[e.name] = e.time
elif e.name in HISTORY and e.event_type == keyboard.KEY_UP:
print(f"The key {e.name} is pressed for {round(e.time - HISTORY.pop(e.name), 3)} seconds")
remove = keyboard.hook(key_recording)
try:
input("Press any key to exit")
except KeyboardInterrupt:
pass
finally:
remove()

How to do something till an input is detected in python3?

I want to execute a piece of code till the user enters an input(detects a random keypress), how do I do that in Python 3.x?
Here is the pseudo-code:
while input == False:
print(x)
You can do it like this:
try:
while True:
print("Running")
except KeyboardInterrupt:
print("User pressed CTRL+c. Program terminated.")
The user just need to press Control+c.
Python provide the built-in exception KeyboardInterrupt to handle this.
To do it with any random key-press with pynput
import threading
from pynput.keyboard import Key, Listener
class MyClass():
def __init__(self) -> None:
self.user_press = False
def RandomPress(self, key):
self.user_press = True
def MainProgram(self):
while self.user_press == False:
print("Running")
print("Key pressed, program stop.")
def Run(self):
t1 = threading.Thread(target=self.MainProgram)
t1.start()
# Collect events until released
with Listener(on_press=self.RandomPress) as listener:
listener.join()
MyClass().Run()
If you want to interact with users, you may follow the below way:
flag = input("please enter yes or no?")
if flag == "no":
print(x)

Unused variable in Python

This is my first time to create a python program to take screenshot every two seconds. The problem is that I don't know how to break the while loop. I wrote while sscount is not zero, keep screenshotting. Then when a user press ESC button, set sscount to 0. This should stop the while loop but I get the warning, "Unused variable 'sscount'" and it doesn't stop the while loop either.
Could anyone help me? Thanks.
import pynput
import pyautogui
import time
from pynput.keyboard import Key, Listener
count = 0
def on_release(key):
if key == Key.esc:
sscount = 0 #this is where the warning comes.
return False
with Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
while sscount != 0:
pyautogui.screenshot('/users/aliha/desktop/screenshot/image'+str(sscount)+'.png')
sscount += 1
time.sleep(2.0)
Below a snippet code to enhance.
The main program run while is_running is True
ctrl+c event set is_running to False
screenshot is took if the elasped time is upper than 2 sec between 2 <escape> key pressed
output path is not hardcoded
global variable is used to share state
import time
from os.path import expanduser
from pathlib import Path
import datetime
from pynput.keyboard import Key, Listener, Controller
keyboard = Controller()
is_running = True
sscount = 0
previous_date = None
screenshot_dir = None
def on_press(key: Key):
global previous_date, sscount
have_to_take_screenshot = False
current_date = datetime.datetime.now()
if previous_date is None:
previous_date = current_date
have_to_take_screenshot = True
else:
elapsed_time = current_date - previous_date
if elapsed_time > datetime.timedelta(seconds=2):
previous_date = current_date
have_to_take_screenshot = True
else:
have_to_take_screenshot = False
print(have_to_take_screenshot)
if have_to_take_screenshot and key == Key.esc:
pyautogui.screenshot(f'{screenshot_dir}/image{sscount}.png')
sscount+= 1
def on_release(key: Key):
global screenshot_dir
should_continue = True
print(key)
if key == Key.esc:
is_running = False
should_continue = False
return should_continue
if __name__ == '__main__':
home_dir = expanduser('~/')
screenshot_dir = Path(f'{home_dir}/desktop/screenshot')
if not screenshot_dir.exists():
screenshot_dir.mkdir(parents=True, exist_ok=True)
while is_running:
try:
with Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
except KeyboardInterrupt:
is_running = False
In order to be able to use variable sscount globally and to manipulate the global variable within function on_release , you have to declare sscount as global variable in the function using global sscount. Complete result:
def on_release(key):
global sscount
if key == Key.esc:
sscount = 0 #this is where the warning comes.
return False
One way to fix this is to raise a StopIteration and make the loop infinite.
import pynput
import pyautogui
import time
from pynput.keyboard import Key, Listener
def on_release(key):
if key == Key.esc:
raise StopIteration
with Listener(on_press=on_press, on_release=on_release) as listener:
listener.join()
while True:
pyautogui.screenshot('/users/aliha/desktop/screenshot/image'+str(sscount)+'.png')
time.sleep(2.0)

How to kill a while loop with a keystroke?

I am reading serial data and writing to a csv file using a while loop. I want the user to be able to kill the while loop once they feel they have collected enough data.
while True:
#do a bunch of serial stuff
#if the user presses the 'esc' or 'return' key:
break
I have done something like this using opencv, but it doesn't seem to be working in this application (and i really don't want to import opencv just for this function anyway)...
# Listen for ESC or ENTER key
c = cv.WaitKey(7) % 0x100
if c == 27 or c == 10:
break
So. How can I let the user break out of the loop?
Also, I don't want to use keyboard interrupt, because the script needs to continue to run after the while loop is terminated.
The easiest way is to just interrupt it with the usual Ctrl-C (SIGINT).
try:
while True:
do_something()
except KeyboardInterrupt:
pass
Since Ctrl-C causes KeyboardInterrupt to be raised, just catch it outside the loop and ignore it.
There is a solution that requires no non-standard modules and is 100% transportable:
import _thread
def input_thread(a_list):
raw_input() # use input() in Python3
a_list.append(True)
def do_stuff():
a_list = []
_thread.start_new_thread(input_thread, (a_list,))
while not a_list:
stuff()
the following code works for me. It requires openCV (import cv2).
The code is composed of an infinite loop that is continuously looking for a key pressed. In this case, when the 'q' key is pressed, the program ends. Other keys can be pressed (in this example 'b' or 'k') to perform different actions such as change a variable value or execute a function.
import cv2
while True:
k = cv2.waitKey(1) & 0xFF
# press 'q' to exit
if k == ord('q'):
break
elif k == ord('b'):
# change a variable / do something ...
elif k == ord('k'):
# change a variable / do something ...
For Python 3.7, I copied and changed the very nice answer by user297171 so it works in all scenarios in Python 3.7 that I tested.
import threading as th
keep_going = True
def key_capture_thread():
global keep_going
input()
keep_going = False
def do_stuff():
th.Thread(target=key_capture_thread, args=(), name='key_capture_thread', daemon=True).start()
while keep_going:
print('still going...')
do_stuff()
pip install keyboard
import keyboard
while True:
# do something
if keyboard.is_pressed("q"):
print("q pressed, ending loop")
break
Here is a solution that worked for me. Got some ideas from posts here and elsewhere. Loop won't end until defined key (abortKey) is pressed. The loop stops as fast as possible and does not try to run to next iteration.
from pynput import keyboard
from threading import Thread
from time import sleep
def on_press(key, abortKey='esc'):
try:
k = key.char # single-char keys
except:
k = key.name # other keys
print('pressed %s' % (k))
if k == abortKey:
print('end loop ...')
return False # stop listener
def loop_fun():
while True:
print('sleeping')
sleep(5)
if __name__ == '__main__':
abortKey = 't'
listener = keyboard.Listener(on_press=on_press, abortKey=abortKey)
listener.start() # start to listen on a separate thread
# start thread with loop
Thread(target=loop_fun, args=(), name='loop_fun', daemon=True).start()
listener.join() # wait for abortKey
pyHook might help. http://sourceforge.net/apps/mediawiki/pyhook/index.php?title=PyHook_Tutorial#tocpyHook%5FTutorial4
See keyboard hooks; this is more generalized-- if you want specific keyboard interactions and not just using KeyboardInterrupt.
Also, in general (depending on your use) I think having the Ctrl-C option still available to kill your script makes sense.
See also previous question: Detect in python which keys are pressed
There is always sys.exit().
The system library in Python's core library has an exit function which is super handy when prototyping.
The code would be along the lines of:
import sys
while True:
selection = raw_input("U: Create User\nQ: Quit")
if selection is "Q" or selection is "q":
print("Quitting")
sys.exit()
if selection is "U" or selection is "u":
print("User")
#do_something()
I modified the answer from rayzinnz to end the script with a specific key, in this case the escape key
import threading as th
import time
import keyboard
keep_going = True
def key_capture_thread():
global keep_going
a = keyboard.read_key()
if a== "esc":
keep_going = False
def do_stuff():
th.Thread(target=key_capture_thread, args=(), name='key_capture_thread', daemon=True).start()
i=0
while keep_going:
print('still going...')
time.sleep(1)
i=i+1
print (i)
print ("Schleife beendet")
do_stuff()
This is the solution I found with threads and standard libraries
Loop keeps going on until one key is pressed
Returns the key pressed as a single character string
Works in Python 2.7 and 3
import thread
import sys
def getch():
import termios
import sys, tty
def _getch():
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(fd)
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
return _getch()
def input_thread(char):
char.append(getch())
def do_stuff():
char = []
thread.start_new_thread(input_thread, (char,))
i = 0
while not char :
i += 1
print "i = " + str(i) + " char : " + str(char[0])
do_stuff()
From following this thread down the rabbit hole, I came to this, works on Win10 and Ubuntu 20.04. I wanted more than just killing the script, and to use specific keys, and it had to work in both MS and Linux..
import _thread
import time
import sys
import os
class _Getch:
"""Gets a single character from standard input. Does not echo to the screen."""
def __init__(self):
try:
self.impl = _GetchWindows()
except ImportError:
self.impl = _GetchUnix()
def __call__(self): return self.impl()
class _GetchUnix:
def __init__(self):
import tty, sys
def __call__(self):
import sys, tty, termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(sys.stdin.fileno())
ch = sys.stdin.read(1)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
class _GetchWindows:
def __init__(self):
import msvcrt
def __call__(self):
import msvcrt
msvcrt_char = msvcrt.getch()
return msvcrt_char.decode("utf-8")
def input_thread(key_press_list):
char = 'x'
while char != 'q': #dont keep doing this after trying to quit, or 'stty sane' wont work
time.sleep(0.05)
getch = _Getch()
char = getch.impl()
pprint("getch: "+ str(char))
key_press_list.append(char)
def quitScript():
pprint("QUITTING...")
time.sleep(0.2) #wait for the thread to die
os.system('stty sane')
sys.exit()
def pprint(string_to_print): #terminal is in raw mode so we need to append \r\n
print(string_to_print, end="\r\n")
def main():
key_press_list = []
_thread.start_new_thread(input_thread, (key_press_list,))
while True:
#do your things here
pprint("tick")
time.sleep(0.5)
if key_press_list == ['q']:
key_press_list.clear()
quitScript()
elif key_press_list == ['j']:
key_press_list.clear()
pprint("knock knock..")
elif key_press_list:
key_press_list.clear()
main()
This may be helpful
install pynput with --
pip install pynput
from pynput.keyboard import Key, Listener
def on_release(key):
if key == Key.esc:
# Stop listener
return False
# Collect events until released
while True:
with Listener(
on_release=on_release) as listener:
listener.join()
break
Here is a simple Windows solution that safely ends current iteration and then quits. I used it with a counter example that breaks the loop with 'Esc' key and quits. It uses kbhit() and getch() functions from msvcrt package. Time package is only called for easement reasons (to set time delay between events).
import msvcrt, time
print("Press 'Esc' to stop the loop...")
x = 0
while True:
x += 1
time.sleep(0.5)
print(x)
if msvcrt.kbhit():
if msvcrt.getch() == b'\x1b':
print("You have pressed Esc! See you!")
time.sleep(2)
break
kbhit() function returns True if a keypress is waiting to be read
getch() function reads a keypress and returns the resulting character as a byte string. It can be used with any key
b'\x1b' is the byte string character for the 'Esc' key.
Here another example using threading.Event, without the need for catching SIGINT (Ctrl+c).
As #Atcold has mentioned in a comment below the accepted answer, pressing Ctrl+c in the loop, may interrupt a long running operation and leave it in an undefined state. This can specially annoying, when that long running operation comes from a library that you are calling.
In the example below, the user needs to press q and then press Enter. If you want to capture the key stroke immediately, you need something like _Getch() from this answer.
import time
from threading import Thread, Event
def read_input(q_entered_event):
c = input()
if c == "q":
print("User entered q")
q_entered_event.set()
def do_long_running_stuff():
q_pressed_event = Event()
input_thread = Thread(target=read_input,
daemon=True,
args=(q_pressed_event,))
input_thread.start()
while True:
print("I am working ...")
time.sleep(1)
if q_pressed_event.is_set():
break
print("Process stopped by user.")
if __name__ == "__main__":
do_long_running_stuff()
from time import sleep
from threading import Thread
import threading
stop_flag = 0
def Wait_Char():
global stop_flag
v = input("Enter Char")
if(v == "z"):
stop_flag = 1
def h():
while(True):
print("Hello Feto")
time.sleep(1)
if(stop_flag == 1):
break
thread1 = Thread(target=Wait_Char)
thread2 = Thread(target=h)
thread1.start()
thread2.start()
print("threads finished...exiting")
This isn't the best way but it can do the job you want,
Running 2 Threads one waiting for the Key you want to stop the loop with
(Wait_Char Method)
and one for loop
(H Method)
And both see a global variable stop_flag which control the stoping process
Stop when I press z
from pynput import keyboard
def on_press(key):
if key == keyboard.Key.esc:
return False
i = 0
with keyboard.Listener(on_press=on_press) as listener:
# Your infinite loop
while listener.running:
print(i)
i=i+1
print("Done")
It works ...
import keyboard
while True:
print('please say yes')
if keyboard.is_pressed('y'):
break
print('i got u :) ')
print('i was trying to write you are a idiot ')
print(' :( ')
for enter use 'ENTER'

Categories