"""
Calculateur de PGCD pour une liste de nombres entiers
======================================================
Application graphique utilisant tkinter pour calculer le Plus Grand Commun Diviseur
d'une liste de nombres entiers saisis par l'utilisateur.
Auteur: wouf
Version originale: août 2006
Version modernisée: janvier 2026
"""
import tkinter as tk
from tkinter import messagebox
from typing import List
# ============================================================================
# CONSTANTES DE L'APPLICATION
# ============================================================================
TEXTE_PRESENTATION = """Cet exemple en Python, qui utilise tkinter, sert à calculer
le pgcd d'une liste de nombres entiers.
Entrez une liste de nombres entiers séparés par des virgules :
"""
COULEUR_TITRE = "#2C3E50"
COULEUR_RESULTAT = "#E74C3C"
COULEUR_BOUTON = "#ECF0F1"
💻 Lire la suite
▼
# ============================================================================
# FONCTIONS DE VALIDATION ET DE CALCUL
# ============================================================================
def est_entier(chaine: str) -> bool:
"""
Vérifie si une chaîne de caractères représente un nombre entier positif.
Cette fonction examine chaque caractère de la chaîne pour s'assurer
qu'il s'agit bien d'un chiffre. C'est une approche pédagogique,
mais en production on utiliserait plutôt str.isdigit() ou un try/except.
Args:
chaine: La chaîne à vérifier
Returns:
True si la chaîne représente un entier positif, False sinon
Exemples:
est_entier("123") retourne True
est_entier("12a3") retourne False
est_entier("") retourne False
"""
# Gestion du cas de la chaîne vide
if not chaine or chaine == "":
return False
# Ensemble des chiffres valides (plus pythonique que le tuple original)
chiffres_valides = set('0123456789')
# Vérifie que tous les caractères sont des chiffres
# Alternative moderne: return chaine.isdigit()
return all(caractere in chiffres_valides for caractere in chaine)
def calculer_pgcd_deux_nombres(a: int, b: int) -> int:
"""
Calcule le PGCD de deux nombres par soustractions successives.
Cette méthode pédagogique utilise l'algorithme d'Euclide par soustractions.
À chaque itération, on remplace le plus grand nombre par la différence
des deux nombres, jusqu'à ce qu'ils soient égaux.
Note: En Python moderne, on utiliserait plutôt math.gcd(a, b)
qui implémente l'algorithme d'Euclide par divisions (plus rapide).
Args:
a: Premier nombre entier
b: Deuxième nombre entier
Returns:
Le PGCD des deux nombres
Exemple:
calculer_pgcd_deux_nombres(48, 18) retourne 6
"""
# Boucle jusqu'à ce que les deux nombres soient égaux
while a != b:
# Si a est plus petit que b, on les échange
# Cette ligne utilise l'affectation multiple (tuple unpacking)
if a < b:
a, b = b, a
# On soustrait le plus petit du plus grand
# a devient (a-b), b reste b
a = a - b
# Quand a == b, on a trouvé le PGCD
return a
def calculer_pgcd_liste(nombres: List[int]) -> int:
"""
Calcule le PGCD d'une liste de nombres entiers.
Le PGCD de plusieurs nombres est calculé de manière itérative:
- On commence avec le PGCD des deux premiers nombres
- Puis on calcule le PGCD de ce résultat avec le troisième nombre
- Et ainsi de suite jusqu'au dernier nombre
Propriété mathématique utilisée:
pgcd(a, b, c) = pgcd(pgcd(a, b), c)
Args:
nombres: Liste d'entiers dont on veut calculer le PGCD
Returns:
Le PGCD de tous les nombres de la liste
Exemple:
calculer_pgcd_liste([12, 18, 24]) retourne 6
"""
# On commence avec le premier nombre de la liste
pgcd_courant = nombres[0]
# On parcourt tous les nombres de la liste
for nombre in nombres:
# On calcule le PGCD du résultat courant avec le nombre suivant
pgcd_courant = calculer_pgcd_deux_nombres(pgcd_courant, nombre)
return pgcd_courant
# ============================================================================
# CLASSE PRINCIPALE DE L'APPLICATION
# ============================================================================
class CalculateurPGCD:
"""
Classe principale gérant l'interface graphique et la logique métier
du calculateur de PGCD.
Cette classe encapsule tous les éléments de l'interface (fenêtre, widgets)
et les méthodes de traitement. C'est plus propre que d'avoir des variables
globales comme dans l'ancienne version.
"""
def __init__(self, fenetre_principale: tk.Tk):
"""
Initialise l'application avec tous ses composants graphiques.
Args:
fenetre_principale: La fenêtre Tk principale de l'application
"""
self.fenetre = fenetre_principale
self.fenetre.title("Calculateur de PGCD - par wouf")
# Configuration de la fenêtre pour une meilleure présentation
self.fenetre.resizable(False, False) # Taille fixe
# Création de tous les widgets de l'interface
self._creer_interface()
def _creer_interface(self):
"""
Crée et dispose tous les widgets de l'interface graphique.
Cette méthode privée (préfixe _) organise l'interface en plusieurs sections:
- Un label de présentation en haut
- Un champ de saisie pour entrer les nombres
- Un bouton pour lancer le calcul
- Un label pour afficher le résultat
"""
# ---- Zone de présentation (en haut) ----
self.label_presentation = tk.Label(
self.fenetre,
text=TEXTE_PRESENTATION,
fg=COULEUR_TITRE,
font=("Arial", 10),
justify=tk.LEFT, # Alignement du texte à gauche
padx=20,
pady=10
)
self.label_presentation.pack()
# ---- Champ de saisie ----
# Frame pour centrer le champ de saisie
frame_saisie = tk.Frame(self.fenetre)
frame_saisie.pack(pady=10)
self.champ_saisie = tk.Entry(
frame_saisie,
width=40,
font=("Arial", 11),
relief=tk.SOLID,
borderwidth=1
)
self.champ_saisie.pack()
# IMPORTANT: On lie la touche Entrée AVANT de mettre le focus
# Cela garantit que l'événement est bien capturé
self.champ_saisie.bind("<Return>", self._valider_et_calculer)
self.champ_saisie.bind("<KP_Enter>", self._valider_et_calculer) # Entrée du pavé numérique
# Le curseur se positionne automatiquement dans le champ de saisie
self.champ_saisie.focus_set()
# ---- Bouton de calcul ----
self.bouton_calculer = tk.Button(
self.fenetre,
text="Calculer le PGCD",
bg=COULEUR_BOUTON,
font=("Arial", 10, "bold"),
padx=20,
pady=5,
cursor="hand2", # Change le curseur en main au survol
# On utilise une lambda pour passer None comme argument event
# Cela évite les problèmes de signature entre le clic et l'événement clavier
command=lambda: self._valider_et_calculer(None)
)
self.bouton_calculer.pack(pady=10)
# ---- Zone d'affichage du résultat ----
self.label_resultat = tk.Label(
self.fenetre,
text="",
fg=COULEUR_RESULTAT,
font=("Arial", 11, "bold"),
wraplength=400, # Retour à la ligne automatique
pady=10
)
self.label_resultat.pack()
def _valider_et_calculer(self, event=None):
"""
Valide la saisie de l'utilisateur et calcule le PGCD.
Cette méthode est appelée soit par un clic sur le bouton,
soit par la touche Entrée. Elle effectue les étapes suivantes:
1. Récupère et nettoie la saisie
2. Vérifie que tous les éléments sont des entiers valides
3. Si validation OK: calcule et affiche le PGCD
4. Si validation KO: affiche un message d'erreur
Args:
event: Événement tkinter (utilisé quand appelé via la touche Entrée)
None si appelé via le bouton
"""
# ---- ÉTAPE 1: Récupération et découpage de la saisie ----
saisie_utilisateur = self.champ_saisie.get()
# On découpe la chaîne selon les virgules et on retire les espaces
# strip() enlève les espaces avant et après chaque nombre
# Exemple: "12, 18, 24 " devient ["12", "18", "24"]
elements_saisis = [element.strip() for element in saisie_utilisateur.split(",")]
# ---- ÉTAPE 2: Validation de chaque élément ----
# Dictionnaire pour stocker les résultats de validation
# Clé: l'élément saisi, Valeur: True si valide, False sinon
resultats_validation = {}
for element in elements_saisis:
resultats_validation[element] = est_entier(element)
# ---- ÉTAPE 3: Identification des erreurs ----
# On construit un message d'erreur si certains éléments ne sont pas valides
elements_invalides = []
for element, est_valide in resultats_validation.items():
if not est_valide:
elements_invalides.append(element)
# ---- ÉTAPE 4: Affichage des erreurs ou calcul du résultat ----
if elements_invalides:
# Il y a des erreurs: on construit un message détaillé
message_erreur = "Les valeurs suivantes ne sont pas des entiers valides:\n\n"
for element in elements_invalides:
# Affiche "vide" si la chaîne est vide, sinon affiche la valeur
valeur_affichee = '"(vide)"' if element == "" else f'"{element}"'
message_erreur += f" • {valeur_affichee}\n"
message_erreur += "\nVeuillez entrer uniquement des nombres entiers séparés par des virgules."
# Affichage d'une boîte de dialogue d'avertissement
messagebox.showwarning("Saisie invalide", message_erreur)
# On efface le résultat précédent s'il y en avait un
self.label_resultat.config(text="")
# IMPORTANT: On remet le focus sur le champ de saisie
# après la fermeture de la messagebox
self.champ_saisie.focus_set()
else:
# Pas d'erreur: on peut calculer le PGCD
# Conversion de la liste de chaînes en liste d'entiers
# On utilise une list comprehension pour plus de clarté
liste_entiers = [int(element) for element in elements_saisis]
# Calcul du PGCD
resultat_pgcd = calculer_pgcd_liste(liste_entiers)
# Construction du message de résultat
message_resultat = (
f"Le PGCD de la liste d'entiers:\n"
f"{liste_entiers}\n"
f"est : {resultat_pgcd}"
)
# Affichage du résultat dans le label
self.label_resultat.config(text=message_resultat)
# On remet le focus sur le champ de saisie pour faciliter
# un nouveau calcul sans avoir à cliquer
self.champ_saisie.focus_set()
# Optionnel: on peut sélectionner tout le texte pour faciliter
# la saisie d'une nouvelle série de nombres
self.champ_saisie.select_range(0, tk.END)
def lancer(self):
"""
Lance la boucle principale de l'application.
Cette méthode démarre l'événement loop de tkinter qui attend
les interactions de l'utilisateur (clics, saisies clavier, etc.)
"""
self.fenetre.mainloop()
# ============================================================================
# POINT D'ENTRÉE DU PROGRAMME
# ============================================================================
def main():
"""
Fonction principale qui lance l'application.
Cette fonction crée la fenêtre principale et l'objet CalculateurPGCD,
puis lance l'application.
L'utilisation d'une fonction main() est une bonne pratique qui permet:
- De structurer le code
- D'éviter les variables globales
- De pouvoir importer ce module sans l'exécuter automatiquement
"""
# Création de la fenêtre principale tkinter
fenetre = tk.Tk()
# Création de l'application
app = CalculateurPGCD(fenetre)
# Lancement de l'application
app.lancer()
# Cette condition vérifie si le script est exécuté directement
# (et non importé comme module)
if __name__ == "__main__":
main()
Partager: