import tkinter as tk from tkinter import messagebox, font from Pile import Pile_chaine as Pile from math import * from expression import * from infixe import infixe2npi from popup import MsgBox class ValeurSaisie: """classe d'une valeur saise par l'utilisateur""" def __init__(self, valeur, master): self.valeur = valeur self.label_valeur = tk.Label(master, text=valeur) self.label_valeur.pack(side="bottom") def __del__(self): """suppression du label""" self.label_valeur.destroy() class Interface(tk.Frame): """interface de la calculatrice""" def __init__(self, master): self.master = master tk.Frame.__init__(self, master) self.etat = tk.Label(self.master, text="notation postfixée (polonaise inversée) -- mode interactif", fg="gray42") self.etat.pack() self.etat_affichage = "post-int" self.stack = Pile() self.create_menu_bar() self.frame_stack = tk.Frame(self.master) self.frame_stack.pack(padx=10, pady=10) self.affichage_expression = tk.Label(self.master, text="") self.affichage_expression.pack() frame_saisie = tk.Frame(self.master, padx=5, pady=5) frame_saisie.pack() self.saisie_expression = tk.Entry(frame_saisie) self.saisie_expression.pack(side="left") self.saisie_expression.bind("", lambda e : self.entrer()) self.saisie_expression.bind("", lambda e : self.entrer()) btn_entrer = tk.Button(frame_saisie, text='entrer', command=self.entrer) btn_entrer.pack() commandes = tk.LabelFrame(self.master, text="commandes", padx=5, pady=5) commandes.pack(fill="both") effacer = tk.LabelFrame(commandes, text="effacer", padx=5, pady=5) effacer.pack(fill="both") btn_effacer_tt = tk.Button(effacer, text='tout effacer', command=self.effacer_tt) btn_effacer_tt.pack(side="left") btn_effacer_dernier = tk.Button(effacer, text='effacer la dernière valeur', command=self.effacer_dernier) btn_effacer_dernier.pack(side="left") fonctions = tk.LabelFrame(commandes, text="fonctions", padx=5, pady=5) fonctions.pack(fill="both") btn_sinus = tk.Button(fonctions, text='sin', command=lambda : self.evaluer_stack("sin",1)) btn_sinus.pack(side="left") btn_sinus = tk.Button(fonctions, text='cos', command=lambda : self.evaluer_stack("cos",1)) btn_sinus.pack(side="left") btn_sinus = tk.Button(fonctions, text='tan', command=lambda : self.evaluer_stack("tan",1)) btn_sinus.pack(side="left") btn_pow = tk.Button(fonctions, text='^', command=lambda : self.evaluer_stack("^",2)) btn_pow.pack(side="right") def create_menu_bar(self): menu_bar = tk.Menu(self) menu_file = tk.Menu(menu_bar, tearoff=0) menu_file.add_command(label="Quitter", command=self.master.destroy) menu_bar.add_cascade(label="Fichier", menu=menu_file) menu_affichage = tk.Menu(menu_bar, tearoff=0) menu_mode = tk.Menu(menu_bar, tearoff=0) menu_affichage.add_command(label="préfixe (polonaise)", command=lambda : self.change_affichage("pre")) menu_affichage.add_command(label="infixe", command=lambda : self.change_affichage("inf")) menu_mode.add_command(label="mode interactif", command=lambda : self.change_affichage("post-int")) menu_mode.add_command(label="mode expression", command=lambda : self.change_affichage("post-expr")) menu_affichage.add_cascade(label="postfixe (polonaise inversée)", menu=menu_mode) menu_bar.add_cascade(label="Notation", menu=menu_affichage) menu_help = tk.Menu(menu_bar, tearoff=0) menu_help.add_command(label="Aide", command=self.aide) menu_help.add_command(label="À propos", command=self.about) menu_bar.add_cascade(label="Aide", menu=menu_help) self.master.config(menu=menu_bar) def change_affichage(self, affichage): """change le mode d'affichage et efface les calculs en cours""" self.etat_affichage = affichage etat_text = {"pre":"notation préfixée (polonaise)", "inf":"notation infixée", "post-int":"notation postfixée (polonaise inversée) -- mode interactif", "post-expr": "notation postfixée (polonaise inversée) -- mode expression" } self.etat.config(text=etat_text[affichage]) self.affichage_expression.config(text="") self.effacer_tt() self.saisie_expression.delete(0 ,'end') def aide(self): """affiche l'aide""" messagebox.showinfo("Aide", "...") def about(self): """à propos de cette application""" messagebox.showinfo("À propos", "Ceci est une calculatrice.") def entrer(self): """si dans mode interactif alors ajoute nv terme si dans autre mode alors evalue l'expression""" if self.etat_affichage == "post-int": self.entrer_terme_dans_stack() if len(self.saisie_expression.get()) > 0: self.evaluer_expr() def entrer_terme_dans_stack(self): """si nombre : ajoute une nouvelle valeur dans le stack ajoute un label avec cette valeur si fonction : on l'évalue""" valeur_a_ajouter = self.saisie_expression.get().replace(" ", "") if valeur_a_ajouter != "": if "." in valeur_a_ajouter: valeur_a_ajouter = float(valeur_a_ajouter) self.stack.empiler(ValeurSaisie(valeur_a_ajouter, self.frame_stack)) elif valeur_a_ajouter in ["sin","cos","tan","sqrt","!","opp"]: self.evaluer_stack(valeur_a_ajouter,1) elif valeur_a_ajouter in ["+","-","/","*","^"]: self.evaluer_stack(valeur_a_ajouter,2) elif valeur_a_ajouter in ["moy3"]: self.evaluer_stack(valeur_a_ajouter,3) elif valeur_a_ajouter in ["somme","produit"]: self.evaluer_stack(valeur_a_ajouter,-1) elif valeur_a_ajouter == "pi": self.stack.empiler(ValeurSaisie(pi, self.frame_stack)) else: try: valeur_a_ajouter = int(valeur_a_ajouter) self.stack.empiler(ValeurSaisie(valeur_a_ajouter, self.frame_stack)) except ValueError: messagebox.showerror("Saisie invalide", "Vous n'avez pas entré un nombre, ou la fonction est inconnue") self.saisie_expression.delete(0 ,'end') def evaluer_stack(self, op, nb_arg): """application de l'operation aux nb_arg dernières valeurs du stack ; si nb_arg == -1 alors toutes les valeurs du stack sont utilisées""" if nb_arg == -1: if self.stack.est_vide(): messagebox.showerror("Pas assez d'arguments", "Il n'y a pas d'arguments") return lst_val = [] while not self.stack.est_vide(): lst_val.append(self.stack.depiler().valeur) if op == "somme": res = 0 for val in lst_val: res += val if op == "produit": res = 1 for val in lst_val: res *= val if self.stack.taille() < nb_arg: if nb_arg - self.stack.taille() == 1: messagebox.showerror("Pas assez d'arguments", "Il manque 1 argument") else: messagebox.showerror("Pas assez d'arguments", "Il manque "+str(nb_arg - self.stack.taille())+" arguments") return if nb_arg == 1: val = self.stack.depiler().valeur if op == "sin": res = sin(val) if op == "cos": res = cos(val) if op == "tan": res = tan(val) if op == "sqrt": res = sqrt(val) if op == "!": res = factorial(val) if op == "opp": res = - val elif nb_arg == 2: droite = self.stack.depiler().valeur gauche = self.stack.depiler().valeur if op == "+": res = gauche + droite if op == "-": res = gauche - droite if op == "*": res = gauche * droite if op == "/": res = gauche / droite if op == "^": res = gauche ** droite elif nb_arg == 3: terme3 = self.stack.depiler().valeur terme2 = self.stack.depiler().valeur terme1 = self.stack.depiler().valeur if op == "moy3": res = (terme1 + terme2 + terme3)/3 self.stack.empiler(ValeurSaisie(res, self.frame_stack)) def evaluer_expr(self): """evalue la chaine de caractère dans le champ de saisie selon l'affichage / la notation choisie (prefixe/infixe/postfixe)""" lst_expr = self.saisie_expression.get().split() if self.etat_affichage == "pre": lst_expr.reverse() if self.etat_affichage == "inf": try: lst_expr = infixe2npi(lst_expr) except SyntaxError as s: explication, i_char = s.args[0] if explication == "manque op": messagebox.showerror("Erreur de syntaxe", "Il manque un opérateur. L'étoile ( « ★ » ) indique la position de l'opérateur manquant dans l'expression." + "\nL'expression : \n\n(" + " ".join(lst_expr[:i_char]) + ") ★ (" + " ".join(lst_expr[i_char:]) + ")") elif explication == "Mauvais () detail": messagebox.showerror("Erreur de syntaxe", explication + "\nLa parenthèse fautive entre 【 crochet 】: \n\n" + " ".join(lst_expr[:i_char]) + " 【 " + str(lst_expr[i_char]) + " 】 " + " ".join(lst_expr[i_char+1:])) else: messagebox.showerror("Erreur de syntaxe", explication) return except ValueError as v: message, explication, i_char = v.args messagebox.showerror(message,"Le terme « " + lst_expr[i_char] + " » " + explication + "\n\n" + "Dans l'expression, entre 【 crochet 】 :\n" + " ".join(lst_expr[:i_char]) + " 【 " + str(lst_expr[i_char]) + " 】 " + " ".join(lst_expr[i_char+1:])) return try: arbre_expr = npi2tree(lst_expr, self.etat_affichage) self.affichage_expression.config(text = str(arbre_expr) + "=" + str(arbre_expr.evalue())) except IndexError: messagebox.showerror("Erreur","Erreur de syntaxe ici") except ValueError: #faire un truc plus beau messagebox.showerror("Saisie invalide", "Vous n'avez pas entré un nombre, ou la fonction est inconnue.") except SyntaxError as s: explication, pile_expr = s.args[0] print("pile_expr depuis evaluer_expr",pile_expr) lst_pile_expr = [] while not pile_expr.est_vide(): lst_pile_expr.append(str(pile_expr.depiler())) lst_pile_expr.reverse() print(lst_pile_expr) messagebox.showerror("Erreur de syntaxe", explication +"\nL'expression : \n\n(" + ") ★ (".join(lst_pile_expr) + ")") def effacer_tt(self): """supprime tout le contenu de la pile (et les labels associés)""" while not self.stack.est_vide(): self.stack.depiler() self.frame_stack.config(height = 1) def effacer_dernier(self): """supprime la dernière valeur entrée (et le label associés)""" if not self.stack.est_vide(): self.stack.depiler() if __name__ == "__main__": root = tk.Tk() root.title("Calculatrice") root.geometry("350x500") hello_frame = Interface(root) hello_frame.mainloop()