KeyboardInterrupt not working in Python to stop thread - python

I want to capture and store the images from live streams using two threads. I want to stop the threads with Ctrl + C keyboard interrupt. I am calling a function from
if name == "main":
but it is not working. I am using spyder with an anaconda environment. I also tried it using terminal but it is not working. Can anyone explain why and how it will work?
The code is as follow
class ImageGrabber(threading.Thread):
def __init__(self, ID):
threading.Thread.__init__(self)
self.ID=ID
self.cam=cv2.VideoCapture(ID)
self.fps = self.cam.get(cv2.CAP_PROP_FPS)
self.w=self.cam.get(cv2.CAP_PROP_FRAME_WIDTH)
self.h=self.cam.get(cv2.CAP_PROP_FRAME_HEIGHT)
(self.grabbed, self.frame) =self.cam.read()
print(f"Camera Opened with fps {self.fps}, width = {self.w}, and height = {self.h}")
plt.imshow(self.frame)
plt.show()
self.stopped = False
def run(self):
global frames
global exitProgram
# print("Entered Grabbed Run ******:")
while not self.stopped:
if not self.grabbed or exitProgram is True:
print("Exit Command reached")
self.stop()
self.cam.release()
else:
(self.grabbed, self.frame) =self.cam.read()
frames.put(self.frame)
def stop(self):
self.stopped = True
class ImageSaver(threading.Thread):
def __init__(self,grabber,codec):
threading.Thread.__init__(self)
global saveImages
fourcc = cv2.VideoWriter_fourcc(*codec)
self.fName=self.getDateStamp() + '.avi'
if saveImages is False:
self.out = cv2.VideoWriter(self.fName, fourcc,grabber.fps ,(int(grabber.w),int(grabber.h)))
def getDateStamp(self):
filedate = str(datetime.now())
filedate = filedate[0:-7]
filedate = filedate.replace(':', '_')
filename = filedate
#print(filename)
return filename
def run(self):
global frames
global saveImages
while True:
# print("Entered Loop 1:")
if(not frames.empty()):
# print("Entered frame showing:")
self.Currframe=frames.get()
if saveImages is False:
self.out.write(self.Currframe)
else:
self.imgName=self.getDateStamp() + '.tiff'
cv2.imwrite(self.imgName,self.Currframe)
print(f"Queue Size in writing = {frames.qsize()}")
# print("Show Image")
elif exitProgram is True:
print("Exit Command Main reached")
print(f"Final Queue Size at exit = {frames.qsize()}")
break
def initStart(*args):
argsList=sum(list(args),[])
argsLen = len(argsList)
if(argsLen==2):
ID = int(argsList[0])
codec = argsList[1]
elif(argsLen==1):
ID = int(argsList[0])
codec = 'XVID'
else:
ID = 0
codec = 'XVID'
grabber = ImageGrabber(ID)
saver = ImageSaver(grabber,codec)
grabber.start()
saver.start()
e = threading.Event()
# main.join()
# grabber.join()
print ('Press CTRL-C to interrupt')
while grabber.isAlive():
try:
time.sleep(1) #wait 1 second, then go back and ask if thread is still alive
print ('Sleep...')
except: #if ctrl-C is pressed within that second,
#catch the KeyboardInterrupt exception
e.set() #set the flag that will kill the thread when it has finished
exitProgram=True
print ('Exiting...')
grabber.join()
saver.join()
if __name__ == "__main__":
full_cmd_arguments = sys.argv
# Keep all but the first. First contains the path of script or file
argument_list = full_cmd_arguments[1:]
initStart(argument_list)
If I change the program in such a way that keyboardInterrupt is called without any function then it works
grabber = ImageGrabber(0)
main = Main(grabber,'XVID')
grabber.start()
main.start()
e = threading.Event()
print ('Press CTRL-C to interrupt')
while grabber.isAlive():
try: time.sleep(1) #wait 1 second, then go back and ask if thread is
except KeyboardInterrupt: #if ctrl-C is pressed within that second,
#catch the KeyboardInterrupt exception
e.set() #set the flag that will kill the thread when it has finished
exitProgram=True
print ('Exiting...')
grabber.join()
main.join()
The above code is called directly as python script then it works. Any reason?
Any idea? how it can work?

