【问题标题】:How to set time limit on raw_input如何设置 raw_input 的时间限制
【发布时间】:2011-02-25 09:05:43
【问题描述】:

在 python 中,有没有办法在等待用户输入时计算时间,以便在 30 秒后自动跳过 raw_input() 函数?

【问题讨论】:

标签: python python-2.7 timeout raw-input


【解决方案1】:

不幸的是,@jer 推荐的解决方案所基于的 signal.alarm 函数仅适用于 Unix。如果您需要跨平台或特定于 Windows 的解决方案,则可以将其基于 threading.Timer,而不是使用 thread.interrupt_main 从计时器线程向主线程发送 KeyboardInterrupt。即:

import thread
import threading

def raw_input_with_timeout(prompt, timeout=30.0):
    print(prompt, end=' ')    
    timer = threading.Timer(timeout, thread.interrupt_main)
    astring = None
    try:
        timer.start()
        astring = input(prompt)
    except KeyboardInterrupt:
        pass
    timer.cancel()
    return astring

这将返回 None 无论是 30 秒超时还是用户明确决定按 control-C 以放弃输入任何内容,但以相同的方式处理这两种情况似乎是可以的(如果您需要区分,您可以为计时器使用您自己的功能,在中断主线程之前,在某处记录超时发生的事实,并在KeyboardInterrupt 的处理程序中访问该“某处”到区分这两种情况中的哪一种发生)。

编辑:我本可以发誓这是可行的,但我一定是错的——上面的代码省略了明显需要的timer.start()即使有它我不能让它工作了。 select.select 显然是另一个值得尝试的方法,但它不适用于 Windows 中的“普通文件”(包括标准输入)——在 Unix 中,它适用于 Windows 中的所有文件,仅适用于套接字。

所以我不知道如何进行跨平台“带超时的原始输入”。可以使用紧密循环轮询msvcrt.kbhit,执行msvcrt.getche(并检查它是否是指示输出完成的返回,在这种情况下它会跳出循环,否则会累积并继续等待) 并在需要时检查超时时间。我无法测试,因为我没有 Windows 机器(它们都是 Mac 和 Linux 机器),但这里未经测试的代码我建议:

import msvcrt
import time

def raw_input_with_timeout(prompt, timeout=30.0):
    print(prompt, end=' ')    
    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

评论中的 OP 说他不想在超时时return None,但有什么替代方法?引发异常?返回不同的默认值?无论他想要什么替代品,他都可以清楚地用它代替我的return None;-)。

如果您不想仅仅因为用户输入缓慢而超时(相反,根本不输入!-),您可以在每次成功输入字符后重新计算finishat。

【讨论】:

  • 嗯,我赞成这个,但现在我测试了它,它似乎不起作用:s。您仍然必须按 Enter(Ubuntu Linux 上的 python 2.6.5)。
  • 是的。我现在正在测试你的代码,我将它设置为 5 秒,但就像 catchmeifyoutry 所说,你仍然需要等到按下输入
  • python 线程文档中还有一个有趣的注释:警告:线程与中断的交互奇怪:任意线程将接收到 KeyboardInterrupt 异常。 (当信号模块可用时,中断总是转到主线程。)
  • @calccrypto,如果你想要一个不同于None的默认值,请将其作为参数添加到函数中;我现在已将其重新编码为仅限 Windows(但由于我没有 Windows,因此无法对其进行测试)并完成它以使其在 30 秒内终止,即使用户 正在 缓慢键入 (而不是等待 30 秒不打字,这对我来说似乎是一个更明智的界面)虽然我也提到了如何轻松地获得更理智的行为(你只需要在成功读取每个键入的字符后重置截止日期,因此只有 30 秒 不作为 会导致超时行为)。
  • linux代码不工作超时不工作。
【解决方案2】:

我找到了解决这个问题的方法in a blog post。这是该博客文章中的代码:

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 操作系统

【讨论】:

  • 很酷,但不幸的是,由于某种原因,信号模块没有“SIGALRM”属性
  • @calccrypto,也许你在 Windows 上? signal.SIGALRM 仅适用于 Unix(请参阅我的回答)。
  • 对,对不起,应该注意到它只是 Unix。
  • 这似乎与其他示例有所有相同的问题,即超时到期后,代码执行不会继续。你必须点击进入。有人解决了这个问题吗?
【解决方案3】:

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

【讨论】:

  • 您仍然需要按“确定”来保存您在该对话框中写入的任何内容。
【解决方案4】:
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 doc,您将清楚这段代码中发生了什么!

【讨论】:

  • 这需要用户输入 "enter" .. 我似乎无法让它超时。
  • 我迟到了 3 年,但是:应该使用 raw_input 而不是 input(Python 2 由 print 表示)。在time_up() 中,除非在末尾调用os._exit(1),否则不会取消读取。这可能会产生其他影响,但要摆脱控制台读取并不容易。
【解决方案5】:

一个用于计时数学测试的诅咒示例

#!/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)

【讨论】:

    【解决方案6】:

    在linux下可以使用curses和getch函数,它是非阻塞的。 见 getch()

    https://docs.python.org/2/library/curses.html

    等待键盘输入 x 秒的函数(你必须先初始化一个 curses 窗口(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
    

    【讨论】:

      【解决方案7】:

      这适用于较新的 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: ")
      
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-08-17
        • 2017-12-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多