Interrupt python script with a specific key on Linux - python

I'm trying to have a loop which increments and prints a value. While it's running I would like to press a key (eg. space or shift) and have it print that the key was pressed. Below is example code of what I would like.
def space():
print 'You pressed space'
def shift():
print 'You pressed shift'
x = 0
while True:
print(x)
#if space is pressed
space()
#if shift is pressed
shift()
x = x + 1;
time.sleep(1)
EDIT: Here is an example output
0
1
2
You pressed shift
3
4
5
You pressed space
6
7
.
.
.

I can help you with modified answer form here:
https://stackoverflow.com/questions/11918999/key-listeners-in-python
and for only space and enter:
import contextlib
import sys
import termios
import time
#contextlib.contextmanager
def raw_mode(file):
old_attrs = termios.tcgetattr(file.fileno())
new_attrs = old_attrs[:]
new_attrs[3] = new_attrs[3] & ~(termios.ECHO | termios.ICANON)
try:
termios.tcsetattr(file.fileno(), termios.TCSADRAIN, new_attrs)
yield
finally:
termios.tcsetattr(file.fileno(), termios.TCSADRAIN, old_attrs)
def space(ch):
if ord(ch) == 32:
print 'You pressed space'
def enter(ch):
if ord(ch) == 10:
print 'You pressed enter'
def main():
print 'exit with ^C or ^D'
with raw_mode(sys.stdin):
try:
x = 0
while True:
print(x)
ch = sys.stdin.read(1)
space(ch)
enter(ch)
x = x + 1;
time.sleep(1)
except (KeyboardInterrupt, EOFError):
pass
if __name__ == '__main__':
main()

If you're on windows, check out msvcrt:
import msvcrt
while True:
x += 1
sleep(x)
if msvcrt.kbhit():
print "You pressed: %s" % msvcrt.getch()

Related

How to make my loop start and stop with key presses in python?

My code will start (pressing p) and stop (pressing o) the loop once, but won't start the loop again, what am I doing wrong?
import pyautogui, time, keyboard
while True:
if keyboard.is_pressed('p'):
print("Rodando")
x=0
while True:
if x == 0:
pyautogui.press("F7")
pyautogui.press("F7")
print("a")
time.sleep(0.1)
pyautogui.press("F7")
pyautogui.press("F7")
print("b")
time.sleep(0.5)
if keyboard.is_pressed('o'):
print("abortando")
x = 1
Try this code.
import pyautogui, time, keyboard
A = False
def start_loop():
global A
while True:
if keyboard.is_pressed('p') or A:
print("Rodando")
A = True
x=0
while True:
if x == 0:
pyautogui.press("F7")
pyautogui.press("F7")
print("a")
time.sleep(0.1)
pyautogui.press("F7")
pyautogui.press("F7")
print("b")
time.sleep(0.5)
if keyboard.is_pressed('o'):
print("abortando")
x = 1
A = False
break
start_loop()
start_loop()
keyboard module does not always work if this happens then please press the key again😊.

How to get Python to run display clear on keyboard interrupt

