raw_input and timeout [duplicate]
Asked Answered
F

4

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'.

Fenske answered 12/8, 2010 at 19:48 Comment(4)
related: Python 3 Timed Input /15528939Trypsin
related: Timeout on a Python function call /492519Trypsin
related: How to set time limit on input /2933399Trypsin
Portable, stdlib-only, thread-only, accepted answer in duplicate hereProfessionalism
U
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.

Uterus answered 12/8, 2010 at 20:41 Comment(16)
Can't use this cause my company computers are using 2.3.3.. Thanks.Fenske
The select module is in python 2.3.Mitzvah
ykmizu: as Aaron pointed out, the select module is available on Python 2.3.3: docs.python.org/release/2.3.3/lib/module-select.htmlUterus
I'm getting this error: Traceback (most recent call last): File "Z:\test.py", line 6, in <module> rlist, _, _ = select([sys.stdin], [], [], timeout) TypeError: argument must be an int, or have a fileno() method.Fenske
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?Uterus
No, I'm running everything on unix, solarisFenske
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 pastebin.org?Uterus
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.Fenske
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!Uterus
nice. btw, i have to sys.stdout.flush() after printing the prompt, to see itMeek
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: home.wlu.edu/~levys/software/kbhit.py . Good luck!Trudietrudnak
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)Reprisal
@Reprisal yes, that's exactly it :)Uterus
This doesn't work on windows: Can select() be used with files in Python under Windows?Guanase
@Guanase I can't test it on Windows, but apparently it doesn't indeed. I've added a note to the answer. Thanks!Uterus
As a reminder, using select instead of raw_input takes away Emacs-like command line editing. docs.python.org/2/library/functions.html#raw_inputForensics
S
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 
Stare answered 12/10, 2010 at 3:50 Comment(5)
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.Stare
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.Backchat
@Stare - 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.6Arching
FYI -- you shouldn't overwrite the built-in function by using chr as a variable nameLeuko
getwche maybe needed for somePhosphorite
L
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

Longbow answered 8/6, 2014 at 3:50 Comment(0)
C
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
Centro answered 1/10, 2012 at 4:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.