Ce projet est une copie d’un document initial de Didier Müller.
# Le jeu du pendu
from tkinter import *
from random import choice
fichier = open("liste_mots.txt", "r")
liste_mots = fichier.readlines() # met tous les mots du fichier dans une liste
fichier.close()
def lettre_dans_mot(lettre) :
global partie_en_cours, mot_partiel, mot_choisi, nb_echecs, image_pendu
if partie_en_cours :
nouveau_mot_partiel = ""
lettre_dans_mot = False
i=0
while i<len(mot_choisi):
if mot_choisi[i]==lettre:
nouveau_mot_partiel = nouveau_mot_partiel + lettre
lettre_dans_mot = True
else:
nouveau_mot_partiel = nouveau_mot_partiel + mot_partiel[i]
i+=1
mot_partiel = nouveau_mot_partiel
afficher_mot(mot_partiel)
if not lettre_dans_mot : # lettre fausse. Changer le dessin.
nb_echecs += 1
nomFichier = "pendu_"+str(nb_echecs)+".gif"
photo=PhotoImage(file=nomFichier)
image_pendu.config(image=photo)
image_pendu.image=photo
if nb_echecs == 7: # trop d'erreurs. Fini.
partie_en_cours = False
afficher_mot(mot_choisi)
elif mot_partiel == mot_choisi: # le mot a été trouvé !
partie_en_cours = False
def afficher_mot(mot):
global lettres
mot_large = ""
i=0
while i<len(mot): # ajoute un espace entre les lettres
mot_large = mot_large + mot[i] + " "
i+=1
canevas.delete(lettres)
lettres = canevas.create_text(320,60,text=mot_large,fill='black',font='Courrier 30')
def init_jeu():
global mot_choisi, mot_partiel, image_pendu, lettres
global nb_echecs, partie_en_cours, liste_mots
nb_echecs = 0
partie_en_cours = True
mot_choisi = choice(liste_mots).rstrip()
mot_choisi = mot_choisi.upper()
mot_partiel = "-" * len(mot_choisi)
afficher_mot(mot_partiel)
photo=PhotoImage(file="pendu_0.gif")
image_pendu.config(image=photo)
image_pendu.image=photo
# création du widget principal
fenetre = Tk()
fenetre.title("Le jeu du pendu")
canevas = Canvas(fenetre, bg='white', height=500, width=620)
canevas.pack(side=BOTTOM)
bouton = [0]*26
for i in range(26):
bouton[i] = Button(fenetre,text=chr(i+65),command=lambda x=i+65:lettre_dans_mot(chr(x)))
bouton[i].pack(side=LEFT)
bouton2 = Button(fenetre,text='Quitter',command=fenetre.quit)
bouton2.pack(side=RIGHT)
bouton1 = Button(fenetre,text='Recommencer',command=init_jeu)
bouton1.pack(side=RIGHT)
photo=PhotoImage(file="pendu_0.gif")
image_pendu = Label(canevas, image=photo, border=0)
image_pendu.place(x=120, y=140)
lettres = canevas.create_text(320,60,text="",fill='black',font='Courrier 30')
init_jeu()
fenetre.mainloop()
fenetre.destroy()
Il faut bien sûr télécharger les images nécessaires et les placer dans le même répertoire que le script.
Le programme est divisé en cinq parties. Tout d'abord, on charge les mots lus dans un fichier externe dans la liste $\texttt{liste_mots}$.
fichier = open("liste_mots.txt", "r")
liste_mots = fichier.readlines() # met tous les mots du fichier dans une liste
fichier.close()
La procédure $\texttt{lettre_dans_mot()}$ se charge de vérifier que la lettre proposée figure dans le mot choisi. Si c'est le cas, la lettre sera placé au bon endroit dans le mot qui sera affiché.
def lettre_dans_mot(lettre) :
global partie_en_cours, mot_partiel, mot_choisi, nb_echecs, image_pendu
if partie_en_cours :
nouveau_mot_partiel = ""
lettre_dans_mot = False
i=0
while i<len(mot_choisi):
if mot_choisi[i]==lettre:
nouveau_mot_partiel = nouveau_mot_partiel + lettre
lettre_dans_mot = True
else:
nouveau_mot_partiel = nouveau_mot_partiel + mot_partiel[i]
i+=1
mot_partiel = nouveau_mot_partiel
afficher_mot(mot_partiel)
...
Dans le cas contraire, le dessin du pendu sera continué. En fait, l'image du pendu sera remplacée par une autre plus complète.
if not lettre_dans_mot : # lettre fausse. Changer le dessin.
nb_echecs += 1
nomFichier = "pendu_"+str(nb_echecs)+".gif"
photo=PhotoImage(file=nomFichier)
image_pendu.config(image=photo)
image_pendu.image=photo
...
Si le dernier dessin est affiché, la partie s'arrête et le mot complet et affiché.
if nb_echecs == 7: # trop d'erreurs. Fini.
partie_en_cours = False
afficher_mot(mot_choisi)
elif mot_partiel == mot_choisi: # le mot a été trouvé !
partie_en_cours = False
Comme son nom l'indique, la procédure $\texttt{afficher_mot()}$ écrit sur l'écran un mot partiel composé de tirets et des lettres qui ont été trouvées.
def afficher_mot(mot):
global lettres
mot_large = ""
i=0
while i<len(mot): # ajoute un espace entre les lettres
mot_large = mot_large + mot[i] + " "
i+=1
canevas.delete(lettres)
lettres = canevas.create_text(320,60,text=mot_large,fill='black',font='Courrier 30')
Nous reviendrons sur la dernière ligne de cette procédure. Nous verrons ce que signifie le $\texttt{chr}$ et le $\texttt{lambda}$.
Remarquons que l'ancien mot partiel, qui a comme nom de variable lettres, est effacé par l'instruction $\texttt{canevas.delete(lettres)}$, avant que le nouveau soit réécrit au même endroit par l'instruction $\texttt{canevas.create_text()}$.
La procédure $\texttt{init_jeu()}$ initialise toutes les variables globales avant de (re)commencer une partie, tire au hasard un nouveau mot dans la liste et écrit des tirets correspondant au nombre de lettres, et enfin affiche le premier dessin du pendu, qui se nomme $\texttt{pendu_0.gif}$.
def init_jeu():
global mot_choisi, mot_partiel, image_pendu, lettres
global nb_echecs, partie_en_cours, liste_mots
nb_echecs = 0
partie_en_cours = True
mot_choisi = choice(liste_mots).rstrip()
mot_choisi = mot_choisi.upper()
mot_partiel = "-" * len(mot_choisi)
afficher_mot(mot_partiel)
photo=PhotoImage(file="pendu_0.gif")
image_pendu.config(image=photo)
image_pendu.image=photo
Dans le dernière partie, on met en place les différentes composantes de l'interface graphique.
La première ligne crée la fenêtre, la seconde lui donne un titre. Notez que l'on ne donne pas les dimensions de la fenêtre : elle est « élastique » et s'adaptera automatiquement au contenu.
fenetre = Tk()
fenetre.title("Le jeu du pendu")
Un canevas est une surface réservée aux éléments graphiques. On indique dans quelle fenêtre il devra se trouver, sa couleur de fond (blanc), et ses dimensions (500 pixels de haut et 620 de large). La seconde ligne indique que ce canevas sera placé en bas de la fenêtre. La fonction $\texttt{pack}$ sera expliquée plus en détail...
canevas = Canvas(fenetre, bg='white', height=500, width=620)
canevas.pack(side=BOTTOM)
Les 26 boutons lettres seront placés dans la fenêtre, au-dessus du canevas (puisqu'on a dit que le canevas occupe le bas de la fenêtre) et tassés à gauche (la fonction $\texttt{chr}$ sera expliquée plus loin ...).
bouton = [0]*26
for i in range(26):
bouton[i] = Button(fenetre,text=chr(i+65),command=lambda x=i+65:lettre_dans_mot(chr(x)))
bouton[i].pack(side=LEFT)
un bouton Quitter, tassé à droite
bouton2 = Button(fenetre,text='Quitter',command=fenetre.quit)
bouton2.pack(side=RIGHT)
et un bouton Recommencer, tassé à droite
bouton1 = Button(fenetre,text='Recommencer',command=init_jeu)
bouton1.pack(side=RIGHT)
Notez que le bouton le plus à droite (Quitter) doit être écrit en premier.
Le dessin du pendu, qui est un $\texttt{Label}$, est placé dans la canevas aux coordonnées $(120,140)$. La fonction $\texttt{place}$ sera expliquée plus en détail ...
photo=PhotoImage(file="pendu_0.gif")
image_pendu = Label(canevas, image=photo, border=0)
image_pendu.place(x=120, y=140)
Le mot à découvrir est lui aussi placé dans le canevas, aux coordonnées $(320, 60)$. Il sera écrit en noir, avec la police « Courrier 30 ».
lettres = canevas.create_text(320,60,text="",fill='black',font='Courrier 30')
Remarquez bien que le dessin du pendu et le mot à découvrir sont placés dans le canevas, contrairement aux boutons.
Le code ASCII (American Standard Code for Information Interchange) est une norme d'encodage informatique des caractères alphanumériques de l'alphabet latin. La norme ASCII établit une correspondance entre une représentation numérique des caractères de l'alphabet latin ainsi que les symboles et les signes. Par exemple, le caractère « A » est associé à $65$ et « a » à $97$. Les nombres du code ASCII vont de $0$ à $127$.
Le codage ASCII (voir tableau ci-dessous) est souvent complété par des correspondances supplémentaires afin de permettre l'encodage informatique d'autres caractères, comme les caractères accentués par exemple. Cette norme s'appelle $\texttt{ISO-8859}$ et se décline par exemple en $\texttt{ISO-8859-1}$ lorsqu'elle étend l'ASCII avec les caractères accentués d'Europe occidentale.
En Python, la fonction qui donne de code ASCII d'un caractère s'appelle $\texttt{ord}$. Ainsi :
ord('A')
Inversement, pour écrire le caractère correspondant à un code ASCII, on utilisera la fonction $\texttt{chr}$ :
chr(65)
On comprend maintenant mieux notre ligne mystérieuse :
bouton[i] = Button(fenetre,text=chr(i+65),command=lambda x=i+65:lettre_dans_mot(chr(x)))
Sur le bouton $0$ sera écrit la lettre 'A' ($\texttt{chr(65)}$), sur le bouton $1$ la lettre 'B' ($\texttt{chr(66)}$), etc.
Par défaut, le paramètre $\texttt{command}$ d'un $\texttt{Button}$ nécessite une fonction sans argument. Vous pouvez cependant utiliser les fonctions $\texttt{lambda}$ pour passer des arguments supplémentaires, ce qui peut être utile si vous avez plusieurs $\texttt{Button}$ qui pointent sur la même fonction.
Le module $\texttt{tkinter}$ contient trois fonctions permettant de positionner les éléments d'une interface graphique :
$\texttt{grid}$ (que nous avons déjà vue au projet 4), $\texttt{pack}$ et $\texttt{place}$.
Lorsqu'on fait appel à $\texttt{pack()}$, $\texttt{tkinter}$ réduit la fenêtre pour épouser au mieux les éléments qu'elle contient. Chaque ajout d'un widget se fera de l'une des quatre manières ci-dessous, selon le côté choisi ($\texttt{LEFT}$, $\texttt{RIGHT}$, $\texttt{TOP}$ ou $\texttt{BOTTOM}$).
Le terme widget désigne toute entité susceptible d'être placée dans une fenêtre (un bouton, une image, etc)
Nous utiliserons le terme de cavité pour désigner l'espace qui sera prochainement occupé par un widget.
On divise la cavité restante en deux, pour créer une nouvelle cavité restante, à laquelle on appliquera le même schéma lors de l'ajout d'un nouveau widget.
Dans notre programme, on a d'abord placé un canevas en bas de la fenêtre. La cavité restante est donc la partie supérieure de la fenêtre. Ensuite, on a ajouté l'un après l'autre les 26 boutons lettres à gauche ; la cavité restante était donc la partie supérieure droite de la fenêtre. Puis on a ajouté à droite le bouton Quitter. La cavité restante se trouve donc toujours dans la partie supérieure de la fenêtre, coincée entre le bouton Z et le bouton Quitter.
C'est donc là que sera ajouté le dernier bouton Recommencer. À noter qu'il reste une cavité entre Z et Recommencer.
La commande $\texttt{place}$ permet de placer un widget à la position $(x,y)$. Pour être précis, c'est le coin supérieur gauche du widget qui est positionné en $(x,y)$.
image_pendu.place(x=120, y=140)
Attention ! Les coordonnées $(0,0)$ se trouvent en haut à gauche du canevas (et non en bas à gauche comme on pourrait s'y attendre).
Il est possible que l'image sorte du canevas : cela ne provoquera pas d'erreur, mais votre image sera tronquée.
Les caractères et autres symboles sont codés grâce au code $\texttt{ASCII}$.
Un canevas est une surface réservée aux éléments graphiques.
Il existe trois manières de positionner les éléments d'une interface graphique : $\texttt{grid}$, $\texttt{pack}$ et $\texttt{place}$.
Une chaîne de caractères peut être transformée en un élément graphique avec la fonction $\texttt{create_text()}$.