タイムアウトのあるキーボード入力?
-
20-09-2019 - |
質問
ユーザーに入力を求めるプロンプトが表示され、N 秒後にタイムアウトになる場合はどうすればよいでしょうか?
Google は、それに関するメール スレッドを次の場所に示しています。 http://mail.python.org/pipermail/python-list/2006- January/533215.html しかし、うまくいかないようです。タイムアウトが発生するステートメント (ステートメントがタイムアウトであるかどうかは関係ありません) sys.input.readline
または timer.sleep()
, 、私はいつも次のようになります:
<type 'exceptions.TypeError'>: [raw_]input expected at most 1 arguments, got 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) で実行しているスクリプトでこの問題に遭遇しましたが、私の状況ではうまくいったのは、サブプロセスで Bash の「read -t」コマンドを実行するだけでした。残忍で嫌なハッキングであることは承知していますが、それがうまく機能したことに十分な罪悪感を感じているので、ここにいるみんなと共有したいと思いました。
import subprocess
subprocess.call('read -t 30', shell=True)
必要なのは、ENTER キーが押されない限り 30 秒間待機する機能だけでした。これはうまくいきました。
そして、ここで、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
ポールの答えはうまくいきませんでした。私にとって機能する以下の変更されたコード
ウィンドウズ7x64
バニラ CMD シェル (例: ない git-bash またはその他の非 M$ シェル)
- 何もない
msvcrt
git-bash で動作するようです。Python 3.6
(Paul の回答を直接編集すると 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)
私はこれで良い20分ほどを費やしたので、私はそれがここにこれを入れて試してみる価値だと思いました。それは直接かかわらず、user137673の答えのオフに構築されます。私はそれが最も有用なこのような何かを見つけます:
#! /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()
次のコードは、私のために働いています。
私は1つがraw_Inputを取得するには、別の特定の時間を待つために2つのスレッドを使用していました。 スレッドが終了するのいずれかの場合は、スレッドの両方が終了して返されます。
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()
Locaneの窓のために類似します:
import subprocess
subprocess.call('timeout /T 30')
これは、スレッドを使用した移植可能でシンプルな Python 3 ソリューションです。これは、クロスプラットフォームで動作する唯一の方法です。
私が試した他のことはすべて問題がありました:
- signal.SIGALRM の使用:Windows では動作しない
- 選択呼び出しの使用:Windows では動作しない
- (スレッドの代わりに) プロセスの強制終了を使用する:stdin は新しいプロセスでは使用できません (stdin は自動的に閉じられます)
- stdin を StringIO にリダイレクトし、stdin に直接書き込みます。input() がすでに呼び出されている場合でも、以前の stdin に書き込みます (「 https://stackoverflow.com/a/15055639/9624704)
from threading import Thread
class myClass:
_input = None
def __init__(self):
get_input_thread = Thread(target=self.get_input)
get_input_thread.daemon = True # Otherwise the thread won't be terminated when the main program terminates.
get_input_thread.start()
get_input_thread.join(timeout=20)
if myClass._input is None:
print("No input was given within 20 seconds")
else:
print("Input given was: {}".format(myClass._input))
@classmethod
def get_input(cls):
cls._input = input("")
return
私のクロスプラットフォームソリューション
def input_process(stdin_fd, sq, str):
sys.stdin = os.fdopen(stdin_fd)
try:
inp = input (str)
sq.put (True)
except:
sq.put (False)
def input_in_time (str, max_time_sec):
sq = multiprocessing.Queue()
p = multiprocessing.Process(target=input_process, args=( sys.stdin.fileno(), sq, str))
p.start()
t = time.time()
inp = False
while True:
if not sq.empty():
inp = sq.get()
break
if time.time() - t > max_time_sec:
break
p.terminate()
sys.stdin = os.fdopen( sys.stdin.fileno() )
return inp
後半の答え:)
私はこのような何かをするだろう
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.')
私は、これは同じですが、多くの現実の問題がこのように解決することができないことを知っています。 (私は、もしユーザーがいないが、現時点では実行を継続するために何かをしたいとき、私は通常、ユーザー入力のタイムアウトを必要としています。)
が、これは、少なくとも部分的にお役に立てば幸いです。 (誰がとにかくそれを読み取る場合:))