My Question is for the very end, I am trying to get the script to clear the display and disable the backlight when Control + C is pressed in linux, half of the time the lcd display will not clear
I have tried to get the python to close properly and clear the display but it only works a portion of time, even with sleep(1) added
#!/usr/bin/env python
import RPi.GPIO as GPIO
import lcd_driver
import socket
import struct
import fcntl
import time
import os
import re
from time import sleep
PIN = 23
COUNT = 0
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(PIN,GPIO.IN,pull_up_down=GPIO.PUD_UP)
print('Writing to Display!')
disp = lcd_driver.lcd()
def cputemp():
while True:
cputemp = os.popen("vcgencmd measure_temp").readline()
celsius = re.sub("[^0123456789/.]", "", cputemp)
fahrenheit = int(9.0/5.0*int(float(celsius)+32))
disp.lcdstring("Cpu : {} C".format(celsius), 1)
disp.lcdstring("Temp: {} F".format(fahrenheit), 2)
button = GPIO.input(PIN)
if button == False:
break
def curtime():
while True:
disp.lcdstring("Time: {}".format(time.strftime("%H:%M:%S")), 1)
disp.lcdstring("Date: {}".format(time.strftime("%m/%d/%Y")), 2)
button = GPIO.input(PIN)
if button == False:
break
def getaddr(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915,
struct.pack('256s', ifname[:15])
)[20:24])
def getip():
ip = getaddr('wlan0')
while True:
disp.lcdstring("IP Address: WiFi", 1)
disp.lcdstring(ip, 2)
button = GPIO.input(PIN)
if button == False:
break
try:
while True:
button = GPIO.input(PIN)
if button == True:
if COUNT == 0:
disp.lcdstring("Press the Button",1,0)
disp.lcdstring("To Start Demo!",2,1)
if COUNT == 1:
disp.clear()
cputemp()
if COUNT == 2:
disp.clear()
curtime()
if COUNT == 3:
disp.clear()
getip()
if COUNT == 4:
disp.clear()
COUNT = 1
if COUNT > 4:
disp.clear()
COUNT = 0
if button == False:
COUNT = COUNT +1
sleep(0.5)
except KeyboardInterrupt:
pass
finally:
disp.clear()
disp.backlight(0)
GPIO.cleanup()
import RPi.GPIO as GPIO
import lcd_driver
import socket
import struct
import atexit
import fcntl
import time
import os
import re
from time import sleep
def exit_handler():
print(' Cleaning Up!')
disp.clear()
disp.backlight(0)
GPIO.cleanup()
exit(1)
try:
while True:
button = GPIO.input(PIN)
if button == True:
if COUNT == 0:
disp.lcdstring("Press the Button",1,0)
disp.lcdstring("To Start Demo!",2,1)
if COUNT == 1:
disp.clear()
cputemp()
if COUNT == 2:
disp.clear()
curtime()
if COUNT == 3:
disp.clear()
getip()
if COUNT == 4:
disp.clear()
COUNT = 1
if COUNT > 4:
disp.clear()
COUNT = 0
if button == False:
COUNT = COUNT +1
sleep(0.5)
except KeyboardInterrupt:
pass
finally:
atexit.register(exit_handler)
You can add a shutdown hook using the atexit module
example code:
import RPi.GPIO as GPIO
import lcd_driver
import socket
import struct
import atexit
import fcntl
import time
import os
import re
from time import sleep
def exit_handler():
print(' Cleaning Up!')
disp.clear()
disp.backlight(0)
GPIO.cleanup()
exit(1)
atexit.register(exit_handler)
try:
while True:
button = GPIO.input(PIN)
if button == True:
if COUNT == 0:
disp.lcdstring("Press the Button",1,0)
disp.lcdstring("To Start Demo!",2,1)
if COUNT == 1:
disp.clear()
cputemp()
if COUNT == 2:
disp.clear()
curtime()
if COUNT == 3:
disp.clear()
getip()
if COUNT == 4:
disp.clear()
COUNT = 1
if COUNT > 4:
disp.clear()
COUNT = 0
if button == False:
COUNT = COUNT +1
sleep(0.5)
except KeyboardInterrupt:
pass
I use it on OrangePI to close a value when my code crash/exit and it works every time
Working code!!!
For Simplicity, being I can't vote yet
The difference is importing the atexit library
Adding in the event handler at top of code and replacing lcd.clear with lcd_driver.lcd()
And omitting the keyboard interrupt and following code to reflect how it is currently
#!/usr/bin/env python
import RPi.GPIO as GPIO
import lcd_driver
import socket
import struct
import atexit
import fcntl
import time
import os
import re
from time import sleep
PIN = 23
COUNT = 0
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(PIN,GPIO.IN,pull_up_down=GPIO.PUD_UP)
print('Writing to Display!')
disp = lcd_driver.lcd()
def exit_handler():
disp = lcd_driver.lcd()
disp.backlight(0)
GPIO.cleanup()
atexit.register(exit_handler)
def cputemp():
while True:
cputemp = os.popen("vcgencmd measure_temp").readline()
celsius = re.sub("[^0123456789/.]", "", cputemp)
fahrenheit = int(9.0/5.0*int(float(celsius)+32))
disp.lcdstring("Cpu : {} C".format(celsius), 1)
disp.lcdstring("Temp: {} F".format(fahrenheit), 2)
button = GPIO.input(PIN)
if button == False:
break
def curtime():
while True:
disp.lcdstring("Time: {}".format(time.strftime("%H:%M:%S")), 1)
disp.lcdstring("Date: {}".format(time.strftime("%m/%d/%Y")), 2)
button = GPIO.input(PIN)
if button == False:
break
def getaddr(ifname):
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
return socket.inet_ntoa(fcntl.ioctl(
s.fileno(),
0x8915,
struct.pack('256s', ifname[:15])
)[20:24])
def getip():
ip = getaddr('wlan0')
while True:
disp.lcdstring("IP Address: WiFi", 1)
disp.lcdstring(ip, 2)
button = GPIO.input(PIN)
if button == False:
break
try:
while True:
button = GPIO.input(PIN)
if button == True:
if COUNT == 0:
disp.lcdstring("Press the Button",1,0)
disp.lcdstring("To Start Demo!",2,1)
if COUNT == 1:
disp.clear()
cputemp()
if COUNT == 2:
disp.clear()
curtime()
if COUNT == 3:
disp.clear()
getip()
if COUNT == 4:
disp.clear()
COUNT = 1
if COUNT > 4:
disp.clear()
COUNT = 0
if button == False:
COUNT = COUNT +1
sleep(0.5)
except KeyboardInterrupt:
pass

