From the pycamera docs I took the example for fast capture and processing and added a sigint event handler to catch the keyboard interrupt:
import io
import time
import threading
import picamera
# Create a pool of image processors
done = False
lock = threading.Lock()
pool = []
def signal_handler(signal, frame):
global done
print 'You pressed Ctrl+C!'
done=True
sys.exit()
signal.signal(signal.SIGINT, signal_handler)
class ImageProcessor(threading.Thread):
def __init__(self):
super(ImageProcessor, self).__init__()
self.stream = io.BytesIO()
self.event = threading.Event()
self.terminated = False
self.daemon=True;
self.start()
def run(self):
# This method runs in a separate thread
global done
while not self.terminated:
# Wait for an image to be written to the stream
if self.event.wait(1):
try:
self.stream.seek(0)
# Read the image and do some processing on it
#Image.open(self.stream)
#...
#...
# Set done to True if you want the script to terminate
# at some point
#done=True
finally:
# Reset the stream and event
self.stream.seek(0)
self.stream.truncate()
self.event.clear()
# Return ourselves to the pool
with lock:
pool.append(self)
def streams():
while not done:
with lock:
if pool:
processor = pool.pop()
else:
processor = None
if processor:
yield processor.stream
processor.event.set()
else:
# When the pool is starved, wait a while for it to refill
time.sleep(0.1)
with picamera.PiCamera() as camera:
pool = [ImageProcessor() for i in range(4)]
camera.resolution = (640, 480)
camera.framerate = 30
camera.start_preview()
time.sleep(2)
camera.capture_sequence(streams(), use_video_port=True)
# Shut down the processors in an orderly fashion
while pool:
with lock:
processor = pool.pop()
processor.terminated = True
processor.join()
but the interrupt signal is never caught.
Until the camera.capture_sequence(streams(), use_video_port=True)
runs the signal is caught, after capture_sequence
is started the signal handler is not called.
I'm new to python so maybe the answer is simple. What am i doing wrong in here?
EDIT:
If i remove the following code the signal is caught:
yield processor.stream
The problem there is that you are using thread.join()
, it block the main thread,which means your program have to wait until that thread you joined finishes to continue.
The signals will always be caught by the main process, because it's the one that receives the signals, it's the process that has threads.
There are plenty of answer about how to deal with main thread and CTRL+C,and i give you three options,
thread1.join(60)
detail here
class Watcher():
def __init__(self):
self.child = os.fork()
if self.child == 0:
return
else:
self.watch()
def watch(self):
try:
os.wait()
except KeyboardInterrupt:
self.kill()
sys.exit()
def kill(self):
try:
os.kill(self.child, signal.SIGKILL)
except OSError:
pass
start a Watcher before you start work thread,like
def main():
init()
Watcher()
start_your_thread1()
start_your_thread2()
start_your_thread3()
just delete the final join(),and add some task for the main thread.
i prefer the second option,it's easy use,and solves two problems with multithreaded programs in Python, (1) a signal might be delivered to any thread (which is just a malfeature) and (2) if the thread that gets the signal is waiting, the signal is ignored (which is a bug). More detail about the Watcher is in Appendix A of the book The Little Book of Semaphores