YYK
YYK

Reputation: 123

How can I quit the loop when I want?

I working with R-Pi and cam. I have made code that iterate procedure over an hour.

import numpy as np
import cv2
import time

ret, frame = cam0.read()
timecheck = time.time()
future = 60
runNo = 0
while ret:
    if time.time() >= timecheck:
        ret, frame = cam0.read()
        #Do something here

        timecheck = time.time()+future
        runNo=runNo+1

        if(runNo> 60):
            break
    else:
        ret = cam0.grab()

#save log as .txt file
with open("acc_list.txt", "w") as output:
    output.write(str(acc_list))

But sometimes, it takes less than hour to finish work. I want to quit the iteration before runNo get to 60. Because I have to save acc_list.txt file, I can't just shut the program down.

If it was video streaming, I would use this:

while(cap.isOpened()):
    ret, frame = cap.read()
    if ret==True:
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

But I have encountered errors while modifying this to my code.

Is there nice way to do this?

Upvotes: 1

Views: 411

Answers (2)

Ezra
Ezra

Reputation: 191

you can put in the loop:

 if cv2.waitKey(1) & 0xFF == ord('q'):
    break

this will let you exit when hitting q.

Upvotes: 0

Mark Setchell
Mark Setchell

Reputation: 207405

There are lots of possible approaches, some are cleaner and some are dirtier :-)

In no particular order, here are some. I will probably add code for them as I find spare time to explain them properly.


Method 1 - Use a sentinel file

An easy (and "hacky") way to do this is to check inside the loop for the existence of a file called stop. Then in the shell/Terminal, just do:

touch stop   

and the program will exit. If you happen to use bash, you can just type:

> stop

Remember to delete the file called stop at the beginning and end of your program.

I am no Python programmer but this works:

#!/usr/local/bin/python3
import os
from time import sleep

# Remove any sentinel "stop" files from previous runs
def CleanUp():
    try:
        os.remove('stop')
    except:
        pass

CleanUp()
runNo=0
while True:
    print("Running...")
    sleep(1)
    if runNo>60 or os.path.exists('stop'):
        break
    runNo=runNo+1

print("Writing results")
CleanUp()

Method 2 - Use a second thread

Another way to do it is to start a second thread that does a blocking read from the terminal, and when the user enters something, it sets a flag which the main thread checks on each iteration through its loop the same as it checks runNo.

This demonstrates:

#!/usr/local/bin/python3
import threading
import os
from time import sleep

ExitRequested=False

def InputHandlerThread():
    global ExitRequested
    s=input('Press Enter/Return to exit\n')
    print("Exit requested")
    ExitRequested=True

# Start user input handler - start as daemon so main() can exit without waiting
t=threading.Thread(target=InputHandlerThread,daemon=True)
t.start()

runNo=0
while True:
    print('runNo: {}'.format(runNo))
    sleep(1)
    if runNo>60 or ExitRequested:
        break
    runNo=runNo+1

print("Writing results")

This may not work best with OpenCV because the imshow() function somehow uses spare time (given as the milliseconds parameter) in the waitKey() function to update the screen. You will see this if you call imshow() without any following waitKey() - nothing will appear on the screen.

So, if you are using imshow() you must use waitKey() and that will interfere with reading the keyboard in the second thread. If that's the case, use one of the other methods.


Method 3 - Write results incrementally

A third way to do it is to open your results file for append inside the loop and add each new result as it comes available rather than waiting till the end.

I don't know enough about your algorithm to know if this is a possibility for you.

I'm still no Python programmer, but this works:

#!/usr/local/bin/python3
import os
from time import sleep

runNo=0
while True:
    print("Running...")
    # Append results to output file
    with open("results.txt", "a") as results:
        results.write("Result {}\n".format(runNo))
    sleep(1)
    if runNo>60:
        break
    runNo=runNo+1

Method 4 - Use a signal

A fourth way to do it is to set up a signal handler and when it receives the signal it sets a flag which the main loop checks on every iteration. Then in the terminal you use:

pkill -SIGUSR1 yourScript.py

See documentation on signals.

Here is some working code:

#!/usr/local/bin/python3
import signal
import os
from time import sleep

def handler(signum,stack):
    print("Signal handler called with signal ",signum)
    global ExitRequested
    ExitRequested=True

ExitRequested=False

# Install signal handler
signal.signal(signal.SIGUSR1,handler)


runNo=0
while True:
    print('runNo: {} Stop program with: "kill -SIGUSR1 {}"'.format(runNo,os.getpid()))
    sleep(1)
    if runNo>60 or ExitRequested:
        break
    runNo=runNo+1

print("Writing results")

Sample Output

runNo: 0 Stop program with: "kill -SIGUSR1 16735"
runNo: 1 Stop program with: "kill -SIGUSR1 16735"
runNo: 2 Stop program with: "kill -SIGUSR1 16735"
runNo: 3 Stop program with: "kill -SIGUSR1 16735"
runNo: 4 Stop program with: "kill -SIGUSR1 16735"
runNo: 5 Stop program with: "kill -SIGUSR1 16735"
runNo: 6 Stop program with: "kill -SIGUSR1 16735"
runNo: 7 Stop program with: "kill -SIGUSR1 16735"
Signal handler called with signal  30
Writing results

Discussion

YMMV, but my feeling is that Method 3 is the cleanest, and that Method 1 is the biggest hack. Method 2 is probably closest to what you asked for and I did Method 4 (and all the others, in fact) so I could learn something.

If any real Python programmers have any comments, I'd be happy to learn.

Upvotes: 2

Related Questions