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

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


person calccrypto    schedule 29.05.2010    source источник
comment
связанные: Python 3 Timed Input / 15528939   -  person n611x007    schedule 13.03.2015
comment
связанные: Ввод с клавиатуры с тайм-аутом в Python / 1335507   -  person n611x007    schedule 13.03.2015
comment
связанные: raw_input и timeout / 3471461   -  person n611x007    schedule 13.03.2015


Ответы (7)


Функция signal.alarm, в которой @ jer рекомендуемое решение основано, к сожалению, только для 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, но он не будет работать с «обычным файлом» (включая stdin) в Windows - в 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, 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 после каждого успешного ввода символа.

person Alex Martelli    schedule 29.05.2010
comment
хм, я проголосовал за это, но теперь, когда я его тестирую, похоже, что это не работает: s. Вам все равно нужно нажать Enter (python 2.6.5 в Ubuntu Linux). - person catchmeifyoutry; 29.05.2010
comment
Ага. Я тестирую ваш код прямо сейчас, и я установил его на 5 секунд, но, как сказал catchmeifyoutry, вам все равно придется ждать, пока не будет нажата клавиша ввода - person calccrypto; 29.05.2010
comment
в документации по потоку Python есть интересное примечание: Предостережение: потоки странным образом взаимодействуют с прерываниями: исключение KeyboardInterrupt будет получено произвольным потоком. (Когда сигнальный модуль доступен, прерывания всегда переходят к основному потоку.) - person catchmeifyoutry; 29.05.2010
comment
так что есть еще идеи о том, как это сделать? Я думаю, что Алекс Мартелли меня немного неправильно понял: я пытаюсь сделать что-то вроде этих предупреждений кнопок Windows. после обратного отсчета input () автоматически прекращает ожидание и запускает остальную часть программы со значением по умолчанию. Я не хочу ответа "Нет" после часа ожидания. Я вообще не хочу ждать более 30 секунд - person calccrypto; 29.05.2010
comment
@calccrypto, если вы хотите, чтобы значение по умолчанию отличалось от None, добавьте его в качестве аргумента функции; Теперь я перекодировал его как только для Windows (но не могу протестировать, так как у меня нет Windows) и сделал так, чтобы он завершился через 30 секунд, даже если пользователь медленно набирает ( вместо того, чтобы ждать 30 секунд без набора текста, что мне кажется гораздо более разумным интерфейсом), хотя я также упоминаю, как легко перейти к более разумному поведению (вам просто нужно сбросить крайний срок после того, как каждый набранный символ будет успешно прочитан , поэтому всего 30 секунд бездействия приведет к тайм-ауту). - person Alex Martelli; 29.05.2010
comment
Я хочу просто подождать 30 секунд бездействия подряд. если ничего не напечатано, используется значение по умолчанию. если что-то набрано, но ввод не нажимается в течение 30 секунд, используется значение по умолчанию, а новый код не работает в Windows, значение по умолчанию не имеет значения. просто скажем, что значение по умолчанию - 3,1415, и ввод ожидает его изменения. и, пожалуйста, извините за мою глупость. мне никогда раньше не требовалось время и нить - person calccrypto; 29.05.2010
comment
Итак, код, который я дал (второй, специфичный для Windows), должен работать, просто изменив return None на return 3.1415, нет? - person Alex Martelli; 29.05.2010
comment
подожди. это была опечатка. вы написали kbkit, а не kbhit. работает!!! Благодарность!!! - person calccrypto; 29.05.2010
comment
Ах да, вот что получается из непроверенного кода, позвольте мне исправить опечатку. - person Alex Martelli; 29.05.2010
comment
подождите .. где используется строка приглашения? - person calccrypto; 29.05.2010
comment
@calccrypto, явно print prompt,, который я пропустил - я только что отредактировал ответ, так что теперь он там (я не уверен, нужен ли вам sys.stdout.flush() после него, а у меня нет Windows-машины, чтобы попробовать; - ). - person Alex Martelli; 30.05.2010
comment
У меня вопрос: второй код вернет все, что введено в консоль, без нажатия Enter !!! Правильно? мне нужно, чтобы ввод принимался только при нажатии клавиши Enter, в противном случае, даже если что-то на консоли не принимается, и должно появиться сообщение Time up без необходимости нажимать Enter. Я использовал TRY / Except, но код останавливается сразу после !!! любое предложение?!!! - person msc87; 24.02.2014
comment
Код linux не работает, тайм-аут не работает. - person sidney; 22.05.2014

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

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.

person jer    schedule 29.05.2010
comment
круто, но к сожалению по какой-то причине в сигнальном модуле нет атрибута SIGALRM - person calccrypto; 29.05.2010
comment
@calccrypto, может быть, у вас Windows? signal.SIGALRM предназначен только для Unix (см. мой ответ). - person Alex Martelli; 29.05.2010
comment
Правильно, извините, надо было заметить, что это был только Unix. - person jer; 29.05.2010
comment
Похоже, здесь возникают те же проблемы, что и в других примерах, а именно, по истечении тайм-аута выполнение кода не продолжается. Вы должны нажать Enter. Кто-нибудь решил эту проблему? - person MydKnight; 19.06.2015

Функция 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')
person madpentiste    schedule 19.06.2015
comment
вам все равно нужно нажать ОК, чтобы сохранить все, что вы напишете в этом диалоговом окне. - person sync11; 30.08.2017

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

person bilal hanif    schedule 17.10.2012
comment
Это требует, чтобы пользователь ввел ввод .. Кажется, я не могу получить тайм-аут. - person Illusionist; 17.07.2013
comment
Я опоздал на 3 года, но: raw_input следует использовать, а не input (Python 2 обозначен print). В time_up() чтение не будет отменено, если в его конце не будет вызван os._exit(1). Это может иметь другие последствия, но избавиться от этого консольного чтения непросто. - person cdarke; 26.08.2015

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

#!/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)
person dvrhax    schedule 11.01.2017

под 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
person Peter Fleix    schedule 09.10.2016

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

person Maxgoodman07    schedule 17.05.2021