Python - do something until keypress or timeout Python - do something until keypress or timeout multithreading multithreading

Python - do something until keypress or timeout


You can use thread.interrupt_main().


Example:

import threadimport timeimport threadinge = threading.Event()def main():    thread.start_new_thread(wait_for_input, tuple())    thread.start_new_thread(do_something, tuple())def wait_for_input():    raw_input()    e.set()def do_something():    T0 = time.clock()    while (time.clock() - T0) < 60 and not e.isSet(): #as long as 60s haven't elapsed                                                      #and the flag is not set        #here do a bunch of stuff        time.sleep(5)    thread.interrupt_main() # kill the raw_input threadtry:    thread.start_new_thread(main, tuple())    while 1:        time.sleep(0.1) except KeyboardInterrupt:    pass 


Here is how I solved the issue. I didn't really want to move over to the lower-level thread module, and I decided I was happy for the user to use CTRL-C to cause the programme to exit gracefully.

It's a bit of a bodge because of the way it repurposes KeyboardInterrupt, which means it can't really be embedded in code that does need CTRL-C to be an ungraceful exit. However it's OK for my purposes.

import timeimport threadingdef do_something():    T0 = time.clock()    while (time.clock() - T0) < 60 and not e.isSet(): #as long as 60s haven't elapsed                                                      #and the flag is not set        #here do a bunch of stuff        time.sleep(5)thread = threading.Thread(target=do_something, args=())e = threading.Event()thread.start()print 'Press CTRL-C to interrupt'while thread.isAlive():    try: time.sleep(1) #wait 1 second, then go back and ask if thread is still alive    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        print 'Exiting...'        thread.join() #wait for the thread to finish

Update: It actually turned out to be much more straightforward to use a GUI button. The below code doesn't involve the slightly patchy repurposing of KeyboardInterrupt.

import timeimport threadingimport Tkinter as Tkdef do_something():    T0 = time.clock()    while (time.clock() - T0) < 60 and not e.isSet(): #as long as 60s haven't elapsed                                                      #and the flag is not set        #here do a bunch of stuff        time.sleep(5)def _quit():    print 'Exiting...'    e.set()    thread.join() #wait for the thread to finish    root.quit()    root.destroy()root = Tk.Tk()QuitButton = Tk.Button(master=root, text='Quit', command=_quit) #the quit buttonQuitButton.pack(side=Tk.BOTTOM)thread = threading.Thread(target=do_something, args=())e = threading.Event()thread.start()root.mainloop()


NOTE: I'd written this answer before you mentioned you were using Windows XP, and as such it won't help you—select only works on sockets under Windows. I think the answer is still useful for others though, so I'll leave it here.


This gets a bit more complicated than I like to write example code for, because I'm sure it will require some debugging, but I might approach the problem this way:

I'd use select in a loop, waiting on sys.stdin, with a five-second timeout. Each time it returned, if no input was present, I'd kick off the thread again (perhaps checking to see if the last thread had actually finished running), then continue the loop. If input was present, I'd exit the loop.

When select indicates input is present, I could either just consider it an interrupt flat-out or read in the input and evaulate whether or not it constitutes a valid interruption—if not, I could buffer it pending further input to complete an interruption. If it's an interruption, I'd wait on the thread to complete the work.