1

I'd like to get input on the terminal and give only a few seconds to respond. When the timeout is reached I'd like to read whatever has been typed in an accept that as the given input. Optionally I'd like the user to be able to press "enter" to submit their answer early.

I have the code below which works ok, but requires the user to press "enter" to submit. It has a bug: Entering text and then waiting for the timeout keeps the text in the "buffer". Then when you get prompted again you enter different text, press enter and then both strings are printed (see output). When the timeout is reached I'd like to accept whatever has been typed as "the answer." I'd like the user to still be able to submit an answer faster by pressing "enter".

Is there a way to achieve the desired behavior?

Note: I'm using Mac OS X

import sys
from select import select

def getResponse(timeout):
   print "Enter something:"
   rlist, wlist, xlist = select([sys.stdin], [], [], timeout)
   if rlist:
      result = sys.stdin.readline()
      return result
   else:
      return ''

while True:
   response = getResponse(3)
   print "Your input is:", response

Output:

Enter something:
pythonYour input is: 
Enter something:
dangit
Your input is: pythondangit
NTUI
  • 329
  • 4
  • 12
  • I don't believe there is any easy answer using the terminal. I can see this being done with ncurses, but that will likely introduce a thousand different problems. – mmgp Jan 14 '13 at 03:28

2 Answers2

2

You want to read user input from terminal in a non-blocking manner and also be able to timeout the reading and then get what has been written without needing to press enter. That is problematic at least, there isn't going to be an easy answer for that. As a starting point, you might consider using the following ncurses code. The user better not need to erase his input! The code can be adjusted to handle character deletion, and many other "fancy" features, in the end you might end with a mini-terminal.

What you are asking for is typically done using event loops, including the reading part (where raw_input is a bad choice). A GUI toolkit could solve that more easily, and more robustly (but of course it has some thousands lines behind it).

import time
import curses

ENTER_KEY = (curses.KEY_ENTER, ord('\n'), ord('\r'))

def run(win, timeout=3): # timeout in seconds
    curses.echo()
    win.timeout(0) # Non-block read.

    line = 0
    while True:
        win.addstr(line, 0, "Enter something: ")
        s = []
        start = time.time()
        run = True
        while run:
            c = win.getch()
            time_taken = time.time() - start

            if c < 0:
                pass
            elif c in ENTER_KEY:
                break
            else:
                s.append(chr(c))

            if time_taken >= timeout:
                # Out of time.
                s.append(-1)
                run = False

        if len(s) == 0:
            break
        if s[-1] == -1:
            s.pop()
        answer = ''.join(s)
        win.addstr(line + 1, 0, "Your input was: %s" % answer)
        line += 2

curses.wrapper(run)

To end gracefully press enter without typing anything. If the time runs out, it continues asking for input. I didn't test this code at all, I'm just aware of its many limitations.

mmgp
  • 18,901
  • 3
  • 53
  • 80
  • the suggestion of using the GUI is a good one. This is not console behavior. The work to write a full duplex terminal on top of console will need to be repeated per OS. – Peter Wooster Jan 14 '13 at 12:26
  • This is perfect! Does exactly what I need! Also thanks for the nod toward curses. I suspected that I could achieve the result using this library, but I didn't know where to start. – NTUI Jan 14 '13 at 13:05
0

This is not normal console behavior, but here is a post that discusses a few options:

Keyboard input with timeout in Python

Community
  • 1
  • 1
Peter Wooster
  • 6,009
  • 2
  • 27
  • 39
  • 1
    Right - I saw that, and I'm using select, but that's not the desired behavior. – NTUI Jan 14 '13 at 02:09
  • What is the desired behavior? – Peter Wooster Jan 14 '13 at 02:17
  • "I'd like to get input on the terminal and give only a few seconds to respond. When the timeout is reached I'd like to read whatever has been typed in an accept that as the given input. Optionally I'd like the user to be able to press "enter" to submit their answer early." – NTUI Jan 14 '13 at 02:43
  • Also note that none of those answers will give the input from the user if enter isn't pressed in time. – mmgp Jan 14 '13 at 03:28