Zhentao Li's Homepage

email:zl zli2 com

 // home


Attention: Ce cours aura lieu le 30 mars et non le 23 mars.

Les transparents du sixième cours sont disponibles ici.

La version longue des transparents est ici.

Voir le chapitre 8 du livre Apprendre à programmer avec Python.

Les documents de référence les plus intéressants sont en anglais. Ainsi le manuel de Fredrik Lundh, An Introduction to Tkinter et celui de John Shipman, Tkinter Reference détaillent l'ensemble des paramètres de configuration des objets graphiques de la bibliothèque.

Les exercices proposés ci-dessous sont empruntés à Christophe Schlick.

Remarque sur .config

Il y a une syntaxe plus naturel de modifier les paramètres des widgets déjà crée: on peut les traiter comme des dictionnaires.

etiquette["text"] = "nouveau texte"

a le même effet que

etiquette.config(text="nouveau texte")

Autre exemples:

etiquette["bg"] = "red"
etiquette["font"] = ("Monospace", 80)

Exercice 1 - Compteur

Érire un programme counter qui affiche une fenêtre graphique permettant à l'utilisateur de contrôler un compteur numérique dans un intervalle [-1000, 1000] par une série de sept boutons de commande.

Les trois boutons "|<", "0" et ">|" permettent d'aller directement à la valeur minimale, la valeur nulle ou la valeur maximale du compteur. Les quatre boutons "<<", "<", ">" et ">>" permettent de modifier la valeur courante du compteur par pas de -10, -1, 1 et 10. À tout moment, il faut vérifier que la valeur du compteur se trouve bien dans l'intervalle autorisé. Le résultat graphique souhaité est représenté dans l'image ci-dessous (widgets utilisées : Frame, Label, Button) :

import Tkinter as tk

MAXIMUM = 1000
MINIMUM = -1000
compteur = 0

def verifier_bornes():
    global compteur
    if compteur > 1000:
        compteur = 1000
    if compteur < -1000:
        compteur = -1000
    etiquette_compteur['text'] = str(compteur)

def remise_au_maximum():
    global compteur
    compteur = MAXIMUM
    verifier_bornes()

def soustraire_un():
    global compteur
    compteur = compteur - 1
    verifier_bornes()

def remise_a_zero():
    global compteur
    compteur = 0
    verifier_bornes()

def ajouter_un():
    global compteur
    compteur = compteur + 1
    verifier_bornes()

def remise_au_minimum():
    global compteur
    compteur = MINIMUM
    verifier_bornes()

racine = tk.Tk()
frame_boutons = tk.Frame(racine)
frame_boutons.pack()
etiquette_compteur = tk.Label(racine, text=str(compteur), font=('Monospace', 40) )
etiquette_compteur.pack()

bouton_minimum = tk.Button(frame_boutons, text='>|', command=remise_au_minimum)
bouton_minimum.pack(side='left')
bouton_moins = tk.Button(frame_boutons, text='<', command=soustraire_un)
bouton_moins.pack(side='left')
bouton_zero = tk.Button(frame_boutons, text='0', command=remise_a_zero)
bouton_zero.pack(side='left')
bouton_plus = tk.Button(frame_boutons, text='>', command=ajouter_un)
bouton_plus.pack(side='left')
bouton_maximum = tk.Button(frame_boutons, text='>|', command=remise_au_maximum)
bouton_maximum.pack(side='left')

# Les paragraphe ci-dessus peut etre ecrit de facon un peu plus compacte
# et en evitant des erreurs de recopie

#liste_des_boutons_voulues = [('|<', remise_au_minimum),
#                              ('<', soustraire_un),
#                              ('0', remise_a_zero),
#                              ('>', ajouter_un),
#                              ('>|', remise_au_maximum_un)]
#
#for nom, fonction in liste_des_boutons_voulues:
#    bouton = tk.Button(frame_boutons, text='|<', command=remise_a_zero)
#    bouton.pack(side='left')

