键盘输入在Python中超时

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了键盘输入在Python中超时相关的知识,希望对你有一定的参考价值。

你会如何提示用户输入一些信息但是在N秒后超时?

谷歌在http://mail.python.org/pipermail/python-list/2006-January/533215.html指向一个关于它的邮件线程,但似乎没有用。超时发生的语句,无论是sys.input.readline还是timer.sleep(),我总是得到:

<type'exception.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
另一答案

我的跨平台解决方案

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.')

我知道这不一样,但许多现实生活中的问题都可以通过这种方式解决。 (如果用户现在没有在那里继续运行,我通常需要超时用户输入。)

希望这至少部分有帮助。 (如果有人再读它:))

另一答案

使用选择呼叫更短,并且应该更加便携

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 
另一答案

保罗的答案并不奏效。修改后的代码对我有用

  • Windows 7 x64
  • vanilla CMD shell(例如,不是git-bash或其他非M $ shell) - 没有任何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分钟左右的时间,所以我觉得值得一试把它放在这里。不过,它直接建立在用户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()
另一答案

以下代码为我工作。

我使用两个线程来获取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 "
Response :",t 
print time.ctime()
另一答案

类似于Locane的Windows:

import subprocess  
subprocess.call('timeout /T 30')
另一答案

这是一个使用线程的可移植且简单的Python 3解决方案。这是跨平台时唯一适合我的方法。

我尝试过的其他事情都有问题:

  • 使用signal.SIGALRM:不在Windows上运行
  • 使用select call:不在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_thr

以上是关于键盘输入在Python中超时的主要内容,如果未能解决你的问题,请参考以下文章

在python中,如何更新CSV文件中超过5K行的值?

睡觉时忽略python中的键盘输入

从片段导航到另一个片段时隐藏键盘

Python:无重复延迟的键盘输入

底部在颤动中超载了 213 像素

在片段替换上显示/隐藏 Android 软键盘