Ok, I have achieved it :D.
#!/usr/bin/env python
import sys
from select import select
def main(argv):
timeout = 3
prompt = '> '
max_chars = 3
# set raw input mode if relevant
# it is necessary to make stdin not wait for enter
try:
import tty, termios
prev_flags = termios.tcgetattr(sys.stdin.fileno())
tty.setraw(sys.stdin.fileno())
except ImportError:
prev_flags = None
buf = ''
sys.stderr.write(prompt)
while True: # main loop
rl, wl, xl = select([sys.stdin], [], [], timeout)
if rl: # some input
c = sys.stdin.read(1)
# you will probably want to add some special key support
# for example stop on enter:
if c == '\n':
break
buf += c
# auto-output is disabled as well, so you need to print it
sys.stderr.write(c)
# stop if N characters
if len(buf) >= max_chars:
break
else:
# timeout
break
# restore non-raw input
if prev_flags is not None:
termios.tcsetattr(sys.stdin.fileno(), termios.TCSADRAIN, prev_flags)
# and print newline
sys.stderr.write('\n')
# now buf contains your input
# ...
if __name__ == "__main__":
main(sys.argv[1:])
It's fairly incomplete; I just put a few values to test it. A few words of explanation:
- You need to switch the tty to 'raw' mode — otherwise you wouldn't be able to get input without it being confirmed by enter key,
- in raw mode the typed in characters are no longer output by default — you need to output them yourself if you want user to see what he is typing,
- you probably want to handle special keys like enter and backspace — I've added enter handling here. Maybe you could reuse parts of
curses
for that,
- I've assumed the timeout is '3 seconds after last key'. If you want timeout for whole process, I think the easiest way would be to get current time, increase it by timeout (i.e. get
end_time
), and then pass end_time - current_time
in seconds as timeout to select()
,
- I've made unix-specific imports optional. I don't know whether it will work on Windows correctly, though.