|
|
|
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("<Return>", lambda e : self.entrer())
|
|
|
|
self.saisie_expression.bind("<KP_Enter>", 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()
|