Contenu | Rechercher | Menus

Annonce

DVD, clés USB et t-shirts Ubuntu-fr disponibles sur la boutique En Vente Libre

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 24/05/2021, à 16:00

DonutMan75

[RESOLU] [python] lire un seul caractère sur l'entrée standard

Bonjour à tous,

je développe un petit programme en Python 3.X et à un moment je souhaite demander à l'utilisateur d'appuyer soit sur "flèche haut" soit sur "flèche bas" et agir en fonction de son action.
Il ne faudrait pas que l'utilisateur ait à appuyer sur "entrée" après avoir choisi la flèche. Confusément, je cherche l'équivalent d'un getchar() classique quoi..

En cherchant sur le net, je suis rapidement tombé sur ce fil qui préconise d'utiliser sys.stdin.read(1) pour lire UN caractère sur l'entrée standard. (j'ai royalement ignoré la partie sur la cross-compatibilité Windows, nous sommes entre gens bien élevés)

Mais voilà, je n'arrive pas à obtenir le bon comportement.... et selon le contexte de lancement du code (via Spyder ou via le terminal), le comportement change !

Mon code minimal :

import sys

while True:
    sys.stdin.flush() # Je ne souhaite pas être pollué par ce qu'il y aurait dans le buffer au moment où je demande d'appuyer sur une touche
    c = sys.stdin.read(1)
    
    if c == ' ':
        break
    else:
        print('- read : %s' % c)

Comportement avec Spyder : j'ai ça qui s'affiche en boucle sans que j'appuie sur une touche !

- read : 
- read : 
- read : 
- read : 
- read : 
- read : 
- read : 
- read : 
- read : 
- read : 
...

Comportement avec le terminal : l'appel est bien bloquant quand j'exécute le script sans rien envoyer sur l'entrée standard.

$ python test_char.py
<rien...>

En revanche, je suis obligé de taper "entrée" pour que ce soit pris en compte...

$ python test_char.py 
abc<Entrée>
- read : a
- read : b
- read : c
- read : 

Qu'en pensez-vous ?

Merci par avance smile

D.

Dernière modification par DonutMan75 (Le 25/05/2021, à 10:57)

Hors ligne

#2 Le 24/05/2021, à 18:47

DonutMan75

Re : [RESOLU] [python] lire un seul caractère sur l'entrée standard

Alors, une première ébauche de réponse.

Pour commencer, getchar() tel qu'il est défini dans stdio.h attend un retour à la ligne ou un EOF pour traiter toute nouvelle arrivée sur stdin.

Donc en Python et avec sys.stdin.read(1), je viens bien récupérer exactement un caractère.. L'appel sera bloquant tant que le flux stdin est vide.
Mais (et c'est là que ça coince), pour le remplir je dois taper sur mon clavier (normal...) ET appuyer sur <Entrée> pour envoyer le tout sur stdin.
Après cela, l'appel à sys.stdin.read(1) reverra le 1ere caractère que j'ai envoyé.

Pour obtenir le comportement souhaité, il est possible d'utiliser la commande stty en amont du programme pour modifier le comportement du terminal

$ python test_char.py 
abc<Entrée>
- read : a
- read : b
- read : c
- read : 
$ stty -icanon
$ python test_char.py # Cette fois-ci j'obtiens bien le comportement attendu ! 
a- read : a
b- read : b
c- read : c
$ stty sane # on rétablit le comportement par défaut

A ce stade, j'ai un peu avancé mais ce n'est pas très élégant de devoir passer par stty pour obtenir ce que je souhaite... Je continue à creuser.
Par ailleurs, je n'ai toujours pas bien compris pourquoi le même programme exécuté via Spyder donnait un comportement différent...

D.

Hors ligne

#3 Le 25/05/2021, à 07:18

pseudofab

Re : [RESOLU] [python] lire un seul caractère sur l'entrée standard

Bonjour,
Remarque:
Tu utilises python comme on utiliserait bash. roll
Avec python, tu disposes notamment de tkinter, une interface graphique qui te permet de gérer les événement claviers. wink
Exemple de mise en oeuvre:

import tkinter as tk
root = tk.Tk()
label = tk.Label(root, text="Appuyer flèche up/down", font=("helvetica", 30))
label.grid()
def action_up(event):
        label.config(text="Tu viens d'appuyer sur up!")
def action_down(event):
        label.config(text="Tu viens d'appuyer sur down!")

root.bind("<Up>", action_up)
root.bind('<Down>', action_down)
root.mainloop

# Là pour le plaisir de découvrir ...
# Envie d'apprendre Bash ou Python? Pensez aux MOOC ---> https://forum.ubuntu-fr.org/viewtopic.php?id=2030646

Hors ligne

#4 Le 25/05/2021, à 10:57

DonutMan75

Re : [RESOLU] [python] lire un seul caractère sur l'entrée standard

Hello pseudofab !
Merci pour ton retour, c'est exactement ce que je cherchais à faire !
Je n'ai jamais vraiment utilisé tkinter, c'est l'occasion de m'y mettre smile

Bonne journée à tous,

D.

Hors ligne

#5 Le 26/05/2021, à 09:46

pseudofab

Re : [RESOLU] [python] lire un seul caractère sur l'entrée standard

DonutMan75 a écrit :

Je n'ai jamais vraiment utilisé tkinter, c'est l'occasion de m'y mettre smile

Je ne peux que t'y encourager... Tu ne regretteras pas le temps investi à son étude . Tkinter permet de faire un tas de trucs , y compris des jeux (tkinter.canvas est très puissant...)
À partir du lien ci-joint , tu trouveras une ressource pdf sur tkinter écrite en bon français et relativement complète...
http://ateliers.mse.free.fr/tkinter/page_tkinter.html
à plus smile


# Là pour le plaisir de découvrir ...
# Envie d'apprendre Bash ou Python? Pensez aux MOOC ---> https://forum.ubuntu-fr.org/viewtopic.php?id=2030646

Hors ligne

#6 Le 14/07/2021, à 18:25

CasseTaTélé_isback

Re : [RESOLU] [python] lire un seul caractère sur l'entrée standard

Bonjour,
@pseudofab
j'ai copié ton example dans un fichier mais ça marche pas, ça donne ça quand je tape sur des touche dont (up et down) :

$ ./clavier6.py 
^[[A^[[B^[[5~^[[6~+
 00
3.48

PS: je ne veux pas d'interface graphique c'est pour un script python dans le terminal


Ubuntu 20.04

Hors ligne

#7 Le 14/07/2021, à 19:25

pseudofab

Re : [RESOLU] [python] lire un seul caractère sur l'entrée standard

Bonjour,
Si tu veux lancer le script depuis le terminal, ajoute en début de script:

#!/usr/bin/env python3

Le mainloop(post #5) a été imputé de ses parenthèses lors de mon copié-collé:

root.mainloop()

Pour lancer le script avec

$ ./clavier6.py

, il te faut au préalable rendre le script exécutable:

chmod +x clavier6.py

Sinon, tu peux aussi le lancer explicitement :

python3 mon_chemin/clavier6.py

Voilà, tu devrais maintenant pouvoir exécuter le script wink


# Là pour le plaisir de découvrir ...
# Envie d'apprendre Bash ou Python? Pensez aux MOOC ---> https://forum.ubuntu-fr.org/viewtopic.php?id=2030646

Hors ligne

#8 Le 16/07/2021, à 10:31

CasseTaTélé_isback

Re : [RESOLU] [python] lire un seul caractère sur l'entrée standard

Le mainloop(post #5) a été imputé de ses parenthèses lors de mon copié-collé:

ah oui c'est ça qu'il manquait ! (j'avais fait tout le reste pour le lancer bien sûr !

Ceci dit le résultat n'est pas celui que je voulais puisque ça ouvre une fenêtre ... Tant pis, merci quand même !


Ubuntu 20.04

Hors ligne