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
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.
This is not normal console behavior, but here is a post that discusses a few options:
Keyboard input with timeout in Python
Related
This is my attempt:
from inputimeout import inputimeout, TimeoutOccurred
import time
import sys
import os
def get_choice(maxtimeout=9):
try:
for remaining in range(maxtimeout, 0, -1):
sys.stdout.write("\r")
sys.stdout.write(
"Want to continue to next item(Y/N)? Defaults to Y in {:2d} seconds...".format(
remaining + 1
)
)
sys.stdout.flush()
time.sleep(1)
c = inputimeout(prompt="", timeout=1)
except TimeoutOccurred:
c = "Y"
return c
The issues with my approach are:
The inputed key is not visible until the timeout completes.
The user has to press enter after his choice.
The user has to wait till timeout completes irrespective of when he entered the value.
There are two problems to solve here to meet your preferred requirements.
First, here's a simple example of how you can use a signal timer to give the user some amount of time to input an answer:
import signal
def input_timeout(*ignore):
raise TimeoutError
signal.signal(signal.SIGALRM, input_timeout)
signal.setitimer(signal.ITIMER_REAL, 10)
try:
inp = input("enter something: ")
signal.setitimer(signal.ITIMER_REAL, 0)
except TimeoutError:
print("you took too long!")
What this does is set a timer to go off after 10 seconds (you can use fractional time if you want, say, 2.5 seconds). If the user hasn't input anything before the timer goes off, it raises the TimeoutError exception, breaking the input prompt. If the user does enter something, it disables the timer by setting it to zero. If the timer does fire and calls input_timeout(), it passes two arguments (the signal number and the interrupted stack frame) but you can usually ignore them, so I've set those arguments as *ignore.
Here's a question that covers this approach in more detail: Python timeout decorator. The reason I didn't just point you to that is so that you could see the bare essence of the process and decide for yourself how to use it. Plus, well, when I pasted the code and tried to use it, it didn't work. You can decide how to proceed as meets your needs.
The second issue is you mention the problem with your approach is the user having to press enter. Here's a question that covers that topic: raw_input in python without pressing enter. In short, there's not a simple built-in solution for that.
In my multi-threaded (2 threads) python program, both threads writes to the console. Main thread accepts commands using input(), another thread fetches some data from server and prints some messages. I have managed to move the input prompt (first thread) to new line by printing '\r' to the console. But I can't find a way to bring back the input that was on half way with that prompt. How can I achieve the correct behavior.
This is a snippet of code from the program:
class GetServerData:
def mainthread(self, interval):
updates = threading.Thread(target=self.getUpdates, args={interval})
updates.start()
self.threadsrunning = True
self.continuemainloop = True
while(self.continuemainloop):
self.promptinput()
command = input()
if(command == "")
self.promptinput()
elif(command=="quit")
self.continueloop = False
print("\rWaiting for all threads to terminate ...")
while(self.threadsrunning)
pass
self.continuemainloop = False
print("Program terminated")
def getUpdates(self, interval)
self.continueloop = True
while(self.continueloop):
#Code for getting data from server
#and print/process the response
#if server responds
time.sleep(15)
print("\rReceived Updates ...") #This prints at the current line
#After this, prompt should be shown
#If something was being typed, it should also be shown here
self.promptinput()
self.threadsrunning = False
def promptinput(self):
print(" >> ", end='', flush=True) #Prompt shows without moving to next line
#TODO : Print current keyboard input which is not finished yet
What should I put after the prompt prints >>, to show unfinished input.
It depends on your terminal.
There are ANSI escape codes to move Forward and Back on a line, if you target such terminals.
Otherwise, there are plenty of libraries to help you do that.
And similar questions on Stack Overflow : this one for example.
I have a function that has a continual loop asking the user for their input using python's builtin input(prompt). I also have a separate thread doing some work, and when a certain condition is met in that thread, the input prompt should change.
So, say at startup the prompt is "Input: " but then in the middle of the thread's work, a condition is met, so the prompt should be switched to "Go Ahead, Type: ". Now if the user doesn't enter anything at that first prompt, but the thread reaches the point where the prompt is switched, then we are still stuck on that first blocking input call that has "Input: " as it's prompt.
# Start the thread that does some work and eventually changes the prompt
myThread.start()
#Start looping to ask for user input, get the prompt from the thread
while True:
userInput = input(myThread.get_prompt())
<do something with input>
I know I can accomplish this with select([stdin],[],[],0.0) to poll stdin before doing a stdin.readline() and then just print the prompt again if 1) we got user input or 2) if the prompt changed.
However, I'd like to find a solution that uses Python's builtin input() function so that I can set tab completion with python's readline module.
I tried playing around with a signal that would basically interrupt input() every couple seconds. With this I would need to make it appear seamless that a new input() call was made without it reprinting the prompt. So something like:
myThread.start()
while True:
userInput = None
signal(SIGALRM, handler)
signal.alarm(3)
try:
userInput = input("\r" + myThread.get_prompt())
except TimeoutError:
pass
finally:
signal.alarm(0)
<do something with input>
def handler(signum, frame):
raise TimeoutError
Which is messy, but then when it times out, and the new input() is called, the current line buffer gets printed but the cursor is at the front of it. So if I type "aaa" then it reprints the "aaa" but the cursor is not at the end of that buffer, it is at the beginning.
Any suggestions?
Update: I can certainly try to play around more with the signal option. It seems like that might be my best option so far. I just can't seem to get the cursor to move to the end of the input buffer if the user has already started typing something. I don't want the user to be aware of the call timing out and being called again.
Ok I figured it out. And it's way better than using the signal alarm to timeout.
In case anyone in the future stumbles across this specific problem, here is essentially what I did:
import threading
import time
import readline
import sys
class MyThread(threading.Thread):
def __init__(self):
super(MyThread, self).__init__()
# Set the initial prompt
self.prompt = "Input: "
def run(self):
time.sleep(6)
# Now we want to change the prompt so that next time input loop asks for prompt it is correct
self.set_prompt("Go ahead, type: ")
# Now overwrite what was there in the prompt
sys.stdout.write("\r" + self.get_prompt())
sys.stdout.flush()
# Get the current line buffer and reprint it, in case some input had started to be entered when the prompt was switched
sys.stdout.write(readline.get_line_buffer())
sys.stdout.flush()
def get_prompt(self):
return self.prompt
def set_prompt(self, new_prompt):
self.prompt = new_prompt
# Create and start thread
myThread = MyThread()
myThread.start()
# Input loop
while True:
userInput = input(myThread.get_prompt())
print("Got: " + userInput)
If you are using tab complete (which I didn't show here), you may notice that if you try to tab complete after the first time that the prompt switches (without accepting any input from the first prompt), then tab complete still thinks we are reading from the first prompt and will reprint that prompt. In that case, you will need to do the following:
readline.set_completion_display_matches_hook(display)
Where display is a function that does something like what our thread had to do to rewrite the correct prompt:
def display(substitution, matches, longest_match_length):
# Print the matches
print('')
buffer = ''
for match in matches:
buffer += match + " "
print(buffer)
# Rewrite the NEW prompt
sys.stdout.write(<prompt>)
sys.stdout.flush()
sys.stdout.write(readline.get_line_buffer())
sys.stdout.flush()
Basically, I want my throw raw input variable to be skipped so that it can still be accessed and referred to later on, but doesn't need a user input to continue, so what happens is if you type "throw brick" within 5 seconds, it will bring you to the outcome print "good job!"
throw = raw_input()
throw_command = ["throw brick"]
import time
me.sleep(5)
if throw in throw_command:
print "good job!"
if throw not in throw_command:
print "GAME OVER"
restart
I know such thing as a goto or jump don't exist in such a structure-based code. So, if anyone could happen to provide an alternative, your assistance would be much appreciated.
I'm not sure I understand what you're trying to do, but it sounds to me like this:
Ask the user for input, but timeout after 5 seconds (your time.sleep(5))
Repeat over and over again (your restart)
If so, it looks like this would be one way to do it: Keyboard input with timeout in Python
I modified the select-based answer with the details of your code:
import sys
import select
throw_command = ["throw brick"]
while True:
print "\nEnter command:"
i, o, e = select.select( [sys.stdin], [], [], 5 ) # times out after 5 seconds
if i: # If input was provided
throw = sys.stdin.readline().strip()
if throw in throw_command:
print "good job!"
break
else:
print "GAME OVER"
else:
print "You didn't enter anything!"
The use of select is a little advanced (ideally, raw_input should have an optional timeout argument) but it's easier than using threads which is the only way I could think of doing this with raw_input.
The use of a while loop instead of your restart attempt at a go-to should be self-explanatory.
I have a program that takes a very long time to run, and I want it to be able to do what it needs to do but then if the user hits a specific key then I want the loop to break at a certain point. Most Q&A's I have seen pertaining to this problem prompt the user to enter something on each iteration of the loop, however I do not want this. I want the loop to run undisturbed until it needs to quit.
The script will be running as an executable in a command prompt (Windows) so I guess the user could just close the window but I want the loop to break at a certain point. For example:
while True:
print "Doing whatever I feel like"
userinput = raw_input()
if userinput == 'q':
break
So this keeps printing until the user enters 'q', but it prompts the user for input each iteration. How can I avoid this?
If you do not need to stop at a specific point, but just to be able to stop it, you could use a try/except with KeyboardInterrupt (Ctrl-C).
try:
while True:
print "Doing whatever I feel like"
except KeyboardInterrupt:
exit
When the user hits CTRL-C it will exit.
Or this:
import msvcrt
while True:
print "Doing whatever I feel like"
if msvcrt.kbhit(): # True if a keypress is waiting to be read.
if msvcrt.getch()=="q": # will not wait for Enter to be pressed
break
Check msvcrt.
Two possible solutions:
1. Press Ctrl-C to close your program. This also works on linux.
2.
while True:
for _ in range(100)
print "Doing whatever I feel like"
userinput = raw_input()
if userinput == 'q':
break
This only asks the user every 100 iterations.
Start a separate Thread to perform the computation that you need to perform while self.flag == False, and the main program can just sit there waiting for the user input. Once the user input is given, set Thread.flag = True, which will stop the Thread. Wait for the Thread to finish and join, then you can exit from the main program as well.