How to set time limit on raw_input
The signal.alarm function, on which @jer's recommended solution is based, is unfortunately Unix-only. If you need a cross-platform or Windows-specific solution, you can base it on threading.Timer instead, using thread.interrupt_main to send a KeyboardInterrupt
to the main thread from the timer thread. I.e.:
import threadimport threadingdef raw_input_with_timeout(prompt, timeout=30.0): print(prompt, end=' ') timer = threading.Timer(timeout, thread.interrupt_main) astring = None try: timer.start() astring = input(prompt) except KeyboardInterrupt: pass timer.cancel() return astring
this will return None whether the 30 seconds time out or the user explicitly decides to hit control-C to give up on inputting anything, but it seems OK to treat the two cases in the same way (if you need to distinguish, you could use for the timer a function of your own that, before interrupting the main thread, records somewhere the fact that a timeout has happened, and in your handler for KeyboardInterrupt
access that "somewhere" to discriminate which of the two cases occurred).
Edit: I could have sworn this was working but I must have been wrong -- the code above omits the obviously-needed timer.start()
, and even with it I can't make it work any more. select.select
would be the obvious other thing to try but it won't work on a "normal file" (including stdin) in Windows -- in Unix it works on all files, in Windows, only on sockets.
So I don't know how to do a cross-platform "raw input with timeout". A windows-specific one can be constructed with a tight loop polling msvcrt.kbhit, performing a msvcrt.getche
(and checking if it's a return to indicate the output's done, in which case it breaks out of the loop, otherwise accumulates and keeps waiting) and checking the time to time out if needed. I cannot test because I have no Windows machine (they're all Macs and Linux ones), but here the untested code I would suggest:
import msvcrtimport timedef raw_input_with_timeout(prompt, timeout=30.0): print(prompt, end=' ') finishat = time.time() + timeout result = [] while True: if msvcrt.kbhit(): result.append(msvcrt.getche()) if result[-1] == '\r': # or \n, whatever Win returns;-) return ''.join(result) time.sleep(0.1) # just to yield to other processes/threads else: if time.time() > finishat: return None
The OP in a comment says he does not want to return None
upon timeout, but what's the alternative? Raising an exception? Returning a different default value? Whatever alternative he wants he can clearly put it in place of my return None
;-).
If you don't want to time out just because the user is typing slowly (as opposed to, not typing at all!-), you could recompute finishat after every successful character input.
I found a solution to this problem in a blog post. Here's the code from that blog post:
import signalclass AlarmException(Exception): passdef alarmHandler(signum, frame): raise AlarmExceptiondef nonBlockingRawInput(prompt='', timeout=20): signal.signal(signal.SIGALRM, alarmHandler) signal.alarm(timeout) try: text = raw_input(prompt) signal.alarm(0) return text except AlarmException: print '\nPrompt timeout. Continuing...' signal.signal(signal.SIGALRM, signal.SIG_IGN) return ''
Please note: this code will only work on *nix OSs.
The input() function is designed to wait for the user to enter something (at least the [Enter] key).
If you are not dead set to use input(), below is a much lighter solution using tkinter. In tkinter, dialog boxes (and any widget) can be destroyed after a given time.
Here is an example :
import tkinter as tkdef W_Input (label='Input dialog box', timeout=5000): w = tk.Tk() w.title(label) W_Input.data='' wFrame = tk.Frame(w, background="light yellow", padx=20, pady=20) wFrame.pack() wEntryBox = tk.Entry(wFrame, background="white", width=100) wEntryBox.focus_force() wEntryBox.pack() def fin(): W_Input.data = str(wEntryBox.get()) w.destroy() wSubmitButton = tk.Button(w, text='OK', command=fin, default='active') wSubmitButton.pack()# --- optionnal extra code in order to have a stroke on "Return" equivalent to a mouse click on the OK button def fin_R(event): fin() w.bind("<Return>", fin_R)# --- END extra code --- w.after(timeout, w.destroy) # This is the KEY INSTRUCTION that destroys the dialog box after the given timeout in millisecondsd w.mainloop()W_Input() # can be called with 2 parameter, the window title (string), and the timeout duration in milisecondsif W_Input.data : print('\nYou entered this : ', W_Input.data, end=2*'\n')else : print('\nNothing was entered \n')