2021-11-18 20:41:58 +01:00
|
|
|
|
import tkinter.messagebox as messagebox
|
|
|
|
|
|
2021-11-20 02:41:38 +01:00
|
|
|
|
from tkinter import Scrollbar, Listbox, Entry, Button, Label, Frame, Tk
|
2021-11-19 11:41:37 +01:00
|
|
|
|
from re import sub
|
|
|
|
|
|
2021-11-18 20:41:58 +01:00
|
|
|
|
from users import Utilisateurs # import de mon fichier pour gérer la base de donnée
|
|
|
|
|
|
|
|
|
|
class GesMag:
|
2021-11-19 18:52:58 +01:00
|
|
|
|
"""Programme de Gestion d'une caise de magasin."""
|
2021-11-19 21:37:48 +01:00
|
|
|
|
def __init__(self) -> None:
|
|
|
|
|
"""Instancie quelques variables pour plus de clareté."""
|
2021-11-20 00:11:59 +01:00
|
|
|
|
self.parent = Tk() # fenêtre affiché à l'utilisateur
|
|
|
|
|
self.parent.resizable(False, False) # empêche la fenêtre d'être redimensionnée
|
|
|
|
|
self.f = Frame(self.parent) # `Frame` affiché à l'écran
|
2021-11-19 21:37:48 +01:00
|
|
|
|
|
2021-11-18 20:41:58 +01:00
|
|
|
|
def demarrer(self) -> None:
|
|
|
|
|
"""Lance le programme GesMag."""
|
2021-11-19 11:41:37 +01:00
|
|
|
|
self.font = ("Comfortaa", 14) # police par défaut
|
|
|
|
|
Utilisateurs().creationTable() # on créer la base de donnée si elle n'existe pas déjà
|
|
|
|
|
|
|
|
|
|
self._interfaceConnexion() # on créer la variable `self.f` qui est la frame a affiché
|
|
|
|
|
self.f.grid() # on affiche la frame
|
2021-11-19 00:47:01 +01:00
|
|
|
|
|
2021-11-19 11:41:37 +01:00
|
|
|
|
self.parent.mainloop() # on affiche la fenêtre
|
2021-11-19 00:47:01 +01:00
|
|
|
|
|
2021-11-19 11:41:37 +01:00
|
|
|
|
def motDePasseCorrect(self, motDPasse: str) -> tuple:
|
|
|
|
|
"""Détermine si un mot de passe suit la politique du programme ou non."""
|
|
|
|
|
if len(motDPasse) == 0: # si le champs est vide
|
|
|
|
|
return (False, "Mot de passe incorrect.")
|
|
|
|
|
if len(motDPasse) < 8: # si le mot de passe est plus petit que 8 caractères
|
|
|
|
|
return (False, "Un mot de passe doit faire 8 caractères minimum.")
|
|
|
|
|
"""
|
|
|
|
|
Pour le regex, je réfléchie comme dans la fonction `self.connexion`.
|
|
|
|
|
J'utilises pas `match` parce que je suis plus à l'aise avec `sub`.
|
|
|
|
|
"""
|
|
|
|
|
if not sub(r"[A-Z]", '', motDPasse) != motDPasse:
|
|
|
|
|
return (False, "Un mot de passe doit au moins contenir une lettre majuscule.")
|
|
|
|
|
if not sub(r"[a-z]", '', motDPasse) != motDPasse:
|
|
|
|
|
return (False, "Un mot de passe doit au moins contenir une lettre minuscule.")
|
|
|
|
|
if not sub(r" *?[^\w\s]+", '', motDPasse) != motDPasse:
|
|
|
|
|
return (False, "Un mot de passe doit au moins contenir un caractère spécial.")
|
|
|
|
|
|
|
|
|
|
return (True,) # si aucun des tests précédents n'est valide, alors le mot de passe est valide
|
2021-11-18 20:41:58 +01:00
|
|
|
|
|
|
|
|
|
def connexion(self, utilisateur: str, motDePasse: str):
|
2021-11-19 00:47:01 +01:00
|
|
|
|
"""Gère la connexion aux différentes interfaces de l'application."""
|
2021-11-19 11:41:37 +01:00
|
|
|
|
"""
|
|
|
|
|
Vérification nom d'utilisateur / mot de passe correctement entré
|
|
|
|
|
-> Pour le nom d'utilisateur on vérifie si le champs n'est pas vide
|
|
|
|
|
et si il y a bien que des lettres et des chiffres (le regex élimine tout
|
|
|
|
|
ce qui n'est pas ça, alors si la fonction `sub` renvoie pas exactement
|
|
|
|
|
la même chaîne de charactère alors c'est qu'il y avait un charactère
|
|
|
|
|
interdit dans le nom d'utilisateur).
|
|
|
|
|
-> Pour le mot de passe on demande à la fonction `motDePasseCorrect` pour
|
|
|
|
|
éviter de faire tout les tests ici.
|
|
|
|
|
"""
|
|
|
|
|
if len(utilisateur) == 0 or sub(r" *?[^\w\s]+", '', utilisateur) != utilisateur:
|
|
|
|
|
messagebox.showerror("Erreur", "Utilisateur incorrect.")
|
|
|
|
|
return
|
|
|
|
|
mdpOk = self.motDePasseCorrect(motDePasse)
|
|
|
|
|
if not mdpOk[0]:
|
|
|
|
|
messagebox.showerror("Erreur", mdpOk[1])
|
2021-11-18 20:41:58 +01:00
|
|
|
|
return
|
2021-11-19 00:47:01 +01:00
|
|
|
|
|
|
|
|
|
# Redirection vers la bonne interface
|
2021-11-19 23:57:11 +01:00
|
|
|
|
utilisateurBaseDeDonnee = Utilisateurs().verificationIdentifiants(utilisateur.lower(), motDePasse)
|
|
|
|
|
if utilisateurBaseDeDonnee[0] > 0:
|
|
|
|
|
if utilisateurBaseDeDonnee[1] == 0: # si le métier est "Manager"
|
|
|
|
|
self._interfaceManager(utilisateurBaseDeDonnee[0])
|
|
|
|
|
elif utilisateurBaseDeDonnee[1] == 1: # si le métier est "Caissier"
|
|
|
|
|
self._interfaceCaissier(utilisateurBaseDeDonnee[0])
|
|
|
|
|
else:
|
|
|
|
|
messagebox.showerror("Erreur", "Une erreur est survenue : métier inconnue.")
|
2021-11-18 20:41:58 +01:00
|
|
|
|
else:
|
2021-11-19 23:57:11 +01:00
|
|
|
|
messagebox.showerror("Erreur", "Utilisateur ou mot de passe incorrect.")
|
2021-11-18 20:41:58 +01:00
|
|
|
|
|
2021-11-19 20:13:53 +01:00
|
|
|
|
def dimensionsFenetre(self, nouveauX: int, nouveauY: int):
|
2021-11-19 20:14:41 +01:00
|
|
|
|
"""Permet de changer les dimensions de la fenêtre parent et la place au centre de l'écran."""
|
2021-11-19 20:13:53 +01:00
|
|
|
|
largeur = self.parent.winfo_screenwidth()
|
|
|
|
|
hauteur = self.parent.winfo_screenheight()
|
2021-11-19 20:12:06 +01:00
|
|
|
|
|
2021-11-19 20:13:53 +01:00
|
|
|
|
x = (largeur // 2) - (nouveauX // 2)
|
|
|
|
|
y = (hauteur // 2) - (nouveauY // 2)
|
2021-11-19 20:12:06 +01:00
|
|
|
|
|
2021-11-19 20:13:53 +01:00
|
|
|
|
self.parent.geometry(f"{nouveauX}x{nouveauY}+{x}+{y}")
|
2021-11-19 20:12:06 +01:00
|
|
|
|
|
2021-11-19 00:47:01 +01:00
|
|
|
|
def _interfaceConnexion(self):
|
2021-11-19 23:57:11 +01:00
|
|
|
|
"""Affiche l'interface de connexion."""
|
2021-11-18 20:41:58 +01:00
|
|
|
|
# Paramètres de la fenêtre
|
2021-11-19 20:13:53 +01:00
|
|
|
|
self.dimensionsFenetre(400, 600)
|
2021-11-19 00:47:01 +01:00
|
|
|
|
self.parent.title("Fenêtre de connexion")
|
|
|
|
|
|
2021-11-19 23:57:11 +01:00
|
|
|
|
# Suppresssion de la dernière Frame
|
|
|
|
|
self.f.destroy()
|
|
|
|
|
# Instanciation d'une nouvelle Frame, on va donc ajouter tout nos widgets à cet Frame
|
2021-11-19 00:47:01 +01:00
|
|
|
|
self.f = Frame(self.parent)
|
2021-11-20 02:40:02 +01:00
|
|
|
|
self.f.grid()
|
2021-11-18 20:41:58 +01:00
|
|
|
|
|
|
|
|
|
# Affichage des labels et boutons
|
2021-11-19 00:47:01 +01:00
|
|
|
|
tentativeDeConnexion = lambda _ = None: self.connexion(utilisateur.get(), motDpasse.get()) # lambda pour envoyer les informations entrés dans le formulaire
|
2021-11-19 11:41:37 +01:00
|
|
|
|
ecart = 80 # écart pour avoir un affichage centré
|
2021-11-19 00:47:01 +01:00
|
|
|
|
Label(self.f).grid(row=0, pady=50) # utilisé pour du padding (meilleur affichage)
|
2021-11-18 20:41:58 +01:00
|
|
|
|
|
2021-11-20 02:40:02 +01:00
|
|
|
|
Label(self.f, text="Utilisateur", font=self.font).grid(column=0, row=1, columnspan=2, padx=ecart - 20, pady=20, sticky='w')
|
2021-11-19 00:47:01 +01:00
|
|
|
|
utilisateur = Entry(self.f, font=self.font, width=18)
|
2021-11-18 20:41:58 +01:00
|
|
|
|
utilisateur.grid(column=1, row=2, columnspan=2, padx=ecart)
|
|
|
|
|
|
2021-11-20 02:40:02 +01:00
|
|
|
|
Label(self.f, text="Mot de passe", font=self.font).grid(column=0, row=3, columnspan=2, padx=ecart - 20, pady=20, sticky='w')
|
2021-11-19 00:47:01 +01:00
|
|
|
|
motDpasse = Entry(self.f, font=self.font, show='⁎', width=18)
|
|
|
|
|
motDpasse.grid(column=1, row=4, columnspan=2, padx=ecart)
|
|
|
|
|
motDpasse.bind("<Return>", tentativeDeConnexion)
|
|
|
|
|
|
2021-11-19 00:08:48 +01:00
|
|
|
|
def __afficherMDP(self):
|
|
|
|
|
"""Permet de gérer l'affichage du mot de passe dans le champs sur la page de connexion."""
|
|
|
|
|
if self.mdpVisible == False: # si mot de passe caché, alors on l'affiche
|
|
|
|
|
self.mdpVisible = True
|
|
|
|
|
motDpasse.config(show='')
|
|
|
|
|
bouttonAffichageMDP.config(font=("Arial", 10, "overstrike"))
|
|
|
|
|
|
|
|
|
|
else: # inversement
|
|
|
|
|
self.mdpVisible = False
|
|
|
|
|
motDpasse.config(show='⁎')
|
|
|
|
|
bouttonAffichageMDP.config(font=("Arial", 10))
|
|
|
|
|
|
2021-11-19 00:47:01 +01:00
|
|
|
|
bouttonAffichageMDP = Button(self.f, text='👁', command=lambda: __afficherMDP(self))
|
2021-11-19 00:08:48 +01:00
|
|
|
|
bouttonAffichageMDP.grid(column=2, row=4, columnspan=2)
|
|
|
|
|
self.mdpVisible = False
|
|
|
|
|
|
2021-11-19 00:47:01 +01:00
|
|
|
|
bouton = Button(self.f, text="Se connecter", font=self.font, command=tentativeDeConnexion)
|
2021-11-18 20:41:58 +01:00
|
|
|
|
bouton.grid(column=0, row=5, columnspan=3, padx=ecart, pady=20)
|
2021-11-19 00:47:01 +01:00
|
|
|
|
bouton.bind("<Return>", tentativeDeConnexion)
|
2021-11-18 20:41:58 +01:00
|
|
|
|
|
2021-11-19 17:14:15 +01:00
|
|
|
|
Button(self.f, text="Quitter", font=self.font, command=quit).grid(column=0, row=6, columnspan=4, pady=20)
|
2021-11-18 20:41:58 +01:00
|
|
|
|
|
2021-11-20 02:40:02 +01:00
|
|
|
|
self._interfaceManager(1) # DEBUG: affiche directement l'interface du Manager
|
2021-11-20 00:11:59 +01:00
|
|
|
|
|
2021-11-19 23:57:11 +01:00
|
|
|
|
def _interfaceCaissier(self, id: int):
|
|
|
|
|
"""Affiche l'interface du caissier."""
|
2021-11-20 02:40:02 +01:00
|
|
|
|
caissier = Utilisateurs().recuperationUtilisateur(id=id)[0]
|
2021-11-19 23:57:11 +01:00
|
|
|
|
self.parent.title(f"Caissier {caissier[3]} {caissier[4]}")
|
2021-11-20 00:11:59 +01:00
|
|
|
|
self.dimensionsFenetre(1280, 720)
|
2021-11-19 23:57:11 +01:00
|
|
|
|
|
|
|
|
|
# Suppresssion de la dernière Frame
|
|
|
|
|
self.f.destroy()
|
|
|
|
|
# Instanciation d'une nouvelle Frame, on va donc ajouter tout nos widgets à cet Frame
|
|
|
|
|
self.f = Frame(self.parent)
|
2021-11-20 02:40:02 +01:00
|
|
|
|
self.f.grid()
|
2021-11-19 23:57:11 +01:00
|
|
|
|
|
|
|
|
|
def _interfaceManager(self, id: int):
|
|
|
|
|
"""Affiche l'interface du manager."""
|
2021-11-20 02:40:02 +01:00
|
|
|
|
manager = Utilisateurs().recuperationUtilisateur(id=id)
|
2021-11-20 00:57:56 +01:00
|
|
|
|
self.parent.title(f"Manager {manager['nom']} {manager['prenom']}")
|
2021-11-20 02:40:02 +01:00
|
|
|
|
self.dimensionsFenetre(530, 720)
|
2021-11-19 23:57:11 +01:00
|
|
|
|
|
|
|
|
|
# Suppresssion de la dernière Frame
|
|
|
|
|
self.f.destroy()
|
|
|
|
|
# Instanciation d'une nouvelle Frame, on va donc ajouter tout nos widgets à cet Frame
|
|
|
|
|
self.f = Frame(self.parent)
|
2021-11-20 02:40:02 +01:00
|
|
|
|
self.f.grid()
|
|
|
|
|
|
|
|
|
|
Label(self.f, text="Interface Manager", font=(self.font[0], 20)).grid(column=0, row=0)
|
|
|
|
|
|
|
|
|
|
Button(self.f, text="Se déconnecter", font=self.font, command=self._interfaceConnexion).grid(column=1, row=0, padx=50)
|
|
|
|
|
|
|
|
|
|
Label(self.f).grid(row = 1, pady=10) # séparateur
|
|
|
|
|
|
|
|
|
|
def __ajouterUtilisateur(metier: int):
|
|
|
|
|
"""Permet de créer un nouvel utilisateur, manager (`metier = 0`) et caissier (`metier = 1`)."""
|
|
|
|
|
print(f"ajout d'un utilisateur (métier = {metier})")
|
|
|
|
|
# ouvrir un toplevel?
|
|
|
|
|
|
|
|
|
|
def __retirerUtilisateur(metier: int):
|
|
|
|
|
"""Permet de supprimer un utilisateur existant, manager (`metier = 0`) et caissier (`metier = 1`)."""
|
|
|
|
|
print(f"retirer un utilisateur (métier = {metier})")
|
|
|
|
|
# ouvrir un toplevel?
|
|
|
|
|
|
|
|
|
|
def __afficherInformationsUtilisateur(_):
|
|
|
|
|
"""Permet d'afficher les informations d'un utilisateur"""
|
|
|
|
|
element = listeUtilisateurs.curselection()
|
|
|
|
|
if len(element) == 0: # si aucun élément n'est selectionné
|
|
|
|
|
return
|
|
|
|
|
print(Utilisateurs().recuperationUtilisateur(pseudo=listeUtilisateurs.get(element[0])))
|
|
|
|
|
# ouvrir un toplevel?
|
|
|
|
|
|
|
|
|
|
Button(self.f, text="Ajouter un caissier", font=self.font, command=lambda: __ajouterUtilisateur(1)).grid(column=0, row=2)
|
|
|
|
|
Button(self.f, text="Retirer un caissier", font=self.font, command=lambda: __retirerUtilisateur(1)).grid(column=1, row=2)
|
|
|
|
|
|
|
|
|
|
Label(self.f).grid(row = 3, pady=10) # séparateur
|
|
|
|
|
|
|
|
|
|
# Liste des utilisateurs
|
|
|
|
|
Label(self.f, text="Liste des utilisateurs", font=self.font).grid(column=0, row=4) # titre
|
|
|
|
|
# On définit une barre pour pouvoir scroller dans la liste
|
|
|
|
|
scroll = Scrollbar(self.f, orient="vertical")
|
|
|
|
|
scroll.grid(column=0, row=5, sticky="nse")
|
|
|
|
|
# On définit notre liste et on la lie à notre `Scrollbar`
|
|
|
|
|
listeUtilisateurs = Listbox(self.f, width=25, height=4, yscrollcommand=scroll.set)
|
|
|
|
|
scroll['command'] = listeUtilisateurs.yview
|
|
|
|
|
# On ajoute nos utilisateurs à notre liste
|
|
|
|
|
for idx, utilisateur in enumerate(Utilisateurs().listUtilisateurs()):
|
|
|
|
|
listeUtilisateurs.insert(idx, utilisateur)
|
|
|
|
|
listeUtilisateurs.grid(column=0, row=5)
|
|
|
|
|
listeUtilisateurs.bind('<Double-Button>', __afficherInformationsUtilisateur) # on affiche l'utilisateur quand on double-clique dessus
|
2021-11-20 00:11:59 +01:00
|
|
|
|
|
2021-11-18 20:41:58 +01:00
|
|
|
|
if __name__ == "__main__":
|
2021-11-20 00:11:59 +01:00
|
|
|
|
"""Application "GesMag" pour le module de Programmation d'interfaces (2021-2022)"""
|
2021-11-19 23:57:11 +01:00
|
|
|
|
print("-- Compte par défaut --\nNom d'utilisateur: admin\nMot de passe: P@ssword")
|
2021-11-18 20:41:58 +01:00
|
|
|
|
GesMag().demarrer()
|