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.

0voto

howdoicode Points 310

Certaines des réponses exigent d'appuyer sur la touche Entrée lorsque le délai d'attente expire pour continuer l'exécution de votre code. D'autres semblent être compliquées, et qui plus est, nécessitent toujours d'appuyer sur la touche Entrée après le délai.

J'ai trouvé la réponse dans un autre fil de discussion, qui fonctionne magnifiquement, mais j'ai découvert un inconvénient. J'ai décidé de placer mon code dans une class pour la portabilité.

Note

J'ai dû utiliser [keyboard](https://github.com/boppreh/keyboard) pour simuler la pression de la touche Entrée, car j'avais une autre instruction input() dans mon code. Pour une raison quelconque, l'instruction input() suivante n'apparaissait pas à moins que je n'appuie sur la touche Entrée.

import threading
import keyboard    # https://github.com/boppreh/keyboard

class Utilities:

    # Variable de classe
    response = None

    @classmethod
    def user_input(cls, timeout):

        def question():
            cls.response = input("Entrez quelque chose : ")

        t = threading.Thread(target=question)
        # La propriété Daemon permet à la fonction cible de se terminer après le délai
        t.daemon = True    
        t.start()
        t.join(timeout)

        if cls.response:
            # Faire quelque chose
        else:
            # Faire autre chose
            # Optionnel. À utiliser si vous avez d'autres instructions input() dans votre code
            keyboard.send("enter")

Utilisation

Utilities.user_input(3)

Ceci a été réalisé avec Python 3.8.3 sur Windows 10.

0voto

tobi delbruck Points 81

Voici un autre exemple que python 3.8+ sur linux qui inclut une réponse oui/non avec un retour par défaut sur timeout

import signal
def alarm_handler(signum, frame):
    raise TimeoutError
def input_with_timeout(prompt, timeout=30):
    """ obtenir une entrée avec délai d'attente

    :param prompt: le message à afficher
    :param timeout: délai d'attente en secondes, ou None pour désactiver

    :returns: l'entrée
    :raises: TimeoutError en cas de dépassement du délai
    """
    # configurer le gestionnaire de signal
    if timeout is not None:
        signal.signal(signal.SIGALRM, alarm_handler)
        signal.alarm(timeout) # générer SIGALRM dans `timeout` secondes
    try:
        return input(prompt)
    except TimeoutError as to:
        raise to
    finally:
        if timeout is not None:
            signal.alarm(0) # annuler l'alarme

def yes_or_no(question, default='y', timeout=None):
    """ Obtenir une réponse oui/non avec choix par défaut et optionnellement un délai d'attente

    :param question: message
    :param default: le choix par défaut, c'est-à-dire 'y' ou 'n'
    :param timeout: délai d'attente en secondes, par défaut None

    :returns: True ou False
    """
    if default is not None and (default!='y' and default!='n'):
        log.error(f'mauvaise option pour le choix par défaut : {default}')
        quit(1)
    y='Y' if default=='y' else 'y'
    n='N' if default=='n' else 'n'
    while "la réponse est invalide":
        try:
            to_str='' if timeout is None else f'(Délai {default} en {timeout}s)'
            reply = str(input_with_timeout(f'{question} {to_str} ({y}/{n}): ',timeout=timeout)).lower().strip()
        except TimeoutError:
            log.warning(f'délai expiré, réponse par défaut={default}')
            reply=''
        if len(reply)==0:
            return True if default=='y' else False
        elif reply[0] == 'y':
            return True
        if reply[0] == 'n':
            return False

Exemple d'utilisation dans le code

if yes_or_no(f'modèle {latest_model_folder} existe, démarrer à partir de là ?', timeout=TIMEOUT):
     log.info(f'initialisation du modèle à partir de {latest_model_folder}')
     model = load_model(latest_model_folder)
else:
     log.info('création d'un nouveau modèle vide')
     model = create_model()

0voto

Ryko Points 163

Je utilise un outil externe inputimeout. Le code source est disponible sur github. Je sais que c'est un outil externe mais il est simple et assez pratique. Après avoir installé l'outil, utilisez ce code :

from inputimeout import inputimeout, TimeoutOccurred
try:
    something = inputimeout(prompt='>>', timeout=5)
except TimeoutOccurred:
    something = 'Aucune entrée.'
print(something)

0voto

sharhp Points 191

Étendant la réponse précédente, qui utilise inputimeout, avec une illustration simple

from inputimeout import inputimeout, TimeoutOccurred

def timed_input (user_prompt, timeout=5):
    user_input = ""
    timed_out = False
    try:
        user_input = inputimeout (prompt=user_prompt, timeout=timeout)
    except TimeoutOccurred:
        timed_out = True
    return (timed_out, user_input)

timed_out, user_input = timed_input ("Entrez quelque chose dans les 3 secondes... ", timeout=3)

if timed_out:
    print ("Vous n'avez pas réussi à entrer quelque chose!")
else:
    print (f"Vous avez entré {user_input}")

0voto

Hesam Eskandari Points 86

Ceci est le code que j'ai écrit. En utilisant le multiprocessing, nous pouvons mettre une limite de temps à l'entrée.

from multiprocessing import Queue, Process
from queue import Empty

class ProcessTimedOutException(Exception):
    def __init__(self, message: str):
        self.message: str = message

class Terminal:

    @staticmethod
    def input_with_timeout(message: str = '', timeout: int = 60) -> Tuple[Optional[str], Optional[Exception]]:
        queue = Queue()
        err: Optional[Exception] = None
        user_input: Optional[str] = None
        input_thread = Process(target=Terminal._input_async, args=(queue, message), daemon=True)
        input_thread.start()
        try:
            user_input = queue.get(timeout=timeout)
        except Empty:
            input_thread.terminate()
            err = ProcessTimedOutException(f'process timed out')
        return user_input, err

    @staticmethod
    def _input_async(queue, message: str = ''):
        sys.stdin = open(0)
        user_input = input(message).strip()
        queue.put(user_input)

if __name__ == '__main__':
    input_message: str = 'entrez quelque chose'
    user_input, err = Terminal.input_with_timeout(message=input_message,timeout=60)
    if err is not None:
        raise err
    print(user_input)

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