103 votes

Entrée au clavier avec délai d'attente?

Comment demanderiez-vous à l'utilisateur de saisir des données mais en arrêtant après N secondes ?

Google pointe vers un fil de discussion par e-mail à ce sujet sur http://mail.python.org/pipermail/python-list/2006-January/533215.html mais cela ne semble pas fonctionner. Peu importe la déclaration dans laquelle le délai d'attente se produit, que ce soit un sys.input.readline ou un timer.sleep(), j'obtiens toujours :

: [raw_]input expected at most 1 arguments, got 2

ce que sauf erreur ne parvient pas à capturer.

11voto

mike Points 184

La réponse de Paul n'a pas fonctionné. Code modifié ci-dessous qui fonctionne pour moi sur

  • windows 7 x64

  • vanilla CMD shell (par exemple, pas git-bash ou autre shell non-M$)

    -- rien ne fonctionne avec msvcrt dans git-bash apparemment.

  • python 3.6

(Je poste une nouvelle réponse, car éditer directement la réponse de Paul la changerait de python 2.x à 3.x, ce qui semble trop pour une édition (py2 est encore utilisé)

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: # touche_enter
                break
            elif ord(byte_arr) >= 32: #espace_char
                input += "".join(map(chr,byte_arr))
        if len(input) == 0 and (time.time() - start_time) > timeout:
            print("délai dépassé, en utilisant la valeur par défaut.")
            break

    print('')  # nécessaire pour passer à la ligne suivante
    if len(input) > 0:
        return input
    else:
        return default

# et quelques exemples d'utilisation
ans = readInput('Veuillez taper un nom', 'john') 
print( 'Le nom est %s' % ans)
ans = readInput('Veuillez entrer un nombre', 10 ) 
print( 'Le numéro est %s' % ans)

6voto

dylnmc Points 1

J'ai passé une bonne vingtaine de minutes sur cela, donc j'ai pensé que ça valait la peine d'essayer de le mettre ici. Cela construit directement sur la réponse de l'utilisateur137673, cependant. J'ai trouvé qu'il était le plus utile de faire quelque chose comme ceci:

#! /usr/bin/env python

import signal

timeout = None

def main():
    inp = stdinWait("Vous avez 5 secondes pour taper du texte et appuyer sur ... ", "[pas de texte]", 5, "Oh la la! Vous avez manqué de temps!!")
    if not timeout:
        print "Vous avez entré", inp
    else:
        print "Vous n'avez rien entré car je suis très occupé!"

def stdinWait(text, default, time, timeoutDisplay = None, **kwargs):
    signal.signal(signal.SIGALRM, interrupt)
    signal.alarm(time) # définit le temps limite
    global timeout
    try:
        inp = raw_input(text)
        signal.alarm(0)
        timeout = False
    except (KeyboardInterrupt):
        printInterrupt = kwargs.get("printInterrupt", True)
        if printInterrupt:
            print "Interruption clavier"
        timeout = True # Faites ceci pour ne pas obtenir involontairement une entrée s'il n'y en a pas
        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()

5voto

Mechatron Points 51

Le code suivant a fonctionné pour moi.

J'ai utilisé deux threads, un pour obtenir le raw_Input et un autre pour attendre un temps spécifique. Si l'un des threads se termine, les deux threads sont terminés et retournés.

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="Appuyez sur Entrée pour continuer", 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 "\nRéponse :",t 
print time.ctime()

4voto

jorisv92 Points 181

Voici une solution portable et simple en Python 3 utilisant des threads. C'est la seule qui a fonctionné pour moi tout en étant multiplateforme.

Les autres choses que j'ai essayées avaient toutes des problèmes :

  • Utiliser signal.SIGALRM: ne fonctionne pas sur Windows
  • Utiliser l'appel à select: ne fonctionne pas sur Windows
  • Utiliser la terminaison forcée d'un processus (au lieu d'un thread): stdin ne peut pas être utilisé dans un nouveau processus (stdin est automatiquement fermé)
  • Renvoyer stdin vers StringIO et écrire directement sur stdin: écrira toujours sur le stdin précédent si input() a déjà été appelé (voir 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  # Sinon le thread ne sera pas terminé lorsque le programme principal se termine.
            get_input_thread.start()
            get_input_thread.join(timeout=20)
    
            if myClass._input is None:
                print("Aucune entrée n'a été donnée dans les 20 secondes")
            else:
                print("L'entrée donnée était : {}".format(myClass._input))
    
        @classmethod
        def get_input(cls):
            cls._input = input("")
            return

4voto

leondgarse Points 41

Pour Linux, je préférerais la version select de @Pontus. Ici, juste une fonction python3 fonctionne comme read dans le shell:

import sys, select

def timeout_input(prompt, timeout=3, default=""):
    print(prompt, end=': ', flush=True)
    inputs, outputs, errors = select.select([sys.stdin], [], [], timeout)
    print()
    return (0, sys.stdin.readline().strip()) if inputs else (-1, default)

Exécutez

In [29]: timeout_input("Continuer? (O/n)", 3, "o")                                                                                                                                                                  
Continuer? (O/n): 
Out[29]: (-1, 'o')

In [30]: timeout_input("Continuer? (O/n)", 3, "o")                                                                                                                                                                  
Continuer? (O/n): n

Out[30]: (0, 'n')

Et une fonction yes_or_no

In [33]: yes_or_no_3 = lambda prompt: 'n' not in timeout_input(prompt + "? (O/n)", 3, default="o")[1].lower()                                                                                                      

In [34]: yes_or_no_3("Continuer")                                                                                                                                                                                   
Continuer? (O/n): 
Out[34]: True

In [35]: yes_or_no_3("Continuer")                                                                                                                                                                                   
Continuer? (O/n): n

Out[35]: False

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X