import tkinter as tk
from tkinter import messagebox
from Pile import Pile_chaine as Pile
from math import *
from expression import *
from infixe import infixe2npi
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 ( fill = " both " )
self . saisie_expression = tk . Entry ( frame_saisie )
self . saisie_expression . pack ( fill = " both " , expand = True , 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 " , 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 ) :
self . saisie_expression . insert ( tk . END , " " + nom + " " )
if self . etat_affichage == " post-int " :
self . entrer ( )
def dupliquer ( self ) :
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 . config ( menu = menu_bar )
def change_affichage ( self , affichage ) :
""" change le mode d ' affichage,
désactive les opérations de pile si 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 . configure ( state = ' disable ' )
else :
for child in self . frame_pile . winfo_children ( ) :
child . configure ( 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 " , " ... " )
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 " " "
try :
if len ( self . saisie_expression . get ( ) ) > 0 :
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 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 nombre : ajoute une nouvelle valeur dans le stack
ajoute un label avec cette valeur
si fonction : on l ' évalue " " "
if " . " in terme :
terme = float ( 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 :
try :
terme = int ( terme )
self . stack . empiler ( ValeurSaisie ( terme , self . frame_stack ) )
except ValueError :
messagebox . showerror ( " Saisie invalide " , " Vous n ' avez pas entré un nombre, ou la fonction 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 = 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 " :
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, 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 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 " )
hello_frame = Interface ( root )
hello_frame . mainloop ( )