Réagir à la frappe d'une touche clavier pour un script en mode console

Le
Francois Lafont
Bonjour à tous,

J'ai un petit programme en mode *console* qui simule une expérience
aléatoire un grand nombre de fois. Le programme fonctionne, mais il peut
mettre un peu de temps (10 minutes) à se terminer car il répète par
exemple 100 000 fois une même simulation. En gros, code, c'est ça :

for k in range(10000):
simule_experience()
affiche_petit_bilan()

J'aimerais que le programme réagisse quand j'appuie sur la barre espace.
Je m'explique.

Je lance le programme, il boucle, il boucle je n'ai pas de prompt sur
la console pendant ce temps bien sûr. Si jamais j'appuie sur SPACE (et
seulement si je fais cela), dès que le programme arrive en début de
boucle, au lieu de simuler une nouvelle expérience, le programme se
stoppe et m'affiche un truc du genre «Pour l'instant 56 257 expériences
simulées, tapez ENTER pour que le programme reprenne son travail ».
Dans ce cas, si j'appuie sur ENTER (et seulement si je fais cela), le
programme reprend ses boucles. Et ainsi de suite

Merci d'avance pour votre aide.


--
François Lafont
Questions / Réponses high-tech
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 2
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Francois Lafont
Le #22752051
Pardon, j'ai oublié de précisé que je suis sous Linux (Ubuntu 10.04),
des fois que ça puisse avoir une importance (même si, tant qu'à faire,
je préférerais que la solution soit portable pour être dans l'esprit de
Python).


--
François Lafont
NicolasP
Le #22752611
Le 03/11/2010 02:03, Francois Lafont a écrit :
Pardon, j'ai oublié de précisé que je suis sous Linux (Ubuntu 10.04),
des fois que ça puisse avoir une importance (même si, tant qu'à faire,
je préférerais que la solution soit portable pour être dans l'esprit de
Python).




Si c'est juste pour vérifier où en est le processus, un affichage toutes les x secondes est possible.
CTRL+C permet de sortir à tout moment du programme.

Nicolas
Avell Diroll
Le #22753261
On 03/11/10 02:00, Francois Lafont wrote:
(snip)
J'ai un petit programme en mode *console* qui simule une expérience
aléatoire un grand nombre de fois. Le programme fonctionne, mais il peut
mettre un peu de temps (10 minutes) à se terminer car il répète par
exemple 100 000 fois une même simulation. En gros, code, c'est ça :

for k in range(10000):
simule_experience()
affiche_petit_bilan()

J'aimerais que le programme réagisse quand j'appuie sur la barre espace.
Je m'explique.
Je lance le programme, il boucle, il boucle... je n'ai pas de prompt sur
la console pendant ce temps bien sûr. Si jamais j'appuie sur SPACE (et
seulement si je fais cela), dès que le programme arrive en début de
boucle, au lieu de simuler une nouvelle expérience, le programme se
stoppe et m'affiche un truc du genre «Pour l'instant 56 257 expériences
simulées, tapez ENTER pour que le programme reprenne son travail... ».
Dans ce cas, si j'appuie sur ENTER (et seulement si je fais cela), le
programme reprend ses boucles. Et ainsi de suite...



Bonjour,

Navré de ne pas répondre à la question exactement, mais je déteste avoir
à réinventer la roue quand il suffit juste d'apprendre à se servir de
son shell.

Tout d'abord pour obtenir un affichage de l'avancement du script (sans
pause, juste une barre d'info qui s'actualise au fur et à mesure des
boucles) je vous propose ce genre de modifications:

###################################################
#!/usr/bin/env python
# -*- coding: utf-8 -*-

from time import sleep
from sys import stdout

def simule_experience():
sleep(1)

def affiche_petit_bilan():
print("That's all folks!")

kmax000

for k in range(kmax):
simule_experience()
stdout.write("r %s sur %s expériences simulées" % (k, kmax))
stdout.flush()

affiche_petit_bilan()
###################################################

Par ailleurs, vous declarez être sous Ubuntu 10.04, votre shell est donc
en toute probabilité le bash.

Le job control en bash se fait à l'aide des commandes fg, bg, jobs et
kill (principalement ces 4 là) et est assisté des interruptions clavier
Ctrl+z et Ctrl+c (entre autres).

Pour mettre en pause un programme (au premier plan) lancé dans un shell
il suffit d'appuyer sur Ctrl+z, le shell rend alors la main à
l'utilisateur, le programme est placé en pause dans le background. Pour
reprendre le calcul il suffit de lancer la commande "fg" (ou fg
numero_du_job si plusieurs programmes ont été mis en pause).

