Как установить ограничение по времени на raw_input

В Python есть ли способ, в ожидании ввода пользователя, считать время, чтобы, например, через 30 секунд, raw_input() функция автоматически пропускается?

7 ответов

Решение

Функция signal.alarm, на которой основано рекомендуемое решение @jer's, к сожалению, доступна только для Unix. Если вам нужно кроссплатформенное или специфичное для Windows решение, вы можете вместо этого использовать threading.Timer, используя thread.interrupt_main для отправки KeyboardInterrupt в основной поток из потока таймера. То есть:

import thread
import threading

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

это вернет None независимо от того, истек ли 30-секундный тайм-аут, или пользователь явно решит нажать control-C, чтобы отказаться от ввода чего-либо, но, похоже, нормально обрабатывать два случая одинаково (если вам нужно различить, вы можете использовать для таймера - ваша собственная функция, которая перед прерыванием основного потока записывает где-то факт того, что истекло время ожидания, а в вашем обработчике KeyboardInterrupt доступ к этому "где-то", чтобы определить, какой из двух случаев произошел).

Редактировать: я мог поклясться, что это работает, но я, должно быть, ошибся - код выше опускает очевидно необходимое timer.start() и даже с этим я не могу заставить его работать больше. select.select было бы очевидной попыткой, но в Windows он не будет работать с "обычным файлом" (включая stdin) - в Unix он работает со всеми файлами, в Windows - только с сокетами.

Так что я не знаю, как сделать кросс-платформенный "необработанный ввод с тайм-аутом". Окно, специфичное для Windows, может быть построено с помощью тесного цикла msvcrt.kbhit, выполняя msvcrt.getche (и проверка, является ли это возвратом, чтобы указать, что выход сделан, в этом случае он выходит из цикла, в противном случае накапливается и продолжает ждать) и проверяет время ожидания, если это необходимо. Я не могу проверить, потому что у меня нет компьютера с Windows (все они - Mac и Linux), но вот непроверенный код, который я бы предложил:

import msvcrt
import time

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

ОП в комментарии говорит, что не хочет return None по таймауту, но какая альтернатива? Возникает исключение? Возвращаете другое значение по умолчанию? Какую бы альтернативу он ни пожелал, он может поставить ее вместо моего return None;-).

Если вы не хотите использовать тайм-аут только потому, что пользователь печатает медленно (в отличие от того, чтобы вообще не печатать!-), вы можете пересчитать финиш после каждого успешного ввода символов.

Я нашел решение этой проблемы в блоге. Вот код из этого поста в блоге:

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 ОС.

Функция 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')
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, и вы будете совершенно ясно, что только что произошло в этом коде!!

Пример проклятия, который берет для теста по времени математики

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

Это для более новых версий 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: ")

Надеюсь, это помогло :-)

Под linux можно использовать curses и функцию getch, не блокируя ее. см. getch ()

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

функция, которая ждет ввода с клавиатуры в течение x секунд (сначала нужно инициализировать окно проклятий (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
Другие вопросы по тегам