Как установить ограничение по времени на raw_input
В Python есть ли способ, в ожидании ввода пользователя, считать время, чтобы, например, через 30 секунд, raw_input()
функция автоматически пропускается?
7 ответов
Функция signal.alarm, на которой основано рекомендуемое решение @jer's, к сожалению, доступна только для Unix. Если вам нужно кроссплатформенное или специфичное для Windows решение, вы можете вместо этого использовать threading.Timer, используя thread.interrupt_main для отправки KeyboardInterrupt
в основной поток из потока таймера. То есть:
import thread
import threading
def raw_input_with_timeout(prompt, timeout=30.0):
print prompt,
timer = threading.Timer(timeout, thread.interrupt_main)
astring = None
try:
timer.start()
astring = raw_input(prompt)
except KeyboardInterrupt:
pass
timer.cancel()
return astring
это вернет None независимо от того, истек ли 30-секундный тайм-аут, или пользователь явно решит нажать control-C, чтобы отказаться от ввода чего-либо, но, похоже, нормально обрабатывать два случая одинаково (если вам нужно различить, вы можете использовать для таймера - ваша собственная функция, которая перед прерыванием основного потока записывает где-то факт того, что истекло время ожидания, а в вашем обработчике KeyboardInterrupt
доступ к этому "где-то", чтобы определить, какой из двух случаев произошел).
Редактировать: я мог поклясться, что это работает, но я, должно быть, ошибся - код выше опускает очевидно необходимое timer.start()
и даже с этим я не могу заставить его работать больше. select.select
было бы очевидной попыткой, но в Windows он не будет работать с "обычным файлом" (включая stdin) - в Unix он работает со всеми файлами, в Windows - только с сокетами.
Так что я не знаю, как сделать кросс-платформенный "необработанный ввод с тайм-аутом". Окно, специфичное для Windows, может быть построено с помощью тесного цикла msvcrt.kbhit, выполняя msvcrt.getche
(и проверка, является ли это возвратом, чтобы указать, что выход сделан, в этом случае он выходит из цикла, в противном случае накапливается и продолжает ждать) и проверяет время ожидания, если это необходимо. Я не могу проверить, потому что у меня нет компьютера с Windows (все они - Mac и Linux), но вот непроверенный код, который я бы предложил:
import msvcrt
import time
def raw_input_with_timeout(prompt, timeout=30.0):
print prompt,
finishat = time.time() + timeout
result = []
while True:
if msvcrt.kbhit():
result.append(msvcrt.getche())
if result[-1] == '\r': # or \n, whatever Win returns;-)
return ''.join(result)
time.sleep(0.1) # just to yield to other processes/threads
else:
if time.time() > finishat:
return None
ОП в комментарии говорит, что не хочет return None
по таймауту, но какая альтернатива? Возникает исключение? Возвращаете другое значение по умолчанию? Какую бы альтернативу он ни пожелал, он может поставить ее вместо моего return None
;-).
Если вы не хотите использовать тайм-аут только потому, что пользователь печатает медленно (в отличие от того, чтобы вообще не печатать!-), вы можете пересчитать финиш после каждого успешного ввода символов.
Я нашел решение этой проблемы в блоге. Вот код из этого поста в блоге:
import signal
class AlarmException(Exception):
pass
def alarmHandler(signum, frame):
raise AlarmException
def nonBlockingRawInput(prompt='', timeout=20):
signal.signal(signal.SIGALRM, alarmHandler)
signal.alarm(timeout)
try:
text = raw_input(prompt)
signal.alarm(0)
return text
except AlarmException:
print '\nPrompt timeout. Continuing...'
signal.signal(signal.SIGALRM, signal.SIG_IGN)
return ''
Обратите внимание: этот код будет работать только на *nix ОС.
Функция input() предназначена для ожидания ввода пользователем чего-либо (по крайней мере, клавиши [Enter]).
Если вы не настроены на использование input(), ниже приведено гораздо более легкое решение с использованием tkinter. В tkinter диалоговые окна (и любой виджет) могут быть уничтожены через определенное время.
Вот пример:
import tkinter as tk
def W_Input (label='Input dialog box', timeout=5000):
w = tk.Tk()
w.title(label)
W_Input.data=''
wFrame = tk.Frame(w, background="light yellow", padx=20, pady=20)
wFrame.pack()
wEntryBox = tk.Entry(wFrame, background="white", width=100)
wEntryBox.focus_force()
wEntryBox.pack()
def fin():
W_Input.data = str(wEntryBox.get())
w.destroy()
wSubmitButton = tk.Button(w, text='OK', command=fin, default='active')
wSubmitButton.pack()
# --- optionnal extra code in order to have a stroke on "Return" equivalent to a mouse click on the OK button
def fin_R(event): fin()
w.bind("<Return>", fin_R)
# --- END extra code ---
w.after(timeout, w.destroy) # This is the KEY INSTRUCTION that destroys the dialog box after the given timeout in millisecondsd
w.mainloop()
W_Input() # can be called with 2 parameter, the window title (string), and the timeout duration in miliseconds
if W_Input.data : print('\nYou entered this : ', W_Input.data, end=2*'\n')
else : print('\nNothing was entered \n')
from threading import Timer
def input_with_timeout(x):
def time_up():
answer= None
print 'time up...'
t = Timer(x,time_up) # x is amount of time in seconds
t.start()
try:
answer = input("enter answer : ")
except Exception:
print 'pass\n'
answer = None
if answer != True: # it means if variable have somthing
t.cancel() # time_up will not execute(so, no skip)
input_with_timeout(5) # try this for five seconds
Поскольку он сам по себе определен... запустите его в командной строке, я надеюсь, что вы получите ответ, прочитав этот документ на python, и вы будете совершенно ясно, что только что произошло в этом коде!!
Пример проклятия, который берет для теста по времени математики
#!/usr/bin/env python3
import curses
import curses.ascii
import time
#stdscr = curses.initscr() - Using curses.wrapper instead
def main(stdscr):
hd = 100 #Timeout in tenths of a second
answer = ''
stdscr.addstr('5+3=') #Your prompt text
s = time.time() #Timing function to show that solution is working properly
while True:
#curses.echo(False)
curses.halfdelay(hd)
start = time.time()
c = stdscr.getch()
if c == curses.ascii.NL: #Enter Press
break
elif c == -1: #Return on timer complete
break
elif c == curses.ascii.DEL: #Backspace key for corrections. Could add additional hooks for cursor movement
answer = answer[:-1]
y, x = curses.getsyx()
stdscr.delch(y, x-1)
elif curses.ascii.isdigit(c): #Filter because I only wanted digits accepted
answer += chr(c)
stdscr.addstr(chr(c))
hd -= int((time.time() - start) * 10) #Sets the new time on getch based on the time already used
stdscr.addstr('\n')
stdscr.addstr('Elapsed Time: %i\n'%(time.time() - s))
stdscr.addstr('This is the answer: %s\n'%answer)
#stdscr.refresh() ##implied with the call to getch
stdscr.addstr('Press any key to exit...')
curses.wrapper(main)
Это для более новых версий Python, но я считаю, что он все равно ответит на вопрос. Это создает сообщение пользователю о том, что время истекло, а затем завершает код. Я уверен, что есть способ заставить его пропустить ввод, а не полностью завершить код, но в любом случае это должно, по крайней мере, помочь ...
import sys
import time
from threading import Thread
import pyautogui as pag
#imports the needed modules
xyz = 1 #for a reference call
choice1 = None #sets the starting status
def check():
time.sleep(15)#the time limit set on the message
global xyz
if choice1 != None:#if choice1 has input in it, than the time will not expire
return
if xyz == 1:#if no input has been made within the time limit, then this message will display
pag.confirm(text = 'Time is up!', title = 'Time is up!!!!!!!!!')
sys.exit()
Thread(target = check).start()#starts the timer
choice1 = input("Please Enter your choice: ")
Надеюсь, это помогло :-)
Под linux можно использовать curses и функцию getch, не блокируя ее. см. getch ()
https://docs.python.org/2/library/curses.html
функция, которая ждет ввода с клавиатуры в течение x секунд (сначала нужно инициализировать окно проклятий (win1)!
import time
def tastaturabfrage():
inittime = int(time.time()) # time now
waitingtime = 2.00 # time to wait in seconds
while inittime+waitingtime>int(time.time()):
key = win1.getch() #check if keyboard entry or screen resize
if key == curses.KEY_RESIZE:
empty()
resize()
key=0
if key == 118:
p(4,'KEY V Pressed')
yourfunction();
if key == 107:
p(4,'KEY K Pressed')
yourfunction();
if key == 99:
p(4,'KEY c Pressed')
yourfunction();
if key == 120:
p(4,'KEY x Pressed')
yourfunction();
else:
yourfunction
key=0