代码之家  ›  专栏  ›  技术社区  ›  Pablo Fernandez

python中带超时的键盘输入

  •  34
  • Pablo Fernandez  · 技术社区  · 15 年前

    您将如何提示用户输入一些信息,但在n秒后超时?

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

    <类型“exceptions.typeerror”>:[原始输入]最多需要1个参数,得到2个

    不知怎的,除了没抓住。

    11 回复  |  直到 15 年前
        1
  •  25
  •   user137673    15 年前

    您链接到的示例是错误的,异常实际上是在调用报警处理程序而不是在读取块时发生的。最好试试这个:

    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
    
        2
  •  76
  •   Pontus    14 年前

    使用select调用时间更短,而且应该更便于携带。

    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!"
    
        3
  •  10
  •   Locane    10 年前

    不是Python解决方案,但…

    我使用一个在CentOS(Linux)下运行的脚本来处理这个问题,而在我的情况下,只需要在子进程中运行bash“read-t”命令就行了。我知道,这是一个残忍、恶心的黑客行为,但我对它的效果感到非常内疚,我想和这里的每个人分享它。

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

    我只需要等待30秒,除非按Enter键。这很管用。

        4
  •  5
  •   Paul    14 年前

    这是一个在窗户上工作的

    我还不能让这些例子中的任何一个在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 
    
        5
  •  5
  •   some bits flipped Guffa    8 年前

    保罗的回答不太管用。下面的修改代码适用于我

    • Windows 7 X64

    • 香草命令壳(例如, Git Bash或其他非M$shell)

      ——什么也没有 msvcrt 在Git-Bash中工作。

    • Python 3.6

    (我发布了一个新的答案,因为直接编辑保罗的答案会将其从python 2.x改为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) 
    
        6
  •  3
  •   Mechatron    7 年前

    以下代码对我有用。

    我使用了两个线程,一个获取原始输入,另一个等待特定的时间。 如果有任何线程退出,则两个线程都将终止并返回。

    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()
    
        7
  •  2
  •   dylnmc    10 年前

    我花了大约二十分钟的时间在这上面,所以我觉得把它放在这里值得一试。不过,它直接建立在用户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()
    
        8
  •  2
  •   Petter Friberg Onceler    8 年前

    类似于locane for windows:

    import subprocess  
    subprocess.call('timeout /T 30')
    
        9
  •  1
  •   jorisv92    6 年前

    这里是一个使用线程的可移植的简单的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
    
        10
  •  0
  •   Darkonaut    6 年前

    因为这个问题似乎是一个重复的目标, here 重复问题中我接受的答案的链接。

    特征

    • 独立于平台(Unix/Windows)。
    • 仅限stdlib,无外部依赖项。
    • 仅线程,无子进程。
    • 超时时立即中断。
    • 超时时清除Prompter关闭。
    • 在时间范围内可能有无限的输入。
    • 易扩展promptmanager类。
    • 程序可以在超时后恢复,在不重新启动程序的情况下可以多次运行Prompter实例。
        11
  •  -4
  •   try    12 年前

    迟了的回答:)

    我会这样做:

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

    我知道这是不一样的,但很多现实生活中的问题可以通过这种方式解决。(如果用户现在不在,我通常需要在需要继续运行某些内容时超时用户输入。)

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

    推荐文章