38

I want to do a raw_input('Enter something: .'). I want it to sleep for 3 seconds and if there's no input, then cancel the prompt and run the rest of the code. Then the code loops and implements the raw_input again. I also want it to break if the user inputs something like 'q'.

tshepang
  • 12,111
  • 21
  • 91
  • 136
ykmizu
  • 421
  • 1
  • 5
  • 7
  • 1
    related: [Python 3 Timed Input /15528939](https://stackoverflow.com/questions/15528939/python-3-timed-input) – n611x007 Mar 13 '15 at 13:10
  • 1
    related: [Timeout on a Python function call /492519](https://stackoverflow.com/questions/492519/timeout-on-a-python-function-call) – n611x007 Mar 13 '15 at 13:10
  • 1
    related: [How to set time limit on input /2933399](https://stackoverflow.com/questions/2933399/how-to-set-time-limit-on-input) – n611x007 Mar 13 '15 at 13:11
  • Portable, stdlib-only, thread-only, accepted answer in duplicate [here](https://stackoverflow.com/a/53180738/9059420) – Darkonaut Nov 12 '18 at 16:41

4 Answers4

62

There's an easy solution that doesn't use threads (at least not explicitly): use select to know when there's something to be read from stdin:

import sys
from select import select

timeout = 10
print "Enter something:",
rlist, _, _ = select([sys.stdin], [], [], timeout)
if rlist:
    s = sys.stdin.readline()
    print s
else:
    print "No input. Moving on..."

Edit[0]: apparently this won't work on Windows, since the underlying implementation of select() requires a socket, and sys.stdin isn't. Thanks for the heads-up, @Fookatchu.

Community
  • 1
  • 1
rbp
  • 43,594
  • 3
  • 38
  • 31
  • Can't use this cause my company computers are using 2.3.3.. Thanks. – ykmizu Aug 12 '10 at 21:00
  • 1
    The `select` module is in python 2.3. – habnabit Aug 12 '10 at 21:02
  • ykmizu: as Aaron pointed out, the select module is available on Python 2.3.3: http://docs.python.org/release/2.3.3/lib/module-select.html – rbp Aug 12 '10 at 21:05
  • 1
    I'm getting this error: Traceback (most recent call last): File "Z:\test.py", line 6, in rlist, _, _ = select([sys.stdin], [], [], timeout) TypeError: argument must be an int, or have a fileno() method. – ykmizu Aug 12 '10 at 21:21
  • Well, the documentation explicitly mentions that "Among the acceptable object types in the lists are Python file objects (e.g. sys.stdin". From your traceback, I assume you're running on Windows, perhaps that's related. Is the final program running on Windows as well? – rbp Aug 12 '10 at 21:29
  • No, I'm running everything on unix, solaris – ykmizu Aug 12 '10 at 21:33
  • On Solaris? Did you name the file "Z:\test.py"? Can you try to print "sys.stdin" before calling select, and paste the whole output (the print and the traceback) on http://pastebin.org/? – rbp Aug 12 '10 at 22:34
  • I just got it to work. Thank you so much for that code. I am forever indebted to you. You have seriously saved my life and my sanity. Thank you. – ykmizu Aug 12 '10 at 22:57
  • I'm glad to hear it :) Since you seem to be new on Stack Overflow, let me give you a gentle nudge: if you find that someone has appropriately answered your question (this one, the other one you asked elsewhere, and any one that you might ask in the future), please don't forget to mark that answer as the correct one. Thanks, and good luck with your project! – rbp Aug 12 '10 at 23:34
  • 5
    nice. btw, i have to `sys.stdout.flush()` after printing the prompt, to see it – mykhal Oct 12 '10 at 04:09
  • I have been struggling with getting a keyboard input with timeout today. I just wanted a way to stop the reproduction of images from the hard-drive so that I can stop it just pressing a key, so I wanted a small timeout (33ms). I just want to point out that some solutions that you'll find on stackoverflow don't work on IDLE!! (I don't know why). You have to execute them on terminal. And also, the most helpful code I have found on internet is this one: http://home.wlu.edu/~levys/software/kbhit.py . Good luck! – jespestana Jun 12 '13 at 23:47
  • Just a tiny question, is this select the same as the one used to check if sockets have something in them to be read? (I'm guessing yes, since in Unix/Linux everything is treated as a file) – ffledgling Nov 08 '13 at 00:17
  • @ffledgling yes, that's exactly it :) – rbp Nov 08 '13 at 10:11
  • 1
    This doesn't work on windows: [Can select() be used with files in Python under Windows?](http://stackoverflow.com/questions/10842428/can-select-be-used-with-files-in-python-under-windows) – Fookatchu Mar 13 '14 at 12:49
  • @Fookatchu I can't test it on Windows, but apparently it doesn't indeed. I've added a note to the answer. Thanks! – rbp Mar 13 '14 at 15:36
  • As a reminder, using `select` instead of `raw_input` takes away Emacs-like command line editing. https://docs.python.org/2/library/functions.html#raw_input – ChaimKut Aug 13 '14 at 11:15
16

If you're working on Windows you can try the following:

import sys, time, msvcrt

def readInput( caption, default, timeout = 5):
    start_time = time.time()
    sys.stdout.write('%s(%s):'%(caption, default));
    input = ''
    while True:
        if msvcrt.kbhit():
            chr = msvcrt.getche()
            if ord(chr) == 13: # enter_key
                break
            elif ord(chr) >= 32: #space_char
                input += chr
        if len(input) == 0 and (time.time() - start_time) > timeout:
            break

    print ''  # needed to move to next line
    if len(input) > 0:
        return input
    else:
        return default

# and some examples of usage
ans = readInput('Please type a name', 'john') 
print 'The name is %s' % ans
ans = readInput('Please enter a number', 10 ) 
print 'The number is %s' % ans 
Paul
  • 329
  • 3
  • 5
  • 1
    This will work fine from the command line, but it will not work when running inside Eclipse, I'm not sure how to get msvcrt reading the keyboard from inside Eclipse. – Paul Oct 21 '10 at 21:15
  • 1
    I never used Eclipse for Python, but i will guess that is same than sublimtext, the builtin console is not a proper implementation, just an output. I am sure you can configure Eclipse to use a real terminal or cmd in that case, so you can type input to it. – m3nda Jan 02 '17 at 10:52
  • @Paul - this does not work for me on windows. I'm running your code, verbatim with the exception of changing Print to py3 syntax, and adding a stdout.flush(). Windows7, python3.6 – some bits flipped Jan 23 '17 at 16:49
  • FYI -- you shouldn't overwrite the built-in function by using `chr` as a variable name – kevinsa5 Aug 01 '18 at 20:09
  • `getwche` maybe needed for some – v010dya Jul 11 '23 at 15:35
2

I have some code which makes a countdown app with a tkinter entry box and button so they can enter something and hit the button, if the timer runs out the tkinter window closes and tells them they ran out of time. I think most other solutions to this problem don't have a window which pops up so thought id add to the list :)

with raw_input() or input(), it isn't possible as it stops at the input section, until it receives input, then it carries on...

I have taken some code from the following link: Making a countdown timer with Python and Tkinter?

I used Brian Oakley's answer to this problem and added the entrybox etc.

import tkinter as tk

class ExampleApp(tk.Tk):

    def __init__(self):
        tk.Tk.__init__(self)
        def well():
            whatis = entrybox.get()
            if whatis == "": # Here you can check for what the input should be, e.g. letters only etc.
                print ("You didn't enter anything...")
            else:
                print ("AWESOME WORK DUDE")
            app.destroy()
        global label2
        label2 = tk.Button(text = "quick, enter something and click here (the countdown timer is below)", command = well)
        label2.pack()
        entrybox = tk.Entry()
        entrybox.pack()
        self.label = tk.Label(self, text="", width=10)
        self.label.pack()
        self.remaining = 0
        self.countdown(10)

    def countdown(self, remaining = None):
        if remaining is not None:
            self.remaining = remaining

        if self.remaining <= 0:
            app.destroy()
            print ("OUT OF TIME")


        else:
            self.label.configure(text="%d" % self.remaining)
            self.remaining = self.remaining - 1
            self.after(1000, self.countdown)

if __name__ == "__main__":
    app = ExampleApp()
    app.mainloop()

I know what I added was a bit lazy but it works and it is an example only

This code works for Windows with Pyscripter 3.3

Community
  • 1
  • 1
W1ll1amvl
  • 1,219
  • 2
  • 16
  • 30
1

For rbp's answer:

To account for input equal to a Carriage Return simply add a nested condition:

if rlist:
    s = sys.stdin.readline()
    print s
    if s == '':
        s = pycreatordefaultvalue
Rohan
  • 52,392
  • 12
  • 90
  • 87
maphilli14
  • 19
  • 5