Ceci fonctionne avec n'importe quel programme lancé dans un shell bash
(à moins que le programme en question patouille avec les interruptions
clavier).

Pour ce qui est de la portabilité, bash est disponible sur de nombreuses
plateformes (souvent utilisé comme shell par défaut sous linux et sous
mac et autre *nix, il a été également porté sous windows (cygwin, mingw,
...)) ... et tous les shells décents proposent ce genre de fonctionnalités.


Bonne continuation.

Ju
--
Those who do not understand Unix are condemned to reinvent it, poorly.
-Henry Spencer
Francois Lafont
Le #22754971
Le 03/11/2010 09:25, NicolasP a écrit :

Si c'est juste pour vérifier où en est le processus, un affichage toutes
les x secondes est possible.



Oui, mais moi je ne veux pas un affichage toutes les secondes. Je veux
un affichage quand je le demande seulement.

CTRL+C permet de sortir à tout moment du programme.



Certes, mais je ne veux pas sortir du programme.

--
François Lafont
Alain Ketterlin
Le #22755901
Francois Lafont
[...]
J'aimerais que le programme réagisse quand j'appuie sur la barre esp ace.
Je m'explique.

Je lance le programme, il boucle, il boucle... je n'ai pas de prompt sur
la console pendant ce temps bien sûr. Si jamais j'appuie sur SPACE ( et
seulement si je fais cela), dès que le programme arrive en débu t de
boucle, au lieu de simuler une nouvelle expérience, le programme se
stoppe et m'affiche un truc du genre «Pour l'instant 56 257 expà ©riences
simulées, tapez ENTER pour que le programme reprenne son travail... ».
Dans ce cas, si j'appuie sur ENTER (et seulement si je fais cela), le
programme reprend ses boucles. Et ainsi de suite...



Jette un oeil au module termios, et aussi à tty (c'est comme cela qu'on
gère les terminaux sous Unix/POSIX). tty.setraw devrait te convenir,
mais tu auras (je crois) besoin de termios pour remettre les choses en
état. Un exemple de code à
http://code.activestate.com/recipes/134892-getch-like-unbuffered-character- reading-from-stdin/

-- Alain.
Francois Lafont
Le #22756071
Le 03/11/2010 11:34, Avell Diroll a écrit :

Navré de ne pas répondre à la question exactement, mais je déteste avoir
à réinventer la roue quand il suffit juste d'apprendre à se servir de
son shell.



À mon petit niveau, ça peut être parfois intéressant de réinventer la
roue (enfin une petite roulette). Mais je comprends bien votre logique.

Tout d'abord pour obtenir un affichage de l'avancement du script (sans
pause, juste une barre d'info qui s'actualise au fur et à mesure des
boucles) je vous propose ce genre de modifications:

[code]



Ok, merci pour l'exemple, qui m'a permis de découvrir cette petite
"siouxerie" :

#----------------------
print "Je suis làrEt moi je te remplace !"






Et moi je te remplace !
#----------------------

Même si effectivement cela ne répond pas à ma question, cet exemple est
une alternative très acceptable pour moi. Merci bien. :-)

Par ailleurs, vous declarez être sous Ubuntu 10.04, votre shell est donc
en toute probabilité le bash.

Explications sur fg, bg etc. [couic]



Merci pour vos explications. J'avoue que je n'utilise guère ces
histoires de "job control". Ceci étant, toutes ces fonctionnalités ne
permettent pas de faire ce que j'avais demandé initialement, donc dans
un sens, je ne souhaite pas tant que ça réinventer la roue. ;-)


--
François Lafont
Pierre Maurette
Le #22758941
Francois Lafont, le 11/3/2010 a écrit :
Bonjour à tous,

J'ai un petit programme en mode *console* qui simule une expérience
aléatoire un grand nombre de fois. Le programme fonctionne, mais il peut
mettre un peu de temps (10 minutes) à se terminer car il répète par
exemple 100 000 fois une même simulation. En gros, code, c'est ça :

for k in range(10000):
simule_experience()
affiche_petit_bilan()

J'aimerais que le programme réagisse quand j'appuie sur la barre espace.
Je m'explique.