...
print ('Press CTRL-C to interrupt')
try:
while grabber.isAlive():
time.sleep(1) #wait 1 second, then go back and ask if thread is
except KeyboardInterrupt: #if ctrl-C is pressed within that second,
#catch the KeyboardInterrupt exception
e.set()
...
Try this way.
The reason why KeyboardInterrupt was not working is because the while loop was scoping the try except statement, so it would repeat it continuously even though you were pressing the KeyboardInterrupt.
Hope this helps!

Related

read keyboard/stdin asynchronously

I have a loop where I would like to do something until a key is pressed.
Is there a way to read input from stdin in another thread until a specific key is pressed?
Is there a better way to do this?
def capture_loop(dev_path, breakout_key="a"):
captured_devs = []
capture = True
def wait_for_key():
global capture
while True:
i, o, e = select.select([sys.stdin], [], [], 10)
if len(i) > 0:
input_string = i[0].read()
if breakout_key in str(input_string):
capture = False
break
else:
print("You said nothing!")
t = threading.Thread(target=wait_for_key)
t.setDaemon(False)
t.start()
while capture:
print("Doing Stuff until the 'a' key is pressed")
time.sleep(10)
print("Done doing stuff")
If all you want is to do something until a key combination is pressed, but you don't care what key combination use, you can catch a KeyboardInterrupt exception which is raised when you press control-C:
try:
while True:
print("Doing something until C-c is pressed...")
time.sleep(1)
except KeyboardInterrupt:
print("Done!")

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()

Python break out of poll when key pressed

Apologies for the newbie Python post, but a bit of Googling and I still can't find what I need.
I have the following Pi Hat; https://github.com/modmypi/Jam-HAT, ...and I'm using their docs to guide me; https://gpiozero.readthedocs.io/en/stable/recipes.html
My idea is pretty simple;
run script when button pressed
wait for it to run, showing flashy lights whilst running
if I press another button, stop/kill script
However, I'm stuck on the part where I need to stop the script with a button
#!/usr/bin/env python3
import gpiozero
import subprocess
from signal import pause
from time import sleep
button1 = gpiozero.Button(18)
button2 = gpiozero.Button(19)
green1 = gpiozero.LED(16)
green2 = gpiozero.LED(17)
red1 = gpiozero.LED(5)
red2 = gpiozero.LED(6)
script = ""
yellow1 = gpiozero.LED(12)
yellow2 = gpiozero.LED(13)
def kill_hello():
global script
script.kill()
print("Killed PID: ", script.pid)
red1.on()
sleep(.5)
red1.off()
red2.on()
sleep(.5)
red2.off()
def say_hello():
global script
green1.on()
sleep(.5)
green1.off()
green2.on()
sleep(.5)
green2.off()
script = subprocess.Popen(['/home/kali/hello.sh'])
print("Script PID: ", script.pid)
while script.poll() is None:
if not button2.when_pressed:
green1.on()
sleep(.5)
green1.off()
sleep(1)
button2.when_pressed = kill_hello
print("Press Ctrl & C to Quit")
try:
button1.when_pressed = say_hello
pause()
except KeyboardInterrupt:
print( "\n...Quit!")
I've tried a try/except where that while loop is, but that has also not worked (not like the KeyboardInterrupt). It doesn't recognise the button has been pressed, and I'm assuming because it's not within a valid catch/else/something block.
Any suggestions for a simple break out of loop, please?
It seems the issue is around a single process is hogging Python, thus creating multiple threads helped;
#!/usr/bin/env python3
import gpiozero
import subprocess
import time
import threading
from signal import pause
button1 = gpiozero.Button(18)
button2 = gpiozero.Button(19)
green1 = gpiozero.LED(16)
green2 = gpiozero.LED(17)
red1 = gpiozero.LED(5)
red2 = gpiozero.LED(6)
script = ""
yellow1 = gpiozero.LED(12)
yellow2 = gpiozero.LED(13)
switch = True
def blink():
def run():
while (switch == True):
green1.on()
time.sleep(.2)
green1.off()
time.sleep(.2)
if switch == False:
break
thread = threading.Thread(target=run)
thread.start()
def switchon():
global switch
global script
switch = True
print('switch on')
script = subprocess.Popen(['/home/kali/hello.sh'])
blink()
def switchoff():
global switch
global script
print('switch off')
script.kill()
switch = False
try:
button1.when_pressed = switchon
button2.when_pressed = switchoff
pause()
except KeyboardInterrupt:
print( "\n...Quit!")

