# C'est très bien, sauf que ta classe Interface fait beaucoup plus de choses # que simplement être une interface. Normalement, toute la partie calcul devrait # être faite par la classe Expression, que tu n'utilises pas !!! # En POO, il y a un principe que s'appelle SRP (Single Responsability Principle) # qui dit que chaque objet doit avoir un unique rôle. # Note : 10 / 10 from math import * import tkinter as tk from tkinter import messagebox from Pile import Pile_chaine as Pile from expression import Expression, toTree from infixe import infixe2npi, estUnNombre class ValeurSaisie: """classe d'une valeur saise par l'utilisateur""" def __init__(self, valeur, master): """initialisation de la valeur""" 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): """initialisation de l'interface""" 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(fill="both") self.saisie_expression = tk.Entry(frame_saisie) self.saisie_expression.pack(fill="both",expand=True,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", padx=5) self.frame_pile = tk.LabelFrame(commandes, text="pile", padx=5, pady=5) self.frame_pile.pack(fill="both") tk.Button(self.frame_pile, text='tout supprimer', command=self.effacer_tt).pack(side="left") tk.Button(self.frame_pile, text='supprimer sommet', command=self.effacer_dernier).pack(side="left") tk.Button(self.frame_pile, text='dupliquer sommet', command=self.dupliquer).pack(side="left") fonctions = tk.LabelFrame(commandes, text="fonctions", padx=5, pady=5) fonctions.pack(fill="both") for i, text in enumerate(("+", "-", "/","*","^")): tk.Button(fonctions, text=text, command=lambda x=text: self.ecrire_fx(x)).grid(row=0, column=i, sticky="ew") for i, text in enumerate(("sin","cos","tan","factorielle")): tk.Button(fonctions, text=text, command=lambda x=text: self.ecrire_fx(x)).grid(row=1, column=i, sticky="ew") for i, text in enumerate(("asin","acos","atan")): tk.Button(fonctions, text=text, command=lambda x=text: self.ecrire_fx(x)).grid(row=2, column=i, sticky="ew") def ecrire_fx(self, nom): """insère le nom de la fonction dans la barre de saisie""" self.saisie_expression.insert(tk.END, " "+nom+" ") if self.etat_affichage == "post-int": self.entrer() def dupliquer(self): """duplique la dernière valeur entrée dans le stack""" if not self.stack.est_vide(): self.stack.empiler(ValeurSaisie(self.stack.sommet().valeur, self.frame_stack)) def create_menu_bar(self): """crée la barre de menu de la calculatrice""" 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['menu']=menu_bar def change_affichage(self, affichage): """change le mode d'affichage, désactive les opérations de pile si l'on est pas dans le mode interactif 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]) if self.etat_affichage != "post-int": for child in self.frame_pile.winfo_children(): child['state']='disable' else: for child in self.frame_pile.winfo_children(): child['state']='normal' self.affichage_expression.config(text="") self.effacer_tt() self.saisie_expression.delete(0 ,'end') def aide(self): """affiche l'aide""" messagebox.showinfo("Aide", "42") def about(self): """à propos de cette application""" messagebox.showinfo("À propos", "Ceci est une calculatrice.") def entrer(self): """selon le mode, on évalue l'expression ou on entre la valeur dans le stack""" if len(self.saisie_expression.get()) > 0: try: if self.etat_affichage == "post-int": try: self.entrer_terme_dans_stack(self.saisie_expression.get().replace(" ","")) except ValueError: messagebox.showerror("Saisie invalide", "Le résultat de l'opération n'est pas un nombre réel.") except TypeError: messagebox.showerror("Saisie invalide", "L'opérande n'est pas un nombre réel.") else: self.evaluer_expr() except OverflowError: messagebox.showerror("OverflowError", "Le résultat est trop élevé.") def entrer_terme_dans_stack(self, terme): """si c'est un nombre ou une constante: on ajoute une nouvelle valeur dans le stack si c'est une fonction : on l'évalue""" if estUnNombre(terme): if "." in terme: terme = float(terme) else: terme = int(terme) self.stack.empiler(ValeurSaisie(terme, self.frame_stack)) elif terme in ["asin","acos","atan","sin","cos","tan","sqrt","!","factorielle","opp"]: self.evaluer_stack(terme,1) elif terme in ["+","-","/","*","^"]: self.evaluer_stack(terme,2) elif terme in ["moy3"]: self.evaluer_stack(terme,3) elif terme in ["somme","produit"]: self.evaluer_stack(terme,-1) elif terme == "pi": self.stack.empiler(ValeurSaisie(pi, self.frame_stack)) elif terme == "e": self.stack.empiler(ValeurSaisie(exp(1), self.frame_stack)) else: messagebox.showerror("Saisie invalide", "La fonction « "+terme+" » est inconnue.") return 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 == "asin": res = asin(val) if op == "acos": res = acos(val) if op == "atan": res = atan(val) if op == "sqrt": res = sqrt(val) if op == "!" or op == "factorielle": res = gamma(val + 1) 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": if "opp" in lst_expr: messagebox.showerror("Erreur", "Vous ne pouvez pas utiliser la fonction 'opp' ici ; veuillez utiliser le signe '-' à la place.\n(Vous pouvez par contre utiliser 'opp' quand vous êtes en notation préfixée ou infixée.)") return try: lst_expr = infixe2npi(lst_expr) except SyntaxError as s: explication = s.args[0] 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 = toTree(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 as v: explication = v.args[0] if v.args[0] == "math domain error": messagebox.showerror("Saisie invalide", "Le résultat n'est pas un nombre réel.") else: message, explication, i_char = v.args if self.etat_affichage == "pre": lst_expr.reverse() i_char = len(lst_expr) - i_char - 1 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:])) except SyntaxError as s: explication = s.args[0] messagebox.showerror("Erreur de syntaxe", explication) 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("380x500") app = Interface(root) app.mainloop()