Je lance le programme, il boucle, il boucle... je n'ai pas de prompt sur
la console pendant ce temps bien sûr. Si jamais j'appuie sur SPACE (et
seulement si je fais cela), dès que le programme arrive en début de
boucle, au lieu de simuler une nouvelle expérience, le programme se
stoppe et m'affiche un truc du genre «Pour l'instant 56 257 expériences
simulées, tapez ENTER pour que le programme reprenne son travail... ».
Dans ce cas, si j'appuie sur ENTER (et seulement si je fais cela), le
programme reprend ses boucles. Et ainsi de suite...



Basé sur les mêmes techniques que d'autres réponses, l'idée est un
thread qui tourne jusqu'à ce que l'espace soit tapé au clavier. Il
suffit de tester si le thread est encore actif à des endroits bien
choisis du programme.
Testé sur Windows 7 et Ubuntu 10.10:

<CODE>
#! /usr/bin/env python
# -*- coding: utf-8 -*-
'''
Created on 4 nov. 2010

@author: pierre
'''
import sys, os, threading, time

def kbThreah():
if os.name == 'posix':
import termios, fcntl
fd = sys.stdin.fileno()

oldterm = termios.tcgetattr(fd)
newattr = termios.tcgetattr(fd)
newattr[3] = newattr[3] & ~termios.ICANON & ~termios.ECHO
termios.tcsetattr(fd, termios.TCSANOW, newattr)

oldflags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
fonction = 'sys.stdin.read(1)'
else:
import msvcrt
fonction = 'msvcrt.getch()'

c = ''

try:
while c != ' ':
try:
c = eval(fonction)
except IOError:
pass
except KeyboardInterrupt:
print u'Arrêt utilisateur'
finally:
if os.name == 'posix':
termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)



def processKB():
global kbth
if kbth.isAlive():
return
else:
print 'm=%d, n=%d' % (m, n)
while True:
_ = raw_input(u'Continuer? (y, Y, o, O pour oui, '
+ 'n ou N pour stopper le programme) -> ')[:1]
if _ in ('y', 'Y', 'o', 'O'):
break
elif _ in ('n', 'N'):
sys.exit()
kbth = threading.Thread(None, kbThreah)
kbth.start()
print u'C'est reparti mon kiki...'

def uneOperationBasique(seconds):
time.sleep(seconds)

kbth = threading.Thread(None, kbThreah)
kbth.start()
print u'C'est parti mon kiki...'

m = n = 0
while True:
m += 1
for _ in range(100):
n += 1
uneOperationBasique(0.001)
processKB()
</CODE>

--
Pierre Maurette
Francois Lafont
Le #22768101
Le 03/11/2010 21:21, Alain Ketterlin a écrit :

Jette un oeil au module termios, et aussi à tty (c'est comme cela qu'on
gère les terminaux sous Unix/POSIX). tty.setraw devrait te convenir,
mais tu auras (je crois) besoin de termios pour remettre les choses en
état.



Merci pour les indications. J'ai déjà jeté un œil, ça n'a pas l'air
facile. :-)


--
François Lafont
Francois Lafont
Le #22768091
Le 04/11/2010 15:14, Pierre Maurette a écrit :

Basé sur les mêmes techniques que d'autres réponses, l'idée est un
thread qui tourne jusqu'à ce que l'espace soit tapé au clavier. Il
suffit de tester si le thread est encore actif à des endroits bien
choisis du programme.



Merci beaucoup Pierre pour votre code, qui, pour l'avoir testé, fait
exactement ce que j'ai demandé. Il ne me reste plus qu'à l'étudier (en
l'état je suis loin de le maîtriser). En plus, je vois qu'il fait
intervenir termios comme le suggérait Alain.

Merci encore.

--
François Lafont
Alain Ketterlin
Le #22769301
Francois Lafont
Merci beaucoup Pierre pour votre code, qui, pour l'avoir testé, fait
exactement ce que j'ai demandé. Il ne me reste plus qu'à l'à ©tudier (en
l'état je suis loin de le maîtriser).



Le code de Pierre fait effectivement ce qu'il faut faire. J'ai juste une
remarque sur l'utilisation à mon avis superflue de eval(). Il vaut
peut-être mieux utiliser :

if os.name == 'posix':
[...]
fonction = lambda : sys.stdin.read(1)
# ou simplement def fonction(): return sys.stdin.read(1)
else:
[...]
fonction = msvcrt.getch

et plus bas utiliser fonction comme un "callable" normal

c = fonction()

Pour la partie termios, bonne lecture du man :-)

-- Alain.
Publicité
Poster une réponse
Anonyme