racine.mainloop()

Exercice 2 - Couleurs

Écrire un programme qui affiche une fenêtre graphique permettant à l'utilisateur de créer interactivement une couleur dans le mode RGB (Red, Green, Blue).

Pour cela, le programme doit fournir trois potentiomètres (Widget Scale) permettant de saisir une valeur entre 0 et 255, contrôlant respectivement les intensités de rouge, de vert et de bleu. Les valeurs de ces trois potentiomètres sont alors converties en hexadécimal pour créer une chaîne de caractères de la forme "#RRGGBB" correspondant à la représentation standard des couleurs de Tkinter.

Cette couleur est ensuite utilisée comme couleur de fond de la partie inférieure de la fenêtre, ce qui permet à l'utilisateur de visualiser le résultat de ses manipulations. Le résultat graphique souhaité est représenté dans l'image ci-dessous (widgets utilisées :Frame, Label, Button, Scale) :

Dans la solution suivante, nous utilisons la chaine de caractere '%20x' qui est interprété transforme un entier en sa representation à deux chiffres en base 16 lorsque l'opération % est appliqué. Par exemple:

>>> "%02x" % 249
'ff'

Sinon, nous pouvons a la place faire la transformation nous même

>>> valeur = 249
>>> chiffres_hexadecimaux = "0123456789abcdef"
>>> chiffres_hexadecimaux[valeur/16]
'f'
>>> chiffres_hexadecimaux[valeur%16]
'9'
>>> chiffres_hexadecimaux[valeur/16] + chiffres_hexadecimaux[valeur%16]
'f9'

Nous utilisons le fait que nous attendons une réponse à deux chiffres. On porrait aussi créer une fonction.

chiffres_hexadecimaux = "0123456789abcdef"
def transformer(valeur):
    return chiffres_hexadecimaux[valeur/16] + chiffres_hexadecimaux[valeur%16]

Solution complète:

import Tkinter as tk

# Cette fonction sera appellee a tout changement.
# Nous ignorons la nouvelle valeur qui nous est donnee
# puisque nous allons lire toutes les valeurs avec get
# de toute facon.
def mise_a_jour_couleur(nouvelle_valeur):
    r, v, b = rouge.get(), vert.get(), bleu.get()
    couleur = "#%02x%02x%02x" % (r, v, b)
    couleur_fond['bg'] = couleur
    nom_couleur['text'] = couleur

racine = tk.Tk()
frame_scales = tk.Frame(racine)
frame_scales.pack(side='top')
nom_couleur = tk.Label(frame_scales, font=("Arial", 12), width=9, relief=tk.GROOVE)
rouge = tk.Scale(frame_scales, orient=tk.HORIZONTAL, to=255, troughcolor='red',
                 show=0, command=mise_a_jour_couleur)
vert = tk.Scale(frame_scales, orient=tk.HORIZONTAL, to=255, troughcolor='green',
                show=0, command=mise_a_jour_couleur)
bleu = tk.Scale(frame_scales, orient=tk.HORIZONTAL, to=255, troughcolor='blue',
                show=0, command=mise_a_jour_couleur)
nom_couleur.pack(side='left')
rouge.pack(side='left')
vert.pack(side='left')
bleu.pack(side='left')
couleur_fond = tk.Frame(racine, bg='black', height=200, width=450)
couleur_fond.pack(side='top')
racine.title("Couleurs")
racine.mainloop()

Exercice 3 - Chasse au trésor

