Comment inviteriez-vous l'utilisateur à entrer des données, mais au bout de N secondes?
Google pointe un fil de discussion à ce sujet à l'adresse http://mail.python.org/pipermail/python-list/2006-January/533215.html mais cela ne semble pas fonctionner. L'instruction dans laquelle le délai d'attente survient, qu'il s'agisse d'un sys.input.readline ou timer.sleep (), j'obtiens toujours:
<type 'exceptions.TypeError'>: [raw_] entrée attendue au plus 1 argument, en a 2
qui en quelque sorte l'exception ne parvient pas à attraper.
L'exemple auquel vous avez lié est faux et l'exception se produit lors de l'appel du gestionnaire d'alarmes au lieu de la lecture. Mieux vaut essayer ceci:
import signal
TIMEOUT = 5 # number of seconds your want for timeout
def interrupted(signum, frame):
"called when read times out"
print 'interrupted!'
signal.signal(signal.SIGALRM, interrupted)
def input():
try:
print 'You have 5 seconds to type in your stuff...'
foo = raw_input()
return foo
except:
# timeout
return
# set alarm
signal.alarm(TIMEOUT)
s = input()
# disable the alarm after success
signal.alarm(0)
print 'You typed', s
L'utilisation d'un appel sélectif est plus courte et devrait être beaucoup plus portable
import sys, select
print "You have ten seconds to answer!"
i, o, e = select.select( [sys.stdin], [], [], 10 )
if (i):
print "You said", sys.stdin.readline().strip()
else:
print "You said nothing!"
Pas une solution Python, mais ...
J'ai rencontré ce problème avec un script exécuté sous CentOS (Linux), et ce qui a bien fonctionné dans mon cas était simplement d'exécuter la commande Bash "read -t" dans un sous-processus. Un bidouillage dégoûtant brutal, je le sais, mais je me sens assez coupable à quel point cela a fonctionné que je voulais le partager avec tout le monde ici.
import subprocess
subprocess.call('read -t 30', Shell=True)
Tout ce dont j'avais besoin était quelque chose qui attendait 30 secondes à moins que la touche ENTER ne soit enfoncée. Cela a très bien fonctionné.
La réponse de Paul n'a pas bien fonctionné. Code modifié ci-dessous qui fonctionne pour moi
windows 7 x64
Vanilla CMD Shell (par exemple, pas git-bash ou autre shell non-M $)
- rien msvcrt
ne fonctionne dans git-bash semble-t-il.
python 3.6
(Je poste une nouvelle réponse, car éditer la réponse de Paul la changerait directement de python 2.x -> 3.x, ce qui semble trop pour une édition (py2 est toujours 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: # enter_key
break
Elif ord(byte_arr) >= 32: #space_char
input += "".join(map(chr,byte_arr))
if len(input) == 0 and (time.time() - start_time) > timeout:
print("timing out, using default value.")
break
print('') # needed to move to next line
if len(input) > 0:
return input
else:
return default
# and some examples of usage
ans = readInput('Please type a name', 'john')
print( 'The name is %s' % ans)
ans = readInput('Please enter a number', 10 )
print( 'The number is %s' % ans)
Et en voici un qui fonctionne sous Windows
Comme aucun de ces exemples ne fonctionnait sous Windows, j'ai donc fusionné différentes réponses StackOverflow pour obtenir les éléments suivants:
import threading, msvcrt
import sys
def readInput(caption, default, timeout = 5):
class KeyboardThread(threading.Thread):
def run(self):
self.timedout = False
self.input = ''
while True:
if msvcrt.kbhit():
chr = msvcrt.getche()
if ord(chr) == 13:
break
Elif ord(chr) >= 32:
self.input += chr
if len(self.input) == 0 and self.timedout:
break
sys.stdout.write('%s(%s):'%(caption, default));
result = default
it = KeyboardThread()
it.start()
it.join(timeout)
it.timedout = True
if len(it.input) > 0:
# wait for rest of input
it.join()
result = it.input
print '' # needed to move to next line
return result
# and some examples of usage
ans = readInput('Please type a name', 'john')
print 'The name is %s' % ans
ans = readInput('Please enter a number', 10 )
print 'The number is %s' % ans
Le code suivant a fonctionné pour moi.
J'ai utilisé deux threads l'un pour obtenir le raw_Input et un autre pour attendre un moment spécifique . Si l'un des threads se ferme, le thread est terminé et renvoyé.
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="Press Enter to continue", 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 "\nResponse :",t
print time.ctime()
J'ai passé une bonne vingtaine de minutes dessus, alors j'ai pensé que ça valait le coup d'essayer de le dire ici. Il se base directement sur la réponse de user137673. J'ai trouvé très utile de faire quelque chose comme ça:
#! /usr/bin/env python
import signal
timeout = None
def main():
inp = stdinWait("You have 5 seconds to type text and press <Enter>... ", "[no text]", 5, "Aw man! You ran out of time!!")
if not timeout:
print "You entered", inp
else:
print "You didn't enter anything because I'm on a tight schedule!"
def stdinWait(text, default, time, timeoutDisplay = None, **kwargs):
signal.signal(signal.SIGALRM, interrupt)
signal.alarm(time) # sets timeout
global timeout
try:
inp = raw_input(text)
signal.alarm(0)
timeout = False
except (KeyboardInterrupt):
printInterrupt = kwargs.get("printInterrupt", True)
if printInterrupt:
print "Keyboard interrupt"
timeout = True # Do this so you don't mistakenly get input when there is none
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 __== "__main__":
main()
Analogue à Locane pour Windows:
import subprocess
subprocess.call('timeout /T 30')
Voici une solution portable et simple Python 3 utilisant des threads .C'est la seule qui a fonctionné pour moi tout en étant multi-plateforme.
Les autres choses que j'ai essayées avaient toutes des problèmes:
from threading import Thread
class myClass:
_input = None
def __init__(self):
get_input_thread = Thread(target=self.get_input)
get_input_thread.daemon = True # Otherwise the thread won't be terminated when the main program terminates.
get_input_thread.start()
get_input_thread.join(timeout=20)
if myClass._input is None:
print("No input was given within 20 seconds")
else:
print("Input given was: {}".format(myClass._input))
@classmethod
def get_input(cls):
cls._input = input("")
return
Puisque cette question semble servir de cible dupliquée, ici le lien vers ma réponse acceptée dans une question dupliquée.
Caractéristiques
ma solution multiplateforme
def input_process(stdin_fd, sq, str):
sys.stdin = os.fdopen(stdin_fd)
try:
inp = input (str)
sq.put (True)
except:
sq.put (False)
def input_in_time (str, max_time_sec):
sq = multiprocessing.Queue()
p = multiprocessing.Process(target=input_process, args=( sys.stdin.fileno(), sq, str))
p.start()
t = time.time()
inp = False
while True:
if not sq.empty():
inp = sq.get()
break
if time.time() - t > max_time_sec:
break
p.terminate()
sys.stdin = os.fdopen( sys.stdin.fileno() )
return inp