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 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704
|
import tkinter as tk
from tkinter import messagebox
from tkinter import font
import tkinter.font as font
from tkinter import *
import re
from datetime import date, datetime, timedelta #-- celle ci est nécessaire à a fonction de trie
#import datetime # celle-ci est nécessaire à l'affichage de la date en entête du menu (J'ai isolé les lignes de la date)
#C'est l que j'ai problème, je ne peux laisser ces deux lignes actives
from openpyxl import Workbook, load_workbook
import os
import logging
import pandas as pd
from openpyxl.utils import get_column_letter
import pandas as pd
from openpyxl.styles import NamedStyle
x = 0
#========================== AFFICHER CELLULES EXCEL ================================================
#----- Iterate through rows
#for row in sheet.iter_rows(min_row=1, max_row=5, values_only=True):
# print(row) # Returns a tuple of values
#----- Iterate through columns
#for column in sheet.iter_cols(min_col=1, max_col=3, values_only=True):
# print(column) # Returns a tuple of values
#----- Access a range of cells
#cell_range = sheet["A1:C5"]
#for row in cell_range:
# for cell in row:
# print(cell.value)
#==============================================================================
# --- 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")
#grey
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 PROPRES AUX VALIDATIONS =============================================================
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
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
#======================== SAISIE DES EFFECTIF ========================================
# ------ 1. Fonction pour RECUPERER LES DONNEES lors du clic sur VALIDER ===
def valider_donnees():
# 1. Récupération des données pour vérification
Lacivilite = entryCivilite.get()
Lenom = entryNom.get()
Leprenom = entryPrenom.get()
Ladhesion = entryAdhesion.get()
Lacotisation = entryCotisation.get()
Lestatut=entryStatut.get()
Ladresse1 = entryAdresse1.get()
Ladresse2 = entryAdresse2.get()
Lecodepostal = entryCpostal.get()
Laville = entryVille.get()
Lemail = entryMail.get()
Lanaissance = entryNaissance.get()
Lanation = entryLanation.get()
Laprofession = entryProfession.get()
Lobserv=entryObserv.get()
Larelance = entryRelance.get()
Letelefixe = entryTphfixe.get()
Leteleport = entryTphport.get()
Lalettre = entryLettre.get()
#------ 2. VERIFICATION de la SAISIE champ par champ ---------
if Lacivilite != "" and Lacivilite not in ["Mr", "Md", "Et"]:
messagebox.showwarning("Attention pour la civilité", "Utiliser Mr - Md - Et")
entryCivilite.focus()
return
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_date(Ladhesion):
messagebox.showwarning("Attention", "Format de date invalide (jj/mm/aaaa)")
entryAdhesion.focus()
return
if Lacotisation != "" and not valider_annee(Lacotisation):
messagebox.showwarning("Attention", "Année de cotisation invalide (4 chiffres)")
entryCotisation.focus()
return
if Larelance != "" and Larelance not in ["O", "N"]:
messagebox.showwarning("Attention", "Lettre info doit être O ou N")
entryLettre.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 {Lacivilite} {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}, Nationalité: {Lanation}, (Cotisation: {Lacotisation}, Relance: {Larelance}\n"
f"Statut: {Lestatut}, Observation: {Lobserv}"
)
messagebox.showinfo("Pour info: Données saisies", message)
#--- message pour OK saisie bloqué ----
#messagebox.showinfo(
# "Succès", f"Adhérent: {Lenom} {Leprenom} enregistré avec succès."
#)
#------ 4. Effacer les champs après validation
def effacer_formulaire():
"""Efface tous les champs du formulaire"""
entryCivilite.delete(0, tk.END)
entryNom.delete(0, tk.END)
entryPrenom.delete(0, tk.END)
entryAdhesion.delete(0, tk.END)
entryCotisation.delete(0, tk.END)
entryStatut.delete(0, tk.END)
entryAdresse1.delete(0, tk.END)
entryAdresse2.delete(0, tk.END)
entryCpostal.delete(0, tk.END)
entryVille.delete(0, tk.END)
entryMail.delete(0, tk.END)
entryNaissance.delete(0, tk.END)
entryLanation.delete(0, tk.END)
entryProfession.delete(0, tk.END)
entryObserv.delete(0, tk.END)
entryRelance.delete(0, tk.END)
entryTphfixe.delete(0, tk.END)
entryTphport.delete(0, tk.END)
entryLettre.delete(0, tk.END)
# Réinitialiser les placeholders
setup_placeholder(entryNom, "Entrez votre nom ici")
setup_placeholder(entryPrenom, "Entrez votre prénom ici")
#=============== Sous/Fonctions BOUTONS "VALIDER" du s/menu EFFECTIF =====================================
# 1 ouvrir le classeur ---
def ouvrir_classeur():
wb = load_workbook('donnees.xlsm', keep_vba=True, read_only=False, data_only=False)
sheet = wb["Effectif"]
return wb, sheet
#---------------------------------------------
# 2 Trouver la première ligne vide en parcourant la colonne A
def dern_ligne():
wb = load_workbook("donnees.xlsm", keep_vba=True)
sheet = wb["Effectif"]
last_row = sheet.max_row
while last_row > 1 and sheet.cell(row=last_row, column=2).value is None:
last_row -= 1
last_row += 1 # ligne vide suivante
x = last_row
return wb, sheet, x
# ----------------------------------------------
# 3 enregistrer dans le classeur Excel feuille EFFECTIF
def enregistrer_dans_excel():
messagebox.showinfo("Enregistrement", "Patientez SVP")
# 1. Récupération des données pour enregistrement Excel
Lacivilite = entryCivilite.get()
Lenom = entryNom.get()
LenomM = Lenom.upper() # ---- passer en majuscule ---
Leprenom = entryPrenom.get()
LeprenomM = Leprenom.upper() # ---- passer en majuscule ---
#==== changer string saisie en format Date =============
Ladhesion = entryAdhesion.get()
Ladhesion2 = datetime.strptime(Ladhesion, "%d/%m/%Y").date()
#------------
Lacotisation = entryCotisation.get()
Lestatut = entryStatut.get()
Ladresse1 = entryAdresse1.get()
Ladresse1MI = Ladresse1.lower()
Ladresse2 = entryAdresse2.get()
Ladresse2MI = Ladresse2.lower()
Lecodepostal = entryCpostal.get()
Laville = entryVille.get()
LavilleM = Laville.upper() # ---- passer en majuscule -
Lemail = entryMail.get()
#==== changer string saisie en format Date =============
Lanaissance = entryNaissance.get()
Lanaissance2 = datetime.strptime(Lanaissance, "%d/%m/%Y").date()
#------------
Lanation = entryLanation.get().upper()
LanationM = Lanation.upper() # ---- passer en majuscule
Laprofession = entryProfession.get()
Lobserv=entryObserv.get()
Larelance = entryRelance.get()
LarelanceM = Larelance.upper() # ---- passer en majuscule ---
if LarelanceM == "O":
LarelanceM = "OUI"
if LarelanceM == "N":
LarelanceM = "NON"
Letelefixe = entryTphfixe.get()
Leteleport = entryTphport.get()
Lalettre = entryLettre.get().upper()
LalettreM = Lalettre.upper() # ---- passer en majuscule ---
if LalettreM == "O":
LalettreM = "OUI"
if LalettreM == "N":
LalettreM = "NON"
wb, sheet, x = dern_ligne() #--- se positionne sur la première ligne vide à partir de colonne A "fonction dern_ligne"
# Colonne A = 1
col = 1
sheet.cell(row=x, column=col).value = Lacivilite
col = 2
sheet.cell(row=x, column=col).value = LenomM
col = 3
sheet.cell(row=x, column=col).value = LeprenomM
col = 4
sheet.cell(row=x, column=col).value = Ladhesion2
col = 5
sheet.cell(row=x, column=col).value = Lacotisation
col = 6
sheet.cell(row=x, column=col).value = Lestatut
col = 7
sheet.cell(row=x, column=col).value = Ladresse1MI
col = 8
sheet.cell(row=x, column=col).value = Lecodepostal
col = 9
sheet.cell(row=x, column=col).value = LavilleM
col = 10
sheet.cell(row=x, column=col).value = Lemail
col = 11
sheet.cell(row=x, column=col).value = Lanaissance2
col = 12
sheet.cell(row=x, column=col).value = LanationM
col = 13
sheet.cell(row=x, column=col).value = Laprofession
col = 14
sheet.cell(row=x, column=col).value = Lobserv
col = 15
sheet.cell(row=x, column=col).value = LarelanceM
col = 16
sheet.cell(row=x, column=col).value = Letelefixe
col = 17
sheet.cell(row=x, column=col).value = Leteleport
col = 18
sheet.cell(row=x, column=col).value = LalettreM
wb.save("donnees.xlsm")
# 4 ----------------------------------------
def trier_feuille():
fichier = "Donnees.xlsm"
feuille = "Effectif"
# Charger la feuille en gardant les dates en datetime
df = pd.read_excel(fichier, sheet_name=feuille)
# Trier sur la colonne B (index 1)
df_trie = df.sort_values(by=df.columns[1], ascending=True)
# Réécrire la feuille triée dans le fichier sans casser les macros
with pd.ExcelWriter(fichier, engine="openpyxl", mode="a", if_sheet_exists="replace") as writer:
df_trie.to_excel(writer, sheet_name=feuille, index=False)
# Appliquer le format de date français aux colonnes datetime
workbook = writer.book
worksheet = writer.sheets[feuille]
for idx, col in enumerate(df_trie.columns, start=1):
if pd.api.types.is_datetime64_any_dtype(df_trie[col]):
for row in range(2, len(df_trie) + 2): # +2 car commence à 1 et header
cell = worksheet.cell(row=row, column=idx)
cell.number_format = 'DD/MM/YYYY'
# 5 ------------------------------------------
def quitter_application():
messagebox.showinfo("Patientez", "Réorganisation des EFFECTIFS en cours")
trier_feuille() # 1️⃣ On trie avant de quitter
app.quit() # 2️⃣ On ferme lapplication
#=============== Fonctions BOUTONS du s/menu EFFECTIF ========================================
# bouton EFFECTIF 1 . Enregistrer les données saisies dans "données" d'Excel
def valider_et_ouvrir():
valider_donnees() #--- valide les données "fonction valider_donnees"
# ouvrir_classeur() #--- ouvrir le classeur"
dern_ligne() #--- positionner sur 1ère ligne vide
enregistrer_dans_excel() #--- enregistre dans le classeur Excel
# trier_feuille() #--- Trier la feuille à partir colonne B les noms
effacer_formulaire() #--- réinitialise le formulaire de saisie fonction "effacer_formulaire"
wb = load_workbook('donnees.xlsm', keep_vba=True, read_only=False, data_only=False)
wb.close()
# bouton EFFECTIF 2 . Modifierimer les données dans "données" d'Excel
# bouton EFFECTIF 3 . Supprimer les données dans "données" d'Excel
# bouton EFFECTIF 4 . Envoyer carte
def envoyer_la_carte():
envoyer_carte()
# bouton EFFECTIF 5 . Envoyer mail
def envoyer_un_mail():
envoyer_mail()
#------------------------
# 2. Modifier adhérent
def modifier_donnees():
"""Fonction pour modifier un adhérent (à implémenter)"""
messagebox.showinfo("Modifier", "Fonction MODIFIER à implémenter")
#------------------------
# 3. Supprimer adhérent
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é")
#------------------------
# 4. Envoyer la carte d'adhérent
def envoyer_carte():
"""Fonction pour modifier un adhérent (à implémenter)"""
messagebox.showinfo("ENVOYER CARTE", "Fonction ENVOYER CARTE à implémenter")
#------------------------
# 5. Envoyer la mail à l'adhérent
def envoyer_mail():
"""Fonction pour modifier un adhérent (à implémenter)"""
messagebox.showinfo("Envoyer2", "Fonction ENVOYER MAIL à implémenter")
#===========================================================================================
# Fonction helper pour créer des lignes avec label et entry (avec polices agrandies)
def create_entry_row(parent, label_text, entry_width, row_num):
"""Crée une ligne avec label aligné à gauche et entry avec police agrandie"""
# Label avec police de taille 12 pour meilleure lisibilité
label = tk.Label(parent, text=label_text, anchor="w", bg="#f0f0f0",
font=("Helvetica", 11))
label.grid(row=row_num, column=0, sticky="w", padx=10, pady=5)
# Entry avec police de taille 12 et hauteur adaptée
entry = tk.Entry(parent, width=entry_width, font=("Helvetica", 12))
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 avec police agrandie"""
tip = tk.Label(parent, text=text, fg="red", font=("Helvetica", 10), bg="#f0f0f0")
tip.grid(row=row_num, column=2, sticky="w", padx=5, pady=5)
#========================= LE MENU PRINCIPAL ========================================
# ----------------- Configuration de la fenêtre principale -----------------
app = tk.Tk()
app.title("Assistance Humanitaire Internationale")
app.geometry("1400x800+10+10") # centrer la fenetre
#app.geometry("1400x800")
app.configure(bg="#e8f4f8")
#Chargement de l'image
photo = PhotoImage(file="D:\\Program\\IMAGE\\LOGO AHI.png")
#---- photo = photo.zoom(4, 4)# Double la taille de l'image
photo = photo.subsample(27, 27)# ----- Réduit la taille de moitié (facteur 2)
labelIcone= tk.Label(app, image=photo)
labelIcone.pack(side="top", pady=(10))
# pour INFO
# Chargement de l'image (commenté pour éviter les erreurs si le fichier n'existe pas)
# Vous pouvez décommenter ces lignes si votre image existe à cet emplacement
# photo = PhotoImage(file="D:\Program\IMAGE\LOGO AHI.png")
# photo = photo.subsample(27, 27)
# labelIcone= tk.Label(app, image=photo)
# labelIcone.pack(side="top", pady=(10))
# Polices
titre_font = font.Font(family="Helvetica", size=20, weight="bold")
section_font = font.Font(family="Helvetica", size=16, weight="bold") # Légèrement réduit pour l'équilibre
# ----------------- 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="GESTION DES EFFECTIFS",
fg="white",
bg="#4a90e2",
font=titre_font,
pady=15
)
labelTitre.pack()
# ======= CORRECTION: Affichage de la date système =======
# On récupère la date actuelle avec datetime.date.today()
# puis on la formate en français avec strftime()
#les lignes avec !!!! ont été isolées car conflit import datetime
#========================================================================
#!!!! date_actuelle = datetime.date.today()
# Formatage: %A = jour de la semaine, %d = jour, %B = mois, %Y = année
# Note: par défaut, strftime() utilise les noms anglais. Pour le français,
# vous pouvez utiliser locale ou formater manuellement
#!!!! date_formatee = date_actuelle.strftime("%d/%m/%Y")
# Alternative avec jour de la semaine en français (méthode simple)
jours_fr = ["Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi", "Dimanche"]
mois_fr = ["janvier", "février", "mars", "avril", "mai", "juin",
"juillet", "août", "septembre", "octobre", "novembre", "décembre"]
#!!!!jour_semaine = jours_fr[date_actuelle.weekday()]
#!!!! texte_date = f"Nous sommes le {jour_semaine} {date_actuelle.day} {mois_fr[date_actuelle.month-1]} {date_actuelle.year}"
# Création du label avec la date formatée et police agrandie
labelDate = tk.Label(
frame_header,
#text=texte_date,
fg="white",
bg="#4a90e2",
font=("Helvetica", 14), # Police agrandie pour meilleure lisibilité
pady=5
)
labelDate.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)
# ================== LA SAISIE =============================
# ========== COLONNE GAUCHE : COORDONNÉES ==========
frame_coordonnees = tk.LabelFrame(
content_frame,
text="📋 Coordonnées",
font=section_font,
bg="lightgreen",
relief="groove",
borderwidth=2,
padx=10,
pady=10
)
frame_coordonnees.grid(row=0, column=0, sticky="nsew", padx=(0, 10))
# Civilité
entryCivilite = create_entry_row(frame_coordonnees, "Civilité", 4, 0)
create_tip_label(frame_coordonnees, "ATTENTION: utiliser Mr ou Md ou Et", 0)
# Nom et Prénom
#labelNom = tk.Label(app, text="NOM", fg="blue", bg="#FF0")
entryNom = create_entry_row(frame_coordonnees, "Nom :", 27, 1)
setup_placeholder(entryNom, "Entrez votre nom ici")
create_tip_label(frame_coordonnees, "Utiliser le - pour noms composés", 1)
entryPrenom = create_entry_row(frame_coordonnees, "Prénom :", 27, 2)
setup_placeholder(entryPrenom, "Entrez votre prénom ici")
create_tip_label(frame_coordonnees, "Utiliser le - pour prénoms composés", 2)
# Séparateur
tk.Frame(frame_coordonnees, height=2, bg="#d0d0d0").grid(row=3, column=0, columnspan=3, sticky="ew", pady=10, padx=10)
# Adresses
entryAdresse1 = create_entry_row(frame_coordonnees, "Adresse 1 :", 33, 4)
entryAdresse2 = create_entry_row(frame_coordonnees, "Adresse 2 :", 33, 5)
# Code Postal et Ville sur la même ligne (avec polices agrandies)
entryCpostal = create_entry_row(frame_coordonnees, "Code Postal :", 7, 6)
entryVille = create_entry_row(frame_coordonnees, "Ville :", 33,7)
# Téléphones
entryTphfixe = create_entry_row(frame_coordonnees, "Tél. fixe :", 20, 8)
create_tip_label(frame_coordonnees, "Ex: 01 23 45 67 89 ou 0033 1 23 45 67 89", 8)
entryTphport = create_entry_row(frame_coordonnees, "Tél. portable :", 20, 9)
create_tip_label(frame_coordonnees, "Ex: 06 12 34 56 78 ou 0033 6 12 34 56 78", 9)
# Mail
entryMail = create_entry_row(frame_coordonnees, "Email :", 33, 10)
# ========== COLONNE DROITE : INFORMATIONS ADHÉSION ==========
frame_adhesion = tk.LabelFrame(
content_frame,
text="👤 Informations Adhésion",
font=section_font,
bg="lightgreen",
relief="groove",
borderwidth=2,
padx=15,
pady=15
)
frame_adhesion.grid(row=0, column=1, sticky="nsew", padx=(0, 0))
# Champs adhésion
entryAdhesion = create_entry_row(frame_adhesion, "Date Adhésion :", 12, 0)
create_tip_label(frame_adhesion, "Format: jj/mm/aaaa", 0)
entryNaissance = create_entry_row(frame_adhesion, "Date de naissance :", 12, 1)
create_tip_label(frame_adhesion, "Format: jj/mm/aaaa", 1)
entryProfession = create_entry_row(frame_adhesion, "Profession :", 33, 2)
entryLanation = create_entry_row(frame_adhesion, "Nationalité :", 3, 3)
create_tip_label(frame_adhesion, "En 2 lettres seulement", 3)
# Séparateur
#tk.Frame(frame_adhesion, height=2, bg="#d0d0d0").grid(row=6, column=0, columnspan=3, sticky="ew", pady=50, padx=10)
entryCotisation = create_entry_row(frame_adhesion, "Année Cotisation :", 5, 4)
entryLettre = create_entry_row(frame_adhesion, "Lettre info :", 3, 5)
create_tip_label(frame_adhesion, "Tapez O ou N", 5)
entryRelance = create_entry_row(frame_adhesion, "Relance Cotisation :", 3, 6)
entryStatut = create_entry_row(frame_adhesion, "Statut A.H.I", 33, 20)
entryObserv = create_entry_row(frame_adhesion, "Observation", 33, 60)
# 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 police agrandie
button_style = {"font": ("Helvetica", 10, "bold"), "padx": 10, "pady": 10, "width": 20}
# ---- command=renvoie à la fonction correspondante
Button0 = tk.Button(
frame_boutons,
text="✓ VALIDER",
command=valider_et_ouvrir,
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="✎ ENVOYER CARTE",
command=envoyer_la_carte,
bg="#F5F5DC",
**button_style
)
Button3.pack(side="left", padx=15)
Button4 = tk.Button(
frame_boutons,
text="✎ ENVOYER UN MAIL",
command=envoyer_un_mail,
bg="#F5F5DC",
**button_style
)
Button4.pack(side="left", padx=15)
Button5 = tk.Button(
frame_boutons,
text="⏻ QUITTER",
command=quitter_application, # 👈 on appelle notre fonction personnalisée
bg="#FFA07A",
**button_style
)
Button5.pack(side="right", padx=15)
app.mainloop() |