Écrire un programme qui affiche une fenêtre graphique permettant à l'utilisateur de jouer au jeu classique de la chasse au trésor.

  • Le programme sélectionne aléatoirement l'une des cases d'un tableau 15x15 qui représentera l'emplacement du trésor (la taille du tableau doit être un paramètre de configuration modifiable dans le code source).

  • À chaque tour, l'utilisateur clique sur une case et le programme y affiche un code de couleur relatif à la distance entre cette case et la case au trésor (rouge pour une distance < 3, jaune pour une distance < 6, vert pour une distance < 9, bleu pour une distance < 12, et gris pour toutes les distances au-delà).

  • Attention, il ne s'agit pas d'une distance euclidienne, mais d'une distance dite de Manhattan, correspondant au nombre de déplacements horizontaux ou verticaux nécessaires pour rejoindre la case au trésor.

Le but du jeu est évidemment de trouver le tésor en un minimum de tours. Le résultat graphique souhaité est représenté dans l'image ci-dessous (widgets utilisées :Frame, Button) :

import Tkinter as tk
import random

largeur = 15
hauteur = 15
couleurs = ['red', 'yellow', 'green', 'blue', 'darkgrey']

# Il est possible sans classe en utilisant global.

class JeuTresor:
    def __init__(self, hauteur, largeur, grille):
        self.hauteur = hauteur
        self.largeur = largeur
        self.grille = grille
        for bouton in self.grille.values():
            # Nous utilisons bind au lieu de command
            # pour pouvoir connaitre le bouton sur
            # lequel nous avons appuye.
            bouton.bind('<Button-1>', self.colorier)
        self.nouvelle_partie()

    def nouvelle_partie(self):
        self.fin_de_partie = False
        self.tresor = (random.randint(0, hauteur-1), random.randint(0, largeur-1))
        self.compteur = 0
        etiquette_coups['text'] = '%s' % self.compteur
        for i in range(self.hauteur):
            for j in range(self.largeur):
                self.grille[(i,j)]['text'] = ''
                self.grille[(i,j)]['background'] = '#FFF'
                self.grille[(i,j)]['activebackground'] = '#FFF'

    def distance(self, i, j):
        return abs(self.tresor[0]-i) + abs(self.tresor[1]-j)

    def couleur(self, i, j):
        return couleurs[min(self.distance(i,j)/3, 4)]

    def fin(self):
        # Afficher toute la grille
        self.fin_de_partie = True
        for i in range(self.hauteur):
            for j in range(self.largeur):
                self.grille[(i,j)]['text'] = self.distance(i,j)
                self.grille[(i,j)]['background'] = self.couleur(i, j)
                self.grille[(i,j)]['activebackground'] = self.couleur(i, j)

    def colorier(self, event):
        bouton = event.widget
        i, j = bouton.coordonnees
        if self.fin_de_partie:
            return
        self.compteur += 1
        etiquette_coups['text'] = '%s' % self.compteur
        if self.distance(i, j) == 0:
            self.fin()
        else:
            self.grille[(i,j)]['background'] = self.couleur(i, j)
            self.grille[(i,j)]['activebackground'] = self.couleur(i, j)        

racine = tk.Tk()

planche = tk.Frame(racine, height=200, width=200)
grille = {}
for i in range(hauteur):
    for j in range(largeur):
        grille[(i, j)] = tk.Button(planche,
                                  text='',
                                  bg='#FFF',
                                  width=1,
                                  height=1)
        grille[(i, j)].coordonnees = (i, j)
        grille[(i, j)].grid(row = i, column = j)

frame_coups = tk.Frame(racine)
etiquette = tk.Label(frame_coups, text = 'Nombre de coups : ')
etiquette_coups = tk.Label(frame_coups)
etiquette.pack(side='left')
etiquette_coups.pack(side='left')

jeu = JeuTresor(hauteur, largeur, grille)

boutons = tk.Frame(racine)
bouton_quitter = tk.Button(boutons, text = 'Quitter', command = racine.quit)
bouton_rejouer = tk.Button(boutons,text = 'Rejouer', command = jeu.nouvelle_partie)
bouton_quitter.pack(side='left')
bouton_rejouer.pack(side='left')

boutons.pack()
planche.pack()
frame_coups.pack()
racine.mainloop()

Website design modified from Sliqua taken from OSWD