1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
| import tkinter as tk
from tkinter import messagebox
from tkinter import font
import re
# --- Gestion des placeholders ---
def setup_placeholder(entry, placeholder_text):
"""Configure un placeholder pour un champ Entry"""
entry.insert(0, placeholder_text)
entry.config(fg="grey")
def on_focus_in(event):
if entry.get() == placeholder_text:
entry.delete(0, tk.END)
entry.config(fg="black")
def on_focus_out(event):
if entry.get() == "":
entry.insert(0, placeholder_text)
entry.config(fg="grey")
entry.bind("<FocusIn>", on_focus_in)
entry.bind("<FocusOut>", on_focus_out)
# --- Fonctions de validation ---
def valider_email(email):
"""Valide le format d'un email"""
pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
return re.match(pattern, email) is not None
def valider_telephone(telephone):
"""Valide le format d'un numéro de téléphone (FR, BE, ES)"""
if telephone == "": # Champ optionnel
return True
# Retirer les espaces pour la validation
tel_clean = telephone.replace(" ", "")
# Format avec indicatif international: 0033, 0032, 0034
# France: 0033 1 23 45 67 89 (11 chiffres après nettoyage)
# Belgique: 0032 4 12 34 56 78 (11 chiffres)
# Espagne: 0034 6 12 34 56 78 (11 chiffres)
pattern_international = r"^00(33|32|34)[1-9]\d{8}$"
# Format national français: 01 23 45 67 89 (10 chiffres)
pattern_national = r"^0[1-9]\d{8}$"
return re.match(pattern_international, tel_clean) or re.match(pattern_national, tel_clean)
def valider_date(date):
"""Valide le format jj/mm/aaaa"""
pattern = r"^\d{2}/\d{2}/\d{4}$"
return re.match(pattern, date) is not None
def valider_annee(annee):
"""Valide une année (4 chiffres)"""
return annee.isdigit() and len(annee) == 4
# --- Fonction pour récupérer les données lors du clic sur VALIDER ---
def valider_donnees():
# 1. Récupération des données
Lenom = entryNom.get()
Leprenom = entryPrenom.get()
Ladresse1 = entryAdresse1.get()
Ladresse2 = entryAdresse2.get()
Lecodepostal = entryCpostal.get()
Laville = entryVille.get()
Letelefixe = entryTphfixe.get()
Leteleport = entryTphport.get()
Lemail = entryMail.get()
Ladhesion = entryAdhesion.get()
Lanaissance = entryNaissance.get()
Laprofession = entryProfession.get()
Lalettre = entryLettre.get().upper()
Lacotisation = entryCotisation.get()
Larelance = entryRelance.get()
# 2. Vérifications
if Lenom == "Entrez votre nom ici" or Lenom == "":
messagebox.showwarning("Attention", "Veuillez entrer un nom valide")
entryNom.focus()
return
if Leprenom == "Entrez votre prénom ici" or Leprenom == "":
messagebox.showwarning("Attention", "Veuillez entrer un prénom valide")
entryPrenom.focus()
return
if Ladresse1 == "":
messagebox.showwarning("Attention", "Veuillez entrer une adresse")
entryAdresse1.focus()
return
if Lecodepostal == "":
messagebox.showwarning("Attention", "Veuillez entrer un code postal")
entryCpostal.focus()
return
if Laville == "":
messagebox.showwarning("Attention", "Veuillez entrer une ville")
entryVille.focus()
return
if Lemail != "" and not valider_email(Lemail):
messagebox.showwarning("Attention", "Format d'email invalide")
entryMail.focus()
return
# Validation des téléphones
if Letelefixe != "" and not valider_telephone(Letelefixe):
messagebox.showwarning(
"Attention",
"Format de téléphone fixe invalide\n\n"
"Exemples acceptés:\n"
" National: 01 23 45 67 89\n"
" International:\n"
" - France: 0033 1 23 45 67 89\n"
" - Belgique: 0032 4 12 34 56 78\n"
" - Espagne: 0034 6 12 34 56 78"
)
entryTphfixe.focus()
return
if Leteleport != "" and not valider_telephone(Leteleport):
messagebox.showwarning(
"Attention",
"Format de téléphone portable invalide\n\n"
"Exemples acceptés:\n"
" National: 06 12 34 56 78\n"
" International:\n"
" - France: 0033 6 12 34 56 78\n"
" - Belgique: 0032 4 12 34 56 78\n"
" - Espagne: 0034 6 12 34 56 78"
)
entryTphport.focus()
return
if Lanaissance != "" and not valider_date(Lanaissance):
messagebox.showwarning("Attention", "Format de date invalide (jj/mm/aaaa)")
entryNaissance.focus()
return
if Ladhesion != "" and not valider_annee(Ladhesion):
messagebox.showwarning("Attention", "Année d'adhésion invalide (4 chiffres)")
entryAdhesion.focus()
return
if Lacotisation != "" and not valider_annee(Lacotisation):
messagebox.showwarning("Attention", "Année de cotisation invalide (4 chiffres)")
entryCotisation.focus()
return
if Lalettre != "" and Lalettre not in ["O", "N"]:
messagebox.showwarning("Attention", "Lettre info doit être O ou N")
entryLettre.focus()
return
# 3. Afficher les données récupérées
message = (
f"Création de {Lenom} {Leprenom}\n"
f"Adresse 1: {Ladresse1}\n"
f"Adresse 2: {Ladresse2}\n"
f"CP/Ville: {Lecodepostal} {Laville}\n"
f"Fixe/Port: {Letelefixe} / {Leteleport}\n"
f"Mail: {Lemail}\n"
f"Adhésion: {Ladhesion}, Naissance: {Lanaissance}, Profession: {Laprofession}\n"
f"Lettre: {Lalettre}, Cotisation: {Lacotisation}, Relance: {Larelance}"
)
messagebox.showinfo("Données saisies", message)
messagebox.showinfo(
"Succès", f"Adhérent: {Lenom} {Leprenom} enregistré avec succès."
)
# 4. Effacer les champs après validation
effacer_formulaire()
def effacer_formulaire():
"""Efface tous les champs du formulaire"""
entryNom.delete(0, tk.END)
entryPrenom.delete(0, tk.END)
entryAdresse1.delete(0, tk.END)
entryAdresse2.delete(0, tk.END)
entryCpostal.delete(0, tk.END)
entryVille.delete(0, tk.END)
entryTphfixe.delete(0, tk.END)
entryTphport.delete(0, tk.END)
entryMail.delete(0, tk.END)
entryAdhesion.delete(0, tk.END)
entryNaissance.delete(0, tk.END)
entryProfession.delete(0, tk.END)
entryLettre.delete(0, tk.END)
entryCotisation.delete(0, tk.END)
entryRelance.delete(0, tk.END)
# Réinitialiser les placeholders
setup_placeholder(entryNom, "Entrez votre nom ici")
setup_placeholder(entryPrenom, "Entrez votre prénom ici")
def modifier_donnees():
"""Fonction pour modifier un adhérent (à implémenter)"""
messagebox.showinfo("Modifier", "Fonction MODIFIER à implémenter")
def supprimer_donnees():
"""Fonction pour supprimer un adhérent"""
reponse = messagebox.askyesno(
"Confirmation", "Voulez-vous vraiment supprimer cet adhérent ?"
)
if reponse:
effacer_formulaire()
messagebox.showinfo("Suppression", "Adhérent supprimé")
# Fonction helper pour créer des lignes avec label et entry
def create_entry_row(parent, label_text, entry_width, row_num):
"""Crée une ligne avec label aligné à gauche et entry"""
label = tk.Label(parent, text=label_text, anchor="w", bg="#f0f0f0")
label.grid(row=row_num, column=0, sticky="w", padx=10, pady=5)
entry = tk.Entry(parent, width=entry_width)
entry.grid(row=row_num, column=1, sticky="w", padx=10, pady=5)
return entry
def create_tip_label(parent, text, row_num):
"""Crée un label de conseil en rouge"""
tip = tk.Label(parent, text=text, fg="red", font=("Helvetica", 8), bg="#f0f0f0")
tip.grid(row=row_num, column=2, sticky="w", padx=5, pady=5)
# ----------------- Configuration de la fenêtre principale -----------------
app = tk.Tk()
app.title("Assistance Humanitaire Internationale")
app.configure(bg="#e8f4f8")
# Polices
titre_font = font.Font(family="Helvetica", size=18, weight="bold")
section_font = font.Font(family="Helvetica", size=11, weight="bold")
# ----------------- En-tête avec titre -----------------
frame_header = tk.Frame(app, bg="#4a90e2", height=80)
frame_header.pack(fill="x", pady=(0, 15))
labelTitre = tk.Label(
frame_header,
text="SAISIE ADHÉRENTS",
fg="white",
bg="#4a90e2",
font=titre_font,
pady=15
)
labelTitre.pack()
# ----------------- Conteneur principal avec padding -----------------
main_container = tk.Frame(app, bg="#e8f4f8")
main_container.pack(fill="both", expand=True, padx=20, pady=10)
# ----------------- Frame pour les deux colonnes -----------------
content_frame = tk.Frame(main_container, bg="#e8f4f8")
content_frame.pack(fill="both", expand=True)
# ========== COLONNE GAUCHE : COORDONNÉES ==========
frame_coordonnees = tk.LabelFrame(
content_frame,
text="📋 Coordonnées",
font=section_font,
bg="#f0f0f0",
relief="groove",
borderwidth=2,
padx=15,
pady=15
)
frame_coordonnees.grid(row=0, column=0, sticky="nsew", padx=(0, 10))
# Nom et Prénom
entryNom = create_entry_row(frame_coordonnees, "Nom :", 35, 0)
setup_placeholder(entryNom, "Entrez votre nom ici")
create_tip_label(frame_coordonnees, "Utiliser le - pour noms composés", 0)
entryPrenom = create_entry_row(frame_coordonnees, "Prénom :", 35, 1)
setup_placeholder(entryPrenom, "Entrez votre prénom ici")
create_tip_label(frame_coordonnees, "Utiliser le - pour prénoms composés", 1)
# Séparateur
tk.Frame(frame_coordonnees, height=2, bg="#d0d0d0").grid(row=2, column=0, columnspan=3, sticky="ew", pady=10, padx=10)
# Adresses
entryAdresse1 = create_entry_row(frame_coordonnees, "Adresse 1 :", 45, 3)
entryAdresse2 = create_entry_row(frame_coordonnees, "Adresse 2 :", 45, 4)
# Code Postal et Ville sur la même ligne
row_cp_ville = tk.Frame(frame_coordonnees, bg="#f0f0f0")
row_cp_ville.grid(row=5, column=0, columnspan=3, sticky="w", pady=5)
tk.Label(row_cp_ville, text="Code Postal :", anchor="w", bg="#f0f0f0").pack(side="left", padx=(10, 5))
entryCpostal = tk.Entry(row_cp_ville, width=10)
entryCpostal.pack(side="left", padx=5)
tk.Label(row_cp_ville, text="Ville :", anchor="w", bg="#f0f0f0").pack(side="left", padx=(20, 5))
entryVille = tk.Entry(row_cp_ville, width=30)
entryVille.pack(side="left", padx=5)
# Séparateur
tk.Frame(frame_coordonnees, height=2, bg="#d0d0d0").grid(row=6, column=0, columnspan=3, sticky="ew", pady=10, padx=10)
# Téléphones
entryTphfixe = create_entry_row(frame_coordonnees, "Tél. fixe :", 20, 7)
create_tip_label(frame_coordonnees, "Ex: 01 23 45 67 89 ou 0033 1 23 45 67 89", 7)
entryTphport = create_entry_row(frame_coordonnees, "Tél. portable :", 20, 8)
create_tip_label(frame_coordonnees, "Ex: 06 12 34 56 78 ou 0033 6 12 34 56 78", 8)
# Mail
entryMail = create_entry_row(frame_coordonnees, "Email :", 40, 9)
# ========== COLONNE DROITE : INFORMATIONS ADHÉSION ==========
frame_adhesion = tk.LabelFrame(
content_frame,
text="👤 Informations Adhésion",
font=section_font,
bg="#f0f0f0",
relief="groove",
borderwidth=2,
padx=15,
pady=15
)
frame_adhesion.grid(row=0, column=1, sticky="nsew", padx=(10, 0))
# Champs adhésion
entryAdhesion = create_entry_row(frame_adhesion, "Année Adhésion :", 10, 0)
entryNaissance = create_entry_row(frame_adhesion, "Date de naissance :", 15, 1)
create_tip_label(frame_adhesion, "Format: jj/mm/aaaa", 1)
entryProfession = create_entry_row(frame_adhesion, "Profession :", 30, 2)
# Séparateur
tk.Frame(frame_adhesion, height=2, bg="#d0d0d0").grid(row=3, column=0, columnspan=3, sticky="ew", pady=10, padx=10)
entryLettre = create_entry_row(frame_adhesion, "Lettre info :", 5, 4)
create_tip_label(frame_adhesion, "Tapez O ou N", 4)
entryCotisation = create_entry_row(frame_adhesion, "Année Cotisation :", 10, 5)
entryRelance = create_entry_row(frame_adhesion, "Relance Cotisation :", 5, 6)
# Configuration des poids pour expansion
content_frame.columnconfigure(0, weight=1)
content_frame.columnconfigure(1, weight=1)
# ----------------- Frame des Boutons -----------------
frame_boutons = tk.Frame(app, bg="#e8f4f8", pady=20)
frame_boutons.pack(side="bottom", fill="x")
# Style des boutons avec plus de padding
button_style = {"font": ("Helvetica", 10, "bold"), "padx": 20, "pady": 8, "width": 12}
Button0 = tk.Button(
frame_boutons,
text="✓ VALIDER",
command=valider_donnees,
bg="#90EE90",
**button_style
)
Button0.pack(side="left", padx=15)
Button1 = tk.Button(
frame_boutons,
text="✎ MODIFIER",
command=modifier_donnees,
bg="#87CEEB",
**button_style
)
Button1.pack(side="left", padx=15)
Button2 = tk.Button(
frame_boutons,
text="✗ SUPPRIMER",
command=supprimer_donnees,
bg="#FFB6C1",
**button_style
)
Button2.pack(side="left", padx=15)
Button3 = tk.Button(
frame_boutons,
text="⏻ QUITTER",
command=app.quit,
bg="#FFA07A",
**button_style
)
Button3.pack(side="right", padx=15)
app.mainloop() |