Thread doesn't stopping when i want to stop it

I have a program that displays video in a tkinter window. When i want to close window i stopping video thread. But it doesn't stop. There are four functions:
def start_video(self):
if self.video is not None:
self.video_thread_stopped = False
try:
if not self.video_thread.is_alive():
self.video_thread = threading.Thread(target=self.video_stream)
self.video_thread.start()
except AttributeError:
if self.video_thread is None:
self.video_thread = threading.Thread(target=self.video_stream)
self.video_thread.start()
start_video function starts video thread
def video_stream(self):
_, frame = self.video.read()
str_time = time.time()
while frame is not None and getattr(self.video_thread, "running", True):
self.current_frame += 1
# resize and set image to label
frame = cv2.resize(frame, self.video_frame_size)
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
imgtk = ImageTk.PhotoImage(image=Image.fromarray(cv2image))
self.video_label.config(image=imgtk)
self.video_label.image = imgtk
# waiting for frame rate
while time.time() - str_time < (1 / self.fps):
pass
str_time = time.time()
# reading next frame
_, frame = self.video.read()
print("exited from loop")
video_stream is the thread function
def pause_video(self):
if self.video_loaded:
self.video_thread_stopped = True
if self.video_thread is not None:
# stop video
self.video_thread.running = False
print('before join')
start_time = time.time()
while self.video_thread.is_alive():
if time.time() - start_time > 1:
break
print("after while")
self.video_thread.join()
print('after join')
pause_video has to kill thread and stop streaming video
def on_window_close(self):
self.pause_video()
print("thread stopped")
self.root.destroy()
on_window_close has to stop thread before closing tk window
(I have the following code)
root.protocol("WM_DELETE_WINDOW", w.on_window_close)
So when I start video thread and pressing a close button on the tk window - thread doesn't stop. Here is a terminal output
before join
after while
Can someone help me and tell why it doesn't stop the video_thread. Thanks!
I found the solution.
In the start_video method you have to make video_thread daemon:
def start_video(self):
if self.video is not None:
self.video_thread_stopped = False
try:
if not self.video_thread.is_alive():
self.video_thread = threading.Thread(target=self.video_stream)
self.video_thread.daemon = 1
# start audio
self.audio_class.start(self.current_frame, self.total_video_frames)
# start thread
self.video_thread.start()
except AttributeError:
if self.video_thread is None:
self.video_thread = threading.Thread(target=self.video_stream)
self.video_thread.daemon = 1
# start audio
self.audio_class.start(self.current_frame, self.total_video_frames)
# start thread
self.video_thread.start()
And in the pause_video method you need to remove line with video_thread.join()
def pause_video(self):
if self.video_loaded:
self.video_thread_stopped = True
if self.video_thread is not None:
# stop audio
# stop video
self.video_thread.running = False
print("video stopped")
self.audio_class.stop()
print("audio stopped")
# print('before join')
start_time = time.time()
while self.video_thread.is_alive():
# print("waiting for none")
if time.time() - start_time > 1:
# self.video_thread = None
break

Stoping python script using KeyboardInterrupt

Hi why is my KeyboardInterrupt: is not stoping my program when i hit control c or control x? this is my current code.
I am using python Threading that runs 2 function coinPulser and coinPulserDone.
import threading
import time
lock = threading.Lock()
counter = 0
input = 3
def coinPulser ():
global counter
global input
lock.acquire()
try:
while counter < input:
counter+=1
time.sleep(.1)
if counter in [1,3,5]:
print(counter)
return counter
finally:
lock.release()
def coinPulserDone ():
while True:
print(coinPulser())
try:
coinpulser = threading.Thread(target = coinPulser)
coinpulser.start()
coinpulserdone = threading.Thread(target = coinPulserDone)
coinpulserdone.start()
except KeyboardInterrupt:
coinpulser.stop()
coinpulserdone.stop()
print('Thread Stops')
I suspect the problem is that your code exits your try/except block before you press Cntr-C. You need to add some form of a loop that will hold it in that block. A simple loop such as
while True:
time.sleep(1)
before your except line should do the trick.

Categories

Resources