From 513a0599e03783108d7157965c80aa24319eb401 Mon Sep 17 00:00:00 2001 From: Mylloon Date: Mon, 29 Nov 2021 13:02:52 +0100 Subject: [PATCH] Modifications: - Row/column seperations in the stock table (better clarity) - Fixed a bug that showed artifacts of the first page when going to the last page after adding a new item - Adding the basket to the right of the screen - Added buttons to add and remove items from the cart in the stock table - Changing the dimensions and placement of certain elements to air out the interface --- main.py | 147 ++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 105 insertions(+), 42 deletions(-) diff --git a/main.py b/main.py index aef5271..303af18 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,6 @@ # Tkinter from tkinter import IntVar, Checkbutton, LabelFrame, PhotoImage, Scrollbar, Listbox, Entry, Button, Label, Frame, Tk, Toplevel -from tkinter.ttk import Combobox +from tkinter.ttk import Combobox, Separator from tkinter.messagebox import showerror, showinfo, showwarning, askyesno from tkinter.filedialog import askopenfile # Regex @@ -22,9 +22,11 @@ class GesMag: self.nomApp = "GesMag" # nom de l'application 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 + self.f = Frame(self.parent) # `Frame` "principale" affiché à l'écran + self.tableau = Frame() # `Frame` qui va afficher le tableau des éléments présents dans le stock self.imagesStock = [] # liste qui va contenir nos images pour l'affichage du stock self.dossierImage = PhotoImage(file = "img/dossier.gif") # image pour l'icone de selection + self.panierAffichage = Frame() # `Frame` qui va afficher le panier self.panier = [] # liste des éléments "dans le panier" def demarrer(self) -> None: @@ -199,7 +201,7 @@ class GesMag: """Affiche l'interface du caissier.""" caissier = Utilisateurs().recuperationUtilisateur(id=id) self.parent.title(f"Caissier {caissier['nom']} {caissier['prenom']} – {self.nomApp}") - self.dimensionsFenetre(self.parent, 1030, 690) + self.dimensionsFenetre(self.parent, 1100, 710) # Suppresssion de la dernière Frame self.f.destroy() @@ -224,26 +226,67 @@ class GesMag: def __formatPrix(prix: str) -> str: return f"{float(prix):.2f} €".replace('.', ',') - def __affichageTableau(parent: Frame, page: int = 1): + def __affichageTableau(page: int = 1): """Fonction qui va actualiser le tableau avec une page donnée (par défaut affiche la première page).""" # On supprime et refais la frame qui va stocker notre tableau - parent.destroy() - parent = Frame(stock) - parent.grid(column=0, row=1, columnspan=6) + self.tableau.destroy() + self.tableau = Frame(stock) + self.tableau.grid(column=0, row=1, columnspan=7) # Filtre pour le tableau filtres = Frame(stock) # Morceau qui va contenir nos checkbutton ecartFiltre = 10 # écart entre les champs des filtres Label(filtres, text="Filtre", font=self.font).grid(column=0, row=0) # titre - Checkbutton(filtres, text="Stock disponible\nuniquement", variable=stockDisponibleVerif, command=lambda: __affichageTableau(parent)).grid(sticky='w', pady=ecartFiltre) - Checkbutton(filtres, text="Cacher les\nfruits & légumes", variable=fruitsLegumesVerif, command=lambda: __affichageTableau(parent)).grid(sticky='w', pady=ecartFiltre) - Checkbutton(filtres, text="Cacher les produits de\nla boulangerie", variable=boulangerieVerif, command=lambda: __affichageTableau(parent)).grid(sticky='w', pady=ecartFiltre) - Checkbutton(filtres, text="Cacher les produits de\nla boucherie\net poissonnerie", variable=boucheriePoissonnerieVerif, command=lambda: __affichageTableau(parent)).grid(sticky='w') - Checkbutton(filtres, text="Cacher les produits\nd'entretien", variable=entretienVerif, command=lambda: __affichageTableau(parent)).grid(sticky='w', pady=ecartFiltre) + Checkbutton(filtres, text="Stock disponible\nuniquement", variable=stockDisponibleVerif, command=__affichageTableau).grid(sticky='w', pady=ecartFiltre) + Checkbutton(filtres, text="Cacher les\nfruits & légumes", variable=fruitsLegumesVerif, command=__affichageTableau).grid(sticky='w', pady=ecartFiltre) + Checkbutton(filtres, text="Cacher les produits de\nla boulangerie", variable=boulangerieVerif, command=__affichageTableau).grid(sticky='w', pady=ecartFiltre) + Checkbutton(filtres, text="Cacher les produits de\nla boucherie\net poissonnerie", variable=boucheriePoissonnerieVerif, command=__affichageTableau).grid(sticky='w') + Checkbutton(filtres, text="Cacher les produits\nd'entretien", variable=entretienVerif, command=__affichageTableau).grid(sticky='w', pady=ecartFiltre) filtres.grid(column=7, row=1, sticky='w') stockListe = Stock().listeStocks() # stock récupéré de la base de données + def ___miseAJourPanier(element: dict, action: bool): + """ + Permet d'ajouter ou de retirer des éléments au panier + -> Action + -> Vrai : Ajout + -> Faux : Retire + """ + # On compte combien de fois l'élément est présent dans le panier + nombreDeFoisPresentDansLePanier = 0 + index = None + for idx, elementDansLePanier in enumerate(self.panier): + if elementDansLePanier[0] == element: + index = idx # On met à jour l'index + nombreDeFoisPresentDansLePanier = elementDansLePanier[1] + break # on peut quitter la boucle car on a trouvé notre élément + + # On vérifie que on peut encore l'ajouter/retirer + if nombreDeFoisPresentDansLePanier == 0 and not action: # pop-up seulement si on veut retirer un élément pas présent + showerror("Erreur", "Impossible de retirer cet élément au panier.\nNon présent dans le panier.") + return + if nombreDeFoisPresentDansLePanier >= element["quantite"] and action: # pop-up seulement si on veut en rajouter + showerror("Erreur", "Impossible de rajouter cet élément au panier.\nLimite excédée.") + return + + if index != None: # on retire l'ancienne valeur du panier si déjà présente dans le panier + self.panier.pop(index) + else: # sinon on définie un index pour pouvoir ajouté la nouvelle valeur à la fin de la liste + index = len(self.panier) + + # On change la valeur dans le panier + if action: # si on ajoute + nombreDeFoisPresentDansLePanier += 1 + else: # si on retire + nombreDeFoisPresentDansLePanier -= 1 + + # On rajoute l'élément avec sa nouvelle quantité seulement s'il y en a + if nombreDeFoisPresentDansLePanier > 0: + self.panier.insert(index, (element, nombreDeFoisPresentDansLePanier)) + + ___affichagePanier() # Met-à-jour le panier + for i in range(0, len(stockListe)): # on retire les éléments plus présent dans la liste if stockDisponibleVerif.get() == 1 and stockListe[i]["quantite"] < 1: stockListe[i] = None @@ -270,18 +313,23 @@ class GesMag: limiteIndex = elementsParPage * page # on définit une limite pour ne pas afficher plus d'éléments qu'il n'en faut par page if len(stockListe) > 0: # si stock non vide # Définition des colonnes - Label(parent, text="ID").grid(column=0, row=0, padx=ecart) - Label(parent, text="Image").grid(column=1, row=0, padx=ecart) - Label(parent, text="Type").grid(column=2, row=0, padx=ecart) - Label(parent, text="Nom").grid(column=3, row=0, padx=ecart) - Label(parent, text="Quantité").grid(column=4, row=0, padx=ecart) - Label(parent, text="Prix unité").grid(column=5, row=0, padx=ecart) + Label(self.tableau, text="ID").grid(column=0, row=0, padx=ecart) + Label(self.tableau, text="Image").grid(column=1, row=0, padx=ecart) + Label(self.tableau, text="Type").grid(column=2, row=0, padx=ecart) + Label(self.tableau, text="Nom").grid(column=3, row=0, padx=ecart) + Label(self.tableau, text="Quantité").grid(column=4, row=0, padx=ecart) + Label(self.tableau, text="Prix unité").grid(column=5, row=0, padx=ecart) + Label(self.tableau, text="Action").grid(column=6, row=0, padx=ecart) + Separator(self.tableau).grid(column=0, row=0, columnspan=7, sticky="sew") + Separator(self.tableau).grid(column=0, row=0, columnspan=7, sticky="new") + for j in range(0, 8): + Separator(self.tableau, orient='vertical').grid(column=j, row=0, columnspan=2, sticky="nsw") curseur = limiteIndex - elementsParPage # on commence à partir du curseur i = 1 # on commence à 1 car il y a déjà le nom des colonnes en position 0 self.imagesStock = [] # on vide la liste si elle contient déjà des images for element in stockListe[curseur:limiteIndex]: # on ignore les éléments avant le curseur et après la limite - Label(parent, text=element["id"]).grid(column=0, row=i, padx=ecart) + Label(self.tableau, text=element["id"]).grid(column=0, row=i, padx=ecart) """ L'idée est que on a une liste `images` qui permet de stocker toutes nos images @@ -292,32 +340,37 @@ class GesMag: self.imagesStock.append(PhotoImage(file = element["image_url"])) else: # si l'image n'existe pas self.imagesStock.append(PhotoImage(file = "img/defaut.gif")) # image par défaut - Label(parent, image=self.imagesStock[i - 1]).grid(column=1, row=i, padx=ecart) + Label(self.tableau, image=self.imagesStock[i - 1]).grid(column=1, row=i, padx=ecart) - Label(parent, text=element["type"].capitalize()).grid(column=2, row=i, padx=ecart) - Label(parent, text=element["nom"].capitalize()).grid(column=3, row=i, padx=ecart) - Label(parent, text=element["quantite"]).grid(column=4, row=i, padx=ecart) - Label(parent, text=__formatPrix(element["prix"])).grid(column=5, row=i, padx=ecart) + Label(self.tableau, text=element["type"].capitalize()).grid(column=2, row=i, padx=ecart) + Label(self.tableau, text=element["nom"].capitalize()).grid(column=3, row=i, padx=ecart) + Label(self.tableau, text=element["quantite"]).grid(column=4, row=i, padx=ecart) + Label(self.tableau, text=__formatPrix(element["prix"])).grid(column=5, row=i, padx=ecart) + # boutons d'actions pour le panier + Button(self.tableau, text='+', font=("Arial", 7), command=lambda e = element: ___miseAJourPanier(e, True)).grid(column=6, row=i, sticky='n', padx=ecart) + Button(self.tableau, text='−', font=("Arial", 7), command=lambda e = element: ___miseAJourPanier(e, False)).grid(column=6, row=i, sticky='s') + for j in range(0, 8): + Separator(self.tableau, orient='vertical').grid(column=j, row=i, columnspan=2, sticky="nsw") + Separator(self.tableau).grid(column=j, row=i, columnspan=2, sticky="sew") curseur += 1 i += 1 # Information sur la page actuelle - Label(parent, text=f"Page {page}/{pageMax}").grid(column=2, row=i, columnspan=2) + Label(self.tableau, text=f"Page {page}/{pageMax}").grid(column=2, row=i, columnspan=3) # Boutons - precedent = Button(parent, text="Page précédente", command=lambda: __affichageTableau(parent, page - 1)) - precedent.grid(column=0, row=i, columnspan=2, sticky='w') - suivant = Button(parent, text="Page suivante", command=lambda: __affichageTableau(parent, page + 1)) - suivant.grid(column=4, row=i, columnspan=2, sticky='e') + precedent = Button(self.tableau, text="Page précédente", command=lambda: __affichageTableau(page - 1)) + precedent.grid(column=0, row=i, columnspan=2, sticky='w', padx=ecart, pady=ecart) + suivant = Button(self.tableau, text="Page suivante", command=lambda: __affichageTableau(page + 1)) + suivant.grid(column=5, row=i, columnspan=2, sticky='e', padx=ecart) if page == 1: # si on est a la première page on désactive le boutton précédent precedent.config(state="disabled") if page == pageMax: # si on est a la dernière page on désactive le boutton suivant suivant.config(state="disabled") else: - Label(parent, text="Il n'y a rien en stock\nEssayez de réduire les critères dans le filtre.").grid(column=0, row=0, columnspan=6) + Label(self.tableau, text="Il n'y a rien en stock\nEssayez de réduire les critères dans le filtre.").grid(column=0, row=0, columnspan=7) - tableau = Frame(stock) - __affichageTableau(tableau) # affichage du tableau + __affichageTableau() # affichage du tableau # Partie affichage du ticket de caisse ecart = 10 @@ -326,17 +379,27 @@ class GesMag: Label(ticket, text=f"Date de vente : {date.today().strftime('%Y/%m/%d')}").grid(column=0, row=0, pady=ecart) - Label(ticket, text=f"Éléments achetés ({len(self.panier)}) :").grid(column=0, row=1, pady=ecart) - i = 2 - prixTotal = 0 - for element in self.panier: - Label(ticket, text=element).grid(column=0, row=i) - prixTotal += 0 # ajout du prix - i += 1 + def ___affichagePanier(): + """Affiche le panier.""" + self.panierAffichage.destroy() + self.panierAffichage = Frame(ticket) + self.panierAffichage.grid(column=0, row=1, pady=ecart) + elementsAchetes = Label(self.panierAffichage) + elementsAchetes.grid(column=0) + prixTotal = 0 + compteurElements = 0 + for element in self.panier: + Label(self.panierAffichage, text=f"[{element[0]['id']}] - {element[1]}x {element[0]['nom']}").grid(column=0) + prixTotal += (element[0]["prix"] * element[1]) # ajout du prix + compteurElements += element[1] - Label(ticket, text=f"Prix total : {__formatPrix(prixTotal)}").grid(column=0, row=i, pady=ecart) + elementsAchetes.config(text=f"Éléments achetés ({compteurElements}) :") - Button(ticket, text="Valider le\nticket de caisse", font=self.font).grid(column=0, row=i + 1, pady=ecart) + Label(self.panierAffichage, text=f"Prix total : {__formatPrix(prixTotal)}").grid(column=0, pady=ecart) + + ___affichagePanier() + + Button(ticket, text="Valider le\nticket de caisse", font=self.font).grid(column=0, pady=ecart) #.grid(column=1, row=1, sticky='s') @@ -419,7 +482,7 @@ class GesMag: float(prix.get()), image.get() ) - __affichageTableau(tableau) # met à jour le tableau + __affichageTableau() # met à jour le tableau # Champs de saisie # Image