Formation I.S.N.

Module tkinter - Exercices supplémentaires

Plus tard ! Les exercices de cette page ne sont pas à réaliser le jour de la formation (sauf s'il vous reste du temps bien évidemment). Ils sont là pour vous donner d'autres idées d'exercices, approfondir vos connaissances ou vous entraîner si besoin.

Jeu de dé (la suite)

On reprend le jeu du lancer de dé en souhaitant ajouter quelques fonctionnalités :

  • afficher le nombre de 6 qui ont été tirés ;
  • afficher le nombre de coups au total.

Voici une illustration du fonctionnement du programme :

Vous trouverez ci-dessous le programme de base qui permet de lancer le dé. Ajoutez les fonctionnalités présentées ci-dessus.


##-----Importation des Modules-----##
from tkinter import *
from random import *


##-----Définition des Fonctions-----##
def NouveauLance():
    nb = randint(1,6)
    Texte.set('Résultat -> ' + str(nb))


##-----Création de la fenêtre-----##
Mafenetre = Tk()
Mafenetre.title('Dé à 6 faces')


##-----Création des boutons-----##
BoutonLancer = Button(Mafenetre, text ='Lancer', command = NouveauLance)
BoutonLancer.grid(row=0, column = 0, padx = 5, pady = 5)

BoutonQuitter = Button(Mafenetre, text ='Quitter', command = Mafenetre.destroy)
BoutonQuitter.grid(row=0, column = 1, padx = 5, pady = 5)


##-----Création des zones de texte-----##
Texte = StringVar(master=Mafenetre)					# On indique quelle est la fenêtre principale
LabelResultat = Label(Mafenetre, textvariable = Texte, fg ='red', bg ='white')
LabelResultat.grid(row=0, column = 2, padx = 5, pady = 5)


##-----Programme principal-----##
NouveauLance()							# On appelle une fois la fonction pour initialiser notre Texte
Mafenetre.mainloop()                    # Boucle d'attente des événements
				
  • StringVar() ?
  • Une piste
  • Une solution

La fonction StringVar() incluse dans le module tkinter permet d'affecter du « texte modifiable » à une variable. Lorsque StringVar() est utilisée en dehors d'une création de formulaire, il est fortement conseillé d'indiquer la fenêtre principale à l'aide du paramètre master (comme lors d'importation d'images dans un Canvas). C'est le cas en lignes 17 et 18 du programme ci-dessus.

Pensez aux variables globales pour le nouveau texte.

##-----Importation des Modules-----##
from tkinter import *
from random import *


##----- Conditions initiales -----##
Nb6 = 0			# Variable globale
NbTotal = 0		# Variable globale


##-----Définition des Fonctions-----##
def NouveauLance():
    global Nb6, NbTotal
    nb = randint(1,6)
    NbTotal  =  NbTotal + 1
    if nb == 6:
            Nb6  = Nb6 + 1
    Texte.set('Résultat -> ' + str(nb))
    Texte2.set(str(Nb6) +'//'+ str(NbTotal))


##-----Création de la fenêtre-----##
Mafenetre = Tk()
Mafenetre.title('Dé à 6 faces')


##-----Création des boutons-----##
BoutonLancer = Button(Mafenetre, text ='Lancer', command = NouveauLance)
BoutonLancer.grid(row=0, column = 0, padx = 5, pady = 5)

BoutonQuitter = Button(Mafenetre, text ='Quitter', command = Mafenetre.destroy)
BoutonQuitter.grid(row=0, column = 1, padx = 5, pady = 5)


##-----Création des zones de texte-----##
Texte = StringVar(master=Mafenetre)					# On indique quelle est la fenêtre principale
LabelResultat = Label(Mafenetre, textvariable = Texte, fg ='red', bg ='white')
LabelResultat.grid(row=0, column = 2, padx = 5, pady = 5)

Texte2 = StringVar(master=Mafenetre)
LabelProp6 = Label(Mafenetre, textvariable = Texte2, fg ='blue', bg ='white')
LabelProp6.grid(row=0, column = 3, padx = 5, pady = 5)


##-----Programme principal-----##
NouveauLance()							# On appelle une fois la fonction pour initialiser notre Texte
Mafenetre.mainloop()                    # Boucle d'attente des événements
						

Calculatrice minimaliste

L'un des inconvénients majeur des boutons du module tkinter provient des fonctions déclenchées qui ne peuvent pas prendre d'argument. En utilisant un fonction anonyme lambda, il est possible de passer des arguments à la fonction déclenchée par le bouton.

Une fonction lambda n'est pas définie par def. Ainsi, les deux définitions ci-dessous sont équivalentes :

Définition « classique » Utilisation de lambda

def f(x):
    return 2*x

print(f(5))

f = lambda x: 2*x


print(f(5))
						

Voici une reformulation de l'exercice sur le lancer d'un dé à l'aide d'une fonction lambda :


##-----Importation des Modules-----##
from tkinter import *
from random import *


##-----Définition des Fonctions-----##
def lancer(a, b):
    """ Entrées : Fonction déclenchée par le bouton
			a est un entier aléatoire entre 1 et 6, b = 0 (nécessaire pour lambda)
		Sorties : Simule le lancer d'un dé à 6 faces et affiche le résultat obtenu dans la zone de texte."""
    texte_affiche.configure(text = str(a))


##-----Création de la fenêtre-----##
fen = Tk()
fen.title('Lancer un dé')


##-----Création des boutons-----##
bouton_lancer = Button(fen, text='Lancer', command = lambda x=0: lancer(randint(1, 6), x))
bouton_lancer.grid(row = 1, column = 0, padx = 5, pady = 3)

bouton_quitter = Button(fen, text='Quitter', command=fen.destroy)
bouton_quitter.grid(row = 1, column = 1, padx = 5, pady = 3)


##-----Création des zones de texte-----##
texte_affiche = Label(fen, text='')
texte_affiche.grid(row = 0, column = 0, columnspan = 2)


##-----Programme principal-----##
fen.mainloop()                          # Boucle d'attente des événements
				

Énoncé

En utilisant correctement une (ou plusieurs) fonctions lambda, ainsi que la fonction eval(), programmer la calculatrice minimaliste dont l'interface est représentée ci-dessous.

  • Fonction eval()
  • Liste de boutons
  • Un programme à compléter
  • Une solution

Le programme devra prendre en compte une chaîne de caractères afin de calculer la valeur de l'expression saisie. Par exemple 3/4 ou 3+4. La fonction eval() permet d'interpréter directement ces calculs sans rentrer dans une programmation laborieuse de recherche de caractères :


x = eval(input("Saisir par exemple 3/4 : "))
print("Le type de la variable x :", type(x))
print(x)
							

On obtient :

Entrez par exemple 3/4 : 3/4
Le type de la variable x : <class 'float'>
0.75
Lorsqu'on veut créer des boutons ayant les mêmes caractéristiques, il faut penser à créer une liste de boutons. Ici, les boutons qui représentent les chiffres et les opérations sont de bons candidats à mettre dans une liste. On peut associer cette liste à une chaîne de caractères comme '0123456789+-*/'.

##-----Importation des Modules-----##
from tkinter import *


##-----Variables et constantes-----##
textes = '123+456-789*0/'
boutons = []                                        # Liste des boutons



##-----Définition des Fonctions-----##
def afficher(a):
    """ Entrées : a est un caractère parmi 0123456789+-*/
        Sorties : Ajoute le caractère au texte déjà affiché dans la zone de texte."""
    # A compléter


def valider():
    """ Entrées : Pas d'argument - Fonction déclenchées par un bouton
        Sorties : Evalue la valeur de l'expression saisie par l'intermédiaire
            des boutons, l'affiche puis ré-initialise la zone de texte."""
    # A compléter


##-----Création de la fenêtre-----##
fen = Tk()
fen.title('Lancer un dé')


##-----Création des boutons-----##
bouton_quitter = Button(fen, width = 10, text='Quitter', font='Arial 15', command=fen.destroy)
bouton_quitter.grid( padx = 5, pady = 3)

bouton_calculer = Button(fen, text='Calculer', font='Arial 15', command=valider)
bouton_calculer.grid( padx = 15, pady = 3, sticky = E+W)

for i in range(?):
    boutons.append(Button(fen, width = 10, text=textes[i], font='Arial 15')
    if i<12:
        boutons[i].grid( padx = 15, pady = 5, sticky = S+W+E)
    else:
        boutons[i].grid( padx = 15, pady = 5, sticky = S+W+E)


##-----Création des zones de texte-----##
texte_affiche = Label(fen, bg='white', width = 22, relief="sunken", borderwidth=5, font='Arial 15', text='', padx = 5)
texte_affiche.grid(row = 0, column = 0, columnspan = 2)

texte_result = Label(fen, text='', font='Arial 15', padx = 15)
texte_result.grid(row = 0, column = 2, columnspan = 2)


##-----Programme principal-----##
fen.mainloop()                          # Boucle d'attente des événements
						

##-----Importation des Modules-----##
from tkinter import *


##-----Variables et constantes-----##
textes = '123+456-789*0/'
boutons = []                                        # Liste des boutons



##-----Définition des Fonctions-----##
def afficher(a):
    """ Entrées : a est un caractère parmi 0123456789+-*/
        Sorties : Ajoute le caractère au texte déjà affiché dans la zone de texte."""
    sauve = texte_affiche.cget("text")
    texte_affiche.configure(text = sauve+a)


def valider():
    """ Entrées : Pas d'argument - Fonction déclenchées par un bouton
        Sorties : Evalue la valeur de l'expression saisie par l'intermédiaire
            des boutons, l'affiche puis ré-initialise la zone de texte."""
    expression = texte_affiche.cget("text")
    expression = eval(expression)
    texte_affiche.configure(text = "")
    texte_result.configure(text = '= '+str(expression))


##-----Création de la fenêtre-----##
fen = Tk()
fen.title('Lancer un dé')


##-----Création des boutons-----##
bouton_quitter = Button(fen, width = 10, text='Quitter', font='Arial 15', command=fen.destroy)
bouton_quitter.grid(row = 5, column = 3, columnspan = 2, padx = 5, pady = 3)

bouton_calculer = Button(fen, text='Calculer', font='Arial 15', command=valider)
bouton_calculer.grid(row = 5, column = 0, columnspan = 3, padx = 15, pady = 3, sticky = E+W)

for i in range(len(textes)):
    boutons.append(Button(fen, width = 10, text=textes[i], font='Arial 15', command = lambda x=textes[i]: afficher(x)))
    if i<12:
        boutons[i].grid(row = i//4+1, column = i%4, padx = 15, pady = 5, sticky = S+W+E)
    else:
        boutons[i].grid(row = 4, column = 2*i-23, padx = 15, pady = 5, sticky = S+W+E)


##-----Création des zones de texte-----##
texte_affiche = Label(fen, bg='white', width = 22, relief="sunken", borderwidth=5, font='Arial 15', text='', padx = 5)
texte_affiche.grid(row = 0, column = 0, columnspan = 2)

texte_result = Label(fen, text='', font='Arial 15', padx = 15)
texte_result.grid(row = 0, column = 2, columnspan = 2)


##-----Programme principal-----##
fen.mainloop()                          # Boucle d'attente des événements
						

Mot de passe

Il s'agit dans cet exemple d'améliorer l'interaction avec l'utilisateur en proposant un champ de saisie de texte. Nous aurons ainsi un équivalent de la commande input() en mode non graphique.

Le programme proposé dans cet exemple va simuler un formulaire de saisie de mot de passe :

Ce sera aussi l'occasion de découvrir les boîtes de dialogue qui sont des fenêtres toute faites pour un usage précis. Ici, nous afficherons un message en cas de mot de passe incorrect :

Etudier le programme ci-dessous, vous pouvez analyser le code pour trouver le mot de passe.


##----- Importation des Modules -----##
from tkinter import *
from tkinter.messagebox import *		# boîte de dialogue


##-----Définition des fonctions-----##
def Verification():
    if Motdepasse.get() == 'python':		# le mot de passe est bon :
                                            # on affiche une boîte de dialogue puis on ferme la fenêtre
        showinfo('Résultat','Mot de passe correct.\nAu revoir !')
        Mafenetre.destroy()

    else:									# le mot de passe est incorrect :
                                            # on affiche une boîte de dialogue
        showwarning('Résultat','Mot de passe incorrect.\nVeuillez recommencer !')
        Motdepasse.set('')


##----- Création de la fenêtre principale -----##
Mafenetre = Tk()
Mafenetre.title('Identification requise')


##-----Création des zones de texte-----##
Label1 = Label(Mafenetre, text = 'Mot de passe ')
Label1.grid(row=0, column = 0, padx = 5, pady = 5)


##----- Création des formulaires -----##
Motdepasse= StringVar(master=Mafenetre)					# On indique quelle est la fenêtre principale
Champ = Entry(Mafenetre, textvariable=Motdepasse, show='*', bg ='bisque', fg='maroon')
Champ.focus_set()
Champ.grid(row=0, column = 1, padx = 5, pady = 5)


##----- Création des boutons -----##
Bouton = Button(Mafenetre, text ='Valider', command = Verification)
Bouton.grid(row=0, column = 2, padx = 5, pady = 5)


##----- Programme principal -----##
Mafenetre.mainloop()
				

Fonctionnement du formulaire Champ

Le champ de saisie du mot de passe se déclare au moyen de la commande suivante :


Champ = Entry(Mafenetre, textvariable= Motdepasse, show='*', bg ='bisque', fg='maroon')
				

La réponse sera stockée dans la variable Motdepasse qui est un objet StringVar() comme on peut s'en douter à présent (c'est en effet un texte qui sera modifié par l'utilisateur). Le paramètre show='*' permet d'afficher des « * » au lieu du mot de passe saisi en clair.

La ligne 26 mérite aussi une petite explication :


Champ.focus_set()
				

Celle-ci permet en effet de positionner le curseur dans le champ de saisie afin qu'il n'y ait pas besoin de cliquer dedans avant de commencer à écrire. La récupération de la saisie de l'utilisateur se fait au moyen de l'appel à la méthode : Motdepasse.get().

Explication sur les fenêtres de dialogue

Une fenêtre s'affiche avec un message et un bouton [OK] une fois le mot de passe saisi. Cette fenêtre est en quelque sorte préfabriquée. C'est ce qu'on appelle un dialogue. Le module tkinter.messagebox possède beaucoup de dialogues prédéfinis :

  • pour afficher des messages d'alerte ;
  • pour sélectionner des couleurs ;
  • pour sélectionner des fichiers dans le disque dur ;
  • etc...

Pour utiliser cette fonctionnalité de dialogue d'alerte, nous commençons par importer la librarie idoine :


from tkinter.messagebox import *
				

Ensuite, l'affichage d'un message se fait par la simple commande :


showinfo('Résultat','Mot de passe correct.\nAu revoir !')
				

Cela affiche une fenêtre contenant une zone de texte (Label) et un bouton (Button) avec une commande aussi simple qu'un simple print(). Magique ! Remarquez au passage le caractère '\n' permettant de passer à la ligne dans le message.

La commande


showwarning('Résultat','Mot de passe incorrect.\nVeuillez recommencer !')
				

est d'usage similaire. Elle affiche un panneau d'avertissement en plus.

En conclusion, les boîtes de dialogues de tkinter.messagebox sont un moyen très économique d'améliorer l'interaction avec l'utilisateur.

Extension de l'exemple :

Reprendre le script précédent de manière à limiter le nombre d'essais à trois.

  • Une solution

##----- Importation des Modules -----##
from tkinter import *
from tkinter.messagebox import *		# boîte de dialogue


##----- Variables globales -----##
NbEssais = 0


##----- Définition des fonctions -----##
def Verification():
    global NbEssais
    NbEssais = NbEssais + 1
    if Motdepasse.get() == 'python':		# le mot de passe est bon :
                                            # on affiche une boîte de dialogue puis on ferme la fenêtre
        showinfo('Résultat','Mot de passe correct.\nAu revoir !')
        Mafenetre.destroy()

    else:									# le mot de passe est incorrect :
                                            # on affiche une boîte de dialogue
        if NbEssais < 3 :
            showwarning('Résultat','Mot de passe incorrect.\nVeuillez recommencer !')
            Motdepasse.set('')
        else :
            showerror('Résultat',"Mot de passe incorrect.\nC'était votre troisième et dernier essai.")
            Mafenetre.destroy()


##----- Création de la fenêtre principale -----##
Mafenetre = Tk()
Mafenetre.title('Identification requise')


##-----Création des zones de texte-----##
Label1 = Label(Mafenetre, text = 'Mot de passe ')
Label1.grid(row=0, column = 0, padx = 5, pady = 5)


##----- Création des formulaires -----##
Motdepasse= StringVar(master=Mafenetre)					# On indique quelle est la fenêtre principale
Champ = Entry(Mafenetre, textvariable=Motdepasse, show='*', bg ='bisque', fg='maroon')
Champ.focus_set()
Champ.grid(row=0, column = 1, padx = 5, pady = 5)


##----- Création des boutons -----##
Bouton = Button(Mafenetre, text ='Valider', command = Verification)
Bouton.grid(row=0, column = 2, padx = 5, pady = 5)


##----- Programme principal -----##
Mafenetre.mainloop()
						

Les boutons aux commandes

Cet exercice provient du livre de G. Swinnen.

L'interface graphique du programme ci-après est constituée d'une fenêtre contenant un canevas et quatre boutons :

  • le bouton [Tracer un cercle] doit afficher, dans le canevas, un cercle, d'épaisseur 2 pixels, dont les coordonnées (entre 50 et 250) du centre et le rayon (entre 10 et 40) sont aléatoires ;
  • le bouton [Changer de couleur] doit modifier aléatoirement la couleur des tracés suivants. Les différentes couleurs possibles sont définies dans la liste : ['purple', 'cyan', 'green', 'red', 'blue', 'orange', 'black'] ;
  • le bouton [Effacer] doit effacer tous les cercles déjà tracés. Il doit ré-initialiser la couleur du tracé suivant à 'blue'.

Complétez la définition des fonctions pilotées par ces boutons pour que le programme «fonctionne» correctement.


##----- Importation des Modules -----##
from tkinter import *

##----- Variables globales -----##
couleur = 'blue'                      	# Couleur par défaut

##-----Définition des fonctions-----##
def tracer():
    """ Entrées : Fonction déclenchée par le bouton - pas d'entrée
        Sorties : Trace dans le canevas un cercle de couleur, de centre un
            point de coordonnées aléatoires (entre 50 et 250) et de rayon
            aléatoire (entre 10 et 40)."""
    global couleur              		# Variable globale, modifiée dans le programme principal


def change_couleur():
    """ Entrées : Fonction déclenchée par le bouton - pas d'entrée
        Sorties : Permet le changement aléatoire de couleur parmi les 7 d'une liste donnée."""
    global couleur              		# Variable globale, modifiée dans le programme principal


def effacer():
    """ Entrées : Fonction déclenchée par le bouton - pas d'entrée
        Sorties : Efface toutes les figures déjà tracée dans le canevas."""
    global couleur


##----- Création de la fenêtre -----##
fen = Tk()
fen.title('Cercles')


##----- Création des boutons -----##
bouton_quitter = Button(fen, text='Quitter', command=fen.destroy)
bouton_quitter.grid(row = 1, column = 3, padx=3, pady=3)

bouton_tracer = Button(fen, text='Tracer un cercle', command=tracer)
bouton_tracer.grid(row = 1, column = 0, padx=3, pady=3)

bouton_changer = Button(fen, text='Nouvelle couleur', command=change_couleur)
bouton_changer.grid(row = 1, column = 1, padx=3, pady=3)

bouton_effacer = Button(fen, text='Effacer', command=effacer)
bouton_effacer.grid(row = 1, column = 2, padx=3, pady=3)


##----- Création des canevas -----##
dessin = Canvas(fen, width=302, height=302, bg='white')
dessin.grid(row = 0, column = 0, columnspan=4, padx=3, pady=3)


##----- Programme principal -----##
fen.mainloop()                  # Boucle d'attente des événements
				
  • Fonction n°1
  • Fonction n°2
  • Fonction n°3
  • Une solution

Il ne faut pas oublier d'importer le module random !!!


def tracer():
    """ Entrées : Fonction déclenchée par le bouton - pas d'entrée
        Sorties : Trace dans le canevas un cercle de couleur, de centre un
            point de coordonnées aléatoires (entre 50 et 250) et de rayon
            aléatoire (entre 10 et 40)."""
    global couleur              		# Variable globale, modifiée dans le programme principal
    x0, y0 = randint(50,250), randint(50,250)
    r = randint(10,40)
    dessin.create_oval(x0-r, y0-r, x0+r+1, y0+r+1, width=2, outline=couleur)
						

def change_couleur():
    """ Entrées : Fonction déclenchée par le bouton - pas d'entrée
        Sorties : Permet le changement aléatoire de couleur parmi les 7 d'une liste donnée."""
    global couleur              		# Variable globale, modifiée dans le programme principal
    couleur=choice(['purple', 'cyan', 'green', 'red', 'blue', 'orange', 'black'])
						

def effacer():
    """ Entrées : Fonction déclenchée par le bouton - pas d'entrée
        Sorties : Efface toutes les figures déjà tracée dans le canevas."""
    global couleur
    dessin.delete(ALL)
    couleur='blue'
						

##----- Importation des Modules -----##
from tkinter import *
from random import *


##----- Variables globales -----##
couleur = 'blue'                      	# Couleur par défaut

##-----Définition des fonctions-----##
def tracer():
    """ Entrées : Fonction déclenchée par le bouton - pas d'entrée
        Sorties : Trace dans le canevas un cercle de couleur, de centre un
            point de coordonnées aléatoires (entre 50 et 250) et de rayon
            aléatoire (entre 10 et 40)."""
    global couleur              		# Variable globale, modifiée dans le programme principal
    x0, y0 = randint(50,250), randint(50,250)
    r = randint(10,40)
    dessin.create_oval(x0-r, y0-r, x0+r+1, y0+r+1, width=2, outline=couleur)
	

def change_couleur():
    """ Entrées : Fonction déclenchée par le bouton - pas d'entrée
        Sorties : Permet le changement aléatoire de couleur parmi les 7 d'une liste donnée."""
    global couleur              		# Variable globale, modifiée dans le programme principal
    couleur=choice(['purple', 'cyan', 'green', 'red', 'blue', 'orange', 'black'])


def effacer():
    """ Entrées : Fonction déclenchée par le bouton - pas d'entrée
        Sorties : Efface toutes les figures déjà tracée dans le canevas."""
    global couleur
    dessin.delete(ALL)
    couleur='blue'


##----- Création de la fenêtre -----##
fen = Tk()
fen.title('Cercles')


##----- Création des boutons -----##
bouton_quitter = Button(fen, text='Quitter', command=fen.destroy)
bouton_quitter.grid(row = 1, column = 3, padx=3, pady=3)

bouton_tracer = Button(fen, text='Tracer un cercle', command=tracer)
bouton_tracer.grid(row = 1, column = 0, padx=3, pady=3)

bouton_changer = Button(fen, text='Nouvelle couleur', command=change_couleur)
bouton_changer.grid(row = 1, column = 1, padx=3, pady=3)

bouton_effacer = Button(fen, text='Effacer', command=effacer)
bouton_effacer.grid(row = 1, column = 2, padx=3, pady=3)


##----- Création des canevas -----##
dessin = Canvas(fen, width=302, height=302, bg='white')
dessin.grid(row = 0, column = 0, columnspan=4, padx=3, pady=3)


##----- Programme principal -----##
fen.mainloop()                  # Boucle d'attente des événements
						

Tirage du loto

Préambule

On considère une liste de n nombres dans laquelle on souhaite tirer aléatoirement k nombres tous distincts. Écrivez un programme qui implémente l'algorithme ci-dessous :

  • On choisit aléatoirement un entier dans la liste.
  • On ()place cet entier à la fin de la liste.
  • On choisit aléatoirement un nouvel entier dans les n-1 premiers nombres de la liste puis on ()place cet entier à la fin de la liste.
  • On choisit aléatoirement un nouvel entier dans les n-2 premiers nombres de la liste puis on ()place cet entier à la fin de la liste.
  • etc... jusqu'à abtenir aléatoirement k nombres, tous distincts.
  • Une piste
  • Une solution
La fonction randint() du module random peut s'avérer utile...

##----- Importation des modules -----##
from random import *

##----- Variables et constantes -----##
n = 20
k = 7
liste = list(range(n))		    # Liste des entiers de 0 à n-1
result = []					    # Liste des nombres tirés au hasard

##----- Programme principal -----##
for i in range(k):
    fin = len(liste)-1-i        # Dernier indice "intéressant"
    indice = randint(0, fin)    # Indice aléatoire
    nombre = liste[indice]
    result.append(nombre)
    liste[indice], liste[fin] = liste[fin], liste[indice]
print(result)
print(liste)
						

Programme à réaliser

On considère une grille de n lignes et p colonnes. Ci-contre, un exemple de grille de 3 lignes et 7colonnes, dans laquelle les cases sont numérotées de 1 à n*p en partant de la case haut-gauche jusqu'à la case bas-droite, en lisant ligne par ligne.

Concevoir une interface graphique qui ressemble à celle de l'animation ci-dessous. Le programme doit respecter les contraintes suivantes :

  1. Le nombre de lignes et le nombre de colonnes doivent être facilement modifiables.
  2. La grille de départ est « vide ».
  3. Le clic sur le bouton [Démarrer] génère un entier aléatoire compris entre 1 et n*p parmi ceux qu n'ont pas encore été générés.
  4. Une fois l'entier « tiré au sort » grâce à la méthode du préambule, celui-ci est affiché à sa bonne place dans la grille.
  5. On recommence (le texte du bouton [Démarrer] a été modifié) jusqu'à obtenir k entiers distincts, où k est un entier facilement modifiable.
  6. Le bouton [Démarrer] devient un bouton [Effacer] qui permet de ré-initialiser le programme.
  • Grille remplie de nombre
  • Une solution
  • Une autre solution

##----- Importation des Modules -----##
from tkinter import *

##----- Variables globales -----##
c = 30                          # Longueur d'un côté d'une case
n = 3                           # Nombre de cases par ligne
p = 8                           # Nombre de cases par colonne
cases = []                      # Liste contenant les objets cases
textes = []                     # Liste contenant les textes affichés

##----- Création de la fenêtre -----##
fen = Tk()
fen.title('loto')

##----- Création des boutons -----##
bouton_quitter = Button(fen, text='Quitter', command=fen.destroy)
bouton_quitter.grid(row = 1, column = 1, sticky=W+E, padx=3, pady=3)

##----- Création des canevas -----##
dessin = Canvas(fen, width = p*c+2, height = n*c+2, bg = 'white')
dessin.grid(row = 0, column = 0, columnspan=2, padx=3, pady=3)

##----- Création des figures -----##
for ligne in range(n):          # Les cases de chaque ligne seront stockées dans "transit"
    transit=[]
    transit2=[]
    for colonne in range(p):    # Conception des cases d'une ligne
        transit.append(dessin.create_rectangle(colonne*c+2, ligne*c+2, (colonne+1)*c+2, (ligne+1)*c+2))
        transit2.append(dessin.create_text(colonne*c+c/2+2, ligne*c+c/2+2, text=str(p*ligne+colonne+1)))
    cases.append(transit)       # Ajout de la ligne à la liste principale
    textes.append(transit2)       # Ajout de la ligne à la liste principale


##----- Programme principal -----##
fen.mainloop()                  # Boucle d'attente des événements
						

##----- Importation des Modules -----##
from tkinter import *
from random import *


##----- Variables globales -----##
n = 3                               # Nombre de cases par ligne
p = 8                               # Nombre de cases par colonne
liste_nb = list(range(1, n*p+1))    # Liste des entiers de 1 à n*p
k = 5                               # Nombre de valeurs tirées au sort
numero = 1                          # Numéro du nombre tiré au sort

c = 30                              # Longueur d'un côté d'une case
cases = []                          # Liste contenant les objets cases
textes = []                         # Liste contenant les textes affichés


##----- Définition des Fonctions -----##
def nb_aleat(num):
    """ Entrées: num est le numéro de l'entier tiré au sort
        Sorties: Renvoie un des éléments d'indice compris entre 0 et fin."""
    global liste_nb             # Modification dans le programme principal
    fin = len(liste_nb)-num
    indice = randint(0, fin)    # Indice aléatoire
    liste_nb[indice], liste_nb[fin] = liste_nb[fin], liste_nb[indice]
    return liste_nb[fin]


def tirage():
    """ Entrées: numero est le numéro de l'entier tiré au sort, valeur
            est le nombre total de numéros à obtenir
        Sorties: Modification de l'aspect du bouton et affichage du
            numéro dans le canevas."""
    global numero, k, p, liste_nb, textes
    if numero <= k:
        nombre = nb_aleat(numero)
        colonne = (nombre-1) % p
        ligne = (nombre-1) // p
        textes.append(dessin.create_text(colonne*c+c/2+2, ligne*c+c/2+2, text=str(nombre)))
        if numero < k:
            bouton_start.configure(text='Encore ?')
        else:
            bouton_start.configure(text='Effacer ?')
        numero += 1

    else:
        bouton_start.configure(text='Démarrer ?')
        for i in range(k):
            dessin.delete(textes[i])
        liste_nb = list(range(1, n*p+1))    # Liste des entiers de 1 à n*p
        numero = 1                          # Numéro du nombre tiré au sort
        textes = []                         # Liste contenant les textes affichés


##----- Création de la fenêtre -----##
fen = Tk()
fen.title('loto')


##----- Création des boutons -----##
bouton_quitter = Button(fen, text='Quitter', command=fen.destroy)
bouton_quitter.grid(row = 1, column = 1, sticky=W+E, padx=3, pady=3)

bouton_start = Button(fen, text='Démarrer', command= tirage)
bouton_start.grid(row = 1, column = 0, sticky=W+E, padx=3, pady=3)


##----- Création des canevas -----##
dessin = Canvas(fen, width = p*c+2, height = n*c+2, bg = 'white')
dessin.grid(row = 0, column = 0, columnspan=2, padx=3, pady=3)


##----- Création des figures -----##
for ligne in range(n):          # Les cases de chaque ligne seront stockées dans "transit"
    transit=[]
    for colonne in range(p):    # Conception des cases d'une ligne
        transit.append(dessin.create_rectangle(colonne*c+2, ligne*c+2, (colonne+1)*c+2, (ligne+1)*c+2))
    cases.append(transit)       # Ajout de la ligne à la liste principale


##----- Programme principal -----##
fen.mainloop()                  # Boucle d'attente des événements
						

Les paramètres de ré-initialisation peuvent être définis dans une fonction, ce qui facilite la maintenance du programme.


##----- Importation des Modules -----##
from tkinter import *
from random import *


##----- Variables globales -----##
n = 3                               # Nombre de cases par ligne
p = 8                               # Nombre de cases par colonne
liste_nb = list(range(1, n*p+1))    # Liste des entiers de 1 à n*p
k = 5                               # Nombre de valeurs tirées au sort
numero = 1                          # Numéro du nombre tiré au sort

c = 30                              # Longueur d'un côté d'une case
cases = []                          # Liste contenant les objets cases
textes = []                         # Liste contenant les textes affichés


##----- Définition des Fonctions -----##
def reinit():
    """ Entrées: La fonction agit sur des variables globales
        Sorties: Renvoie un des éléments d'indice compris entre 0 et fin."""
    global liste_nb, textes, numero
    bouton_start.configure(text='Démarrer ?')
    liste_nb = list(range(1, n*p+1))    # Liste des entiers de 1 à n*p
    numero = 1                          # Numéro du nombre tiré au sort
    textes = []                         # Liste contenant les textes affichés


def nb_aleat(num):
    """ Entrées: num est le numéro de l'entier tiré au sort
        Sorties: Renvoie un des éléments d'indice compris entre 0 et fin."""
    global liste_nb             # Modification dans le programme principal
    fin = len(liste_nb)-num
    indice = randint(0, fin)    # Indice aléatoire
    liste_nb[indice], liste_nb[fin] = liste_nb[fin], liste_nb[indice]
    return liste_nb[fin]


def tirage():
    """ Entrées: numero est le numéro de l'entier tiré au sort, valeur
            est le nombre total de numéros à obtenir
        Sorties: Modification de l'aspect du bouton et affichage du
            numéro dans le canevas."""
    global numero, k, p, liste_nb, textes
    if numero <= k:
        nombre = nb_aleat(numero)
        colonne = (nombre-1) % p
        ligne = (nombre-1) // p
        textes.append(dessin.create_text(colonne*c+c/2+2, ligne*c+c/2+2, text=str(nombre)))
        if numero < k:
            bouton_start.configure(text='Encore ?')
        else:
            bouton_start.configure(text='Effacer ?')
        numero += 1

    else:
        for i in range(k):
            dessin.delete(textes[i])
        reinit()
        


##----- Création de la fenêtre -----##
fen = Tk()
fen.title('loto')


##----- Création des boutons -----##
bouton_quitter = Button(fen, text='Quitter', command=fen.destroy)
bouton_quitter.grid(row = 1, column = 1, sticky=W+E, padx=3, pady=3)

bouton_start = Button(fen, text='Démarrer', command= tirage)
bouton_start.grid(row = 1, column = 0, sticky=W+E, padx=3, pady=3)


##----- Création des canevas -----##
dessin = Canvas(fen, width = p*c+2, height = n*c+2, bg = 'white')
dessin.grid(row = 0, column = 0, columnspan=2, padx=3, pady=3)


##----- Création des figures -----##
for ligne in range(n):          # Les cases de chaque ligne seront stockées dans "transit"
    transit=[]
    for colonne in range(p):    # Conception des cases d'une ligne
        transit.append(dessin.create_rectangle(colonne*c+2, ligne*c+2, (colonne+1)*c+2, (ligne+1)*c+2))
    cases.append(transit)       # Ajout de la ligne à la liste principale


##----- Programme principal -----##
fen.mainloop()                  # Boucle d'attente des événements