Ввод клавиатуры с тайм-аутом в Python

Как бы вы предложили пользователю ввести какой-либо вход, но время после N секунд?

Google указывает на поток почты об этом на http://mail.python.org/pipermail/python-list/2006-January/533215.html, но, похоже, это не сработает. Оператор, в котором происходит таймаут, независимо от того, является ли это sys.input.readline или timer.sleep (), я всегда получаю:

<type 'exceptions.TypeError'>: [raw_] ввод ожидался не более 1 аргумента, получил 2

которые каким-то образом за исключением не поймают.

    Пример, с которым вы связаны, неверен, и исключение на самом деле происходит при вызове обработчика ошибок, а не при чтении блоков. Лучше попробуйте это:

    import signal TIMEOUT = 5 # number of seconds your want for timeout def interrupted(signum, frame): "called when read times out" print 'interrupted!' signal.signal(signal.SIGALRM, interrupted) def input(): try: print 'You have 5 seconds to type in your stuff...' foo = raw_input() return foo except: # timeout return # set alarm signal.alarm(TIMEOUT) s = input() # disable the alarm after success signal.alarm(0) print 'You typed', s 

    Использование выделенного вызова короче и должно быть гораздо более переносимым

     import sys, select print "You have ten seconds to answer!" i, o, e = select.select( [sys.stdin], [], [], 10 ) if (i): print "You said", sys.stdin.readline().strip() else: print "You said nothing!" 

    Не решение Python, но …

    Я столкнулся с этой проблемой со сценарием, работающим под CentOS (Linux), и то, что работало для моей ситуации, было просто запущено в команде «read -t» Bash в подпроцессе. Жестокий отвратительный взлом, я знаю, но я чувствую себя достаточно виноватым в том, насколько хорошо он работал, и я хотел поделиться им со всеми здесь.

     import subprocess subprocess.call('read -t 30', shell=True) 

    Все, что мне нужно, было то, что ожидало 30 секунд, если не была нажата клавиша ENTER. Это отлично поработало.

    И вот тот, который работает в Windows

    Я не смог заставить любой из этих примеров работать в Windows, поэтому я объединил несколько разных ответов StackOverflow, чтобы получить следующее:

     import threading, msvcrt import sys def readInput(caption, default, timeout = 5): class KeyboardThread(threading.Thread): def run(self): self.timedout = False self.input = '' while True: if msvcrt.kbhit(): chr = msvcrt.getche() if ord(chr) == 13: break elif ord(chr) >= 32: self.input += chr if len(self.input) == 0 and self.timedout: break sys.stdout.write('%s(%s):'%(caption, default)); result = default it = KeyboardThread() it.start() it.join(timeout) it.timedout = True if len(it.input) > 0: # wait for rest of input it.join() result = it.input print '' # needed to move to next line return result # 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 - import threading, msvcrt import sys def readInput(caption, default, timeout = 5): class KeyboardThread(threading.Thread): def run(self): self.timedout = False self.input = '' while True: if msvcrt.kbhit(): chr = msvcrt.getche() if ord(chr) == 13: break elif ord(chr) >= 32: self.input += chr if len(self.input) == 0 and self.timedout: break sys.stdout.write('%s(%s):'%(caption, default)); result = default it = KeyboardThread() it.start() it.join(timeout) it.timedout = True if len(it.input) > 0: # wait for rest of input it.join() result = it.input print '' # needed to move to next line return result # 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 

    Следующий код работал для меня.

    Я использовал два потока один, чтобы получить raw_Input, а другой – ждать определенного времени. Если какой-либо из потоков выходит, оба потока завершаются и возвращаются.

     def _input(msg, q): ra = raw_input(msg) if ra: q.put(ra) else: q.put("None") return def _slp(tm, q): time.sleep(tm) q.put("Timeout") return def wait_for_input(msg="Press Enter to continue", time=10): q = Queue.Queue() th = threading.Thread(target=_input, args=(msg, q,)) tt = threading.Thread(target=_slp, args=(time, q,)) th.start() tt.start() ret = None while True: ret = q.get() if ret: th._Thread__stop() tt._Thread__stop() return ret return ret print time.ctime() t= wait_for_input() print "\nResponse :",t print time.ctime() - def _input(msg, q): ra = raw_input(msg) if ra: q.put(ra) else: q.put("None") return def _slp(tm, q): time.sleep(tm) q.put("Timeout") return def wait_for_input(msg="Press Enter to continue", time=10): q = Queue.Queue() th = threading.Thread(target=_input, args=(msg, q,)) tt = threading.Thread(target=_slp, args=(time, q,)) th.start() tt.start() ret = None while True: ret = q.get() if ret: th._Thread__stop() tt._Thread__stop() return ret return ret print time.ctime() t= wait_for_input() print "\nResponse :",t print time.ctime() 

    Я потратил около двадцати минут или около того, поэтому я подумал, что стоит сделать это здесь. Тем не менее, это напрямую связано с ответом пользователя137673. Мне было очень полезно сделать что-то вроде этого:

     #! /usr/bin/env python import signal timeout = None def main(): inp = stdinWait("You have 5 seconds to type text and press <Enter>... ", "[no text]", 5, "Aw man! You ran out of time!!") if not timeout: print "You entered", inp else: print "You didn't enter anything because I'm on a tight schedule!" def stdinWait(text, default, time, timeoutDisplay = None, **kwargs): signal.signal(signal.SIGALRM, interrupt) signal.alarm(time) # sets timeout global timeout try: inp = raw_input(text) signal.alarm(0) timeout = False except (KeyboardInterrupt): printInterrupt = kwargs.get("printInterrupt", True) if printInterrupt: print "Keyboard interrupt" timeout = True # Do this so you don't mistakenly get input when there is none inp = default except: timeout = True if not timeoutDisplay is None: print timeoutDisplay signal.alarm(0) inp = default return inp def interrupt(signum, frame): raise Exception("") if __name__ == "__main__": main() 

    Аналогично Locane для окон:

     import subprocess subprocess.call('timeout /T 30') 

    Ответ Павла не совсем сработал. Модифицированный код, ниже которого работает для меня

    • окна 7 x64

    • ванильной CMD-оболочки (например, не git-bash или другой оболочки без M $)

      – ничто msvcrt работает в git-bash.

    • python 3.6

    (Я отправляю новый ответ, потому что редактирование ответа Павла напрямую изменит его из python 2.x -> 3.x, что кажется слишком большим для редактирования (py2 все еще используется)

     import sys, time, msvcrt def readInput( caption, default, timeout = 5): start_time = time.time() sys.stdout.write('%s(%s):'%(caption, default)) sys.stdout.flush() input = '' while True: if msvcrt.kbhit(): byte_arr = msvcrt.getche() if ord(byte_arr) == 13: # enter_key break elif ord(byte_arr) >= 32: #space_char input += "".join(map(chr,byte_arr)) if len(input) == 0 and (time.time() - start_time) > timeout: print("timing out, using default value.") 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) 

    Поздний ответ 🙂

    Я бы сделал что-то вроде этого:

     from time import sleep print('Please provide input in 20 seconds! (Hit Ctrl-C to start)') try: for i in range(0,20): sleep(1) # could use a backward counter to be preeety :) print('No input is given.') except KeyboardInterrupt: raw_input('Input x:') print('You, you! You know something.') 

    Я знаю, что это не одно и то же, но многие проблемы реальной жизни могут быть решены таким образом. (Мне обычно нужен тайм-аут для ввода пользователем, когда я хочу, чтобы что-то продолжало работать, если пользователь не существует в данный момент.)

    Надеюсь, это по крайней мере частично помогает. (Если кто-нибудь все это прочитает :))