Escape Sequences in Python Not Working in CMD

import time
listy = ['timer','stopwatch']
def intro():
print("This is a program which contains useful tools")
print(listy)
def timer():
x = int(input("How long Seconds ?"))
while x > 0:
print(x)
time.sleep(1)
x -= 1
def stopwatch():
verif = input("Do you want to start y/n \n")
if verif == 'y':
x = 0
while True:
print(x, end = "\b"*5)
time.sleep(1)
x += 1
def main():
intro()
decider = input("Which Program?")
if decider.lower() == 'timer':
timer()
elif decider.lower() == 'stopwatch':
stopwatch()
main()
in this code i dont know why the \b escape sequence isnt working in cmd or in idle, can anyone explain why? Is it because of a logic error?
A flush may be required. How about...
print("{0}{1}".format("\b"*5, x), end="", flush=True)

specific thread exit in python

I am using a thread to create an interruptible, which sends the KeyboardInterrupt command when a specific key is pressed. After this process I again use the msvcrt.getch() function. the problem is that the thread won't exit until I have pressed the key once. Ie the countdown function finishes but the the thread is still waiting for the input for the msvcrt.getch() function.
I tried putting exit.thread() at the end of the countdown function, but that exited the main thread. Do I need to use threading instead of thread?
import time, thread, msvcrt
print "enter time for timer"
n=raw_input()
def input_thread():
z=msvcrt.getch()
if z == "a" :
thread.interrupt_main()
if z != "a" :
thread.start_new_thread(input_thread, ())
print "nope"
thread.exit()
def countdown(n):
try:
thread.start_new_thread(input_thread, ())
for i in range(int(n)):
c = int(n) - int(i)
print c ,'seconds left','\r',
time.sleep(1)
except KeyboardInterrupt:
print "I was rudly interrupted"
countdown(n)
print """
"""
k=msvcrt.getch()
if k == "k" :
print "you typed k"
else :
print "you did not type K"
It is not the most beautiful way, but it works.
from concurrent.futures import thread
import threading
import time, msvcrt
print("enter time for timer")
n=input()
def input_thread():
z=msvcrt.getch()
if z == "a" :
thread.interrupt_main()
if z != "a" :
thread.start_new_thread(input_thread, ())
print ("nope")
thread.exit()
def countdown(n):
try:
threading.Thread(target=input_thread)
for i in range(int(n)):
c = int(n) - int(i)
print (c ,'seconds left','\r')
time.sleep(1)
except KeyboardInterrupt:
print("I was rudly interrupted")
countdown(n)
print ("")
k=msvcrt.getch()
if k == "k" :
print("you typed k")
else :
print ("you did not type K")

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