web-dev-qa-db-fra.com

Comment définir une limite de temps sur raw_input

en python, existe-t-il un moyen de compter le temps pendant l'attente d'une entrée utilisateur afin qu'après, par exemple, 30 secondes, la fonction raw_input() soit automatiquement ignorée?

20
calccrypto

La fonction signal.alarm , sur laquelle est basée la solution recommandée par @ jer, est malheureusement uniquement sous Unix. Si vous avez besoin d'une solution multiplate-forme ou spécifique à Windows, vous pouvez vous baser sur threading.Timer , utilisez thread.interrupt_main pour envoyer une KeyboardInterrupt au thread principal à partir du thread du minuteur. C'est à dire.:

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

cela retournera None, que le délai expire après 30 secondes ou que l'utilisateur décide explicitement de frapper control-C pour abandonner quoi que ce soit, mais il semble correct de traiter les deux cas de la même manière (si vous avez besoin de distinguer, vous pouvez utiliser pour le minuteur, une fonction de votre choix qui, avant d’interrompre le fil principal, enregistre quelque part le fait qu’un délai a expiré a, et dans votre gestionnaire pour KeyboardInterrupt accéder à "quelque part" pour distinguer lequel des deux cas eu lieu).

Edit : J'aurais juré que cela fonctionnait mais j'ai dû me tromper - le code ci-dessus omet le timer.start(), et de toute évidence, même avec cela, je ne peux pas le faire fonctionner. pas plus. select.select serait une autre chose évidente à essayer, mais cela ne fonctionnera pas sur un "fichier normal" (y compris stdin) sous Windows. Sous Unix, cela fonctionne sur tous les fichiers, sous Windows, uniquement sur les sockets.

Donc, je ne sais pas comment faire une "entrée brute avec timeout" multiplate-forme. Un fichier spécifique à Windows peut être construit avec une scrutation serrée en boucle msvcrt.kbhit , en effectuant un msvcrt.getche (et en vérifiant s’il s’agit d’un retour indiquant la sortie effectuée. continue d’attendre) et en vérifiant le délai si nécessaire. Je ne peux pas tester parce que je n'ai pas de machine Windows (ce sont tous des Mac et Linux), mais ici, le code non testé je suggérerais:

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

Dans un commentaire, l'OP dit qu'il ne veut pas return None à l'expiration du délai, mais quelle est l'alternative? Relever une exception? Renvoyer une valeur par défaut différente? Quelle que soit l’alternative qu’il recherche, il peut clairement le mettre à la place de mon return None ;-).

Si vous ne voulez pas expirer juste parce que l'utilisateur est en train de taper lentement (plutôt que de ne pas taper du tout! -), vous pouvez recalculer le résultat après chaque saisie de caractère réussie.

28
Alex Martelli

J'ai trouvé une solution à ce problème dans un article de blog . Voici le code de cet article de blog:

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

Remarque: ce code ne fonctionnera que sur les systèmes d'exploitation * nix .

13
jer
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

Comme il est auto-défini ... lancez-le en ligne de commande Invite, j'espère que vous obtiendrez la réponse Lisez ceci python doc vous saurez parfaitement ce qui vient de se passer dans ce code!

4
bilal hanif

La fonction input () est conçue pour attendre que l'utilisateur entre quelque chose (au moins la touche [Entrée]).

Si vous n’êtes pas prêt à utiliser input (), vous trouverez ci-dessous une solution beaucoup plus légère avec tkinter. Dans tkinter, les boîtes de dialogue (et tout widget) peuvent être détruites après un temps donné.

Voici un exemple :

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

Un exemple de malédiction qui prend pour un test mathématique chronométré

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

sous Linux, on pourrait utiliser curses et la fonction getch, son blocage. voir getch ()

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

fonction qui attend la saisie au clavier pendant x secondes (vous devez d’abord initialiser une fenêtre 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
0
Peter Fleix