Browse Source

bug racine carre

master
BARRAUX Arthur 2 years ago
parent
commit
a260d3f23d
  1. 14
      main.py
  2. 689
      package/ctk_app.py
  3. 668
      package/data_structure.py
  4. 146
      package/expression.py
  5. 659
      package/tk_app.py

14
main.py

@ -1,7 +1,7 @@
# test si custom tkinter fonction sur le pc # test si custom tkinter fonction sur le pc
try: try:
from package.ctk_app import App from package.ctk_app import App
except ImportError: except ImportError:
from package.tk_app import App from package.tk_app import App
App().mainloop() App().mainloop()

689
package/ctk_app.py

@ -1,343 +1,346 @@
"""Classe d'application tk et ctk""" """Classe d'application tk et ctk"""
import math import math
import customtkinter as ctk import customtkinter as ctk
from package.data_structure import Pile_chaine from package.data_structure import Pile_chaine
from package.expression import Expression from package.expression import Expression
def change_appearance_mode_event(new_appearance_mode: str) -> None: def change_appearance_mode_event(new_appearance_mode: str) -> None:
"""Change le thème de sombre à Claire""" """Change le thème de sombre à Claire"""
ctk.set_appearance_mode(new_appearance_mode) ctk.set_appearance_mode(new_appearance_mode)
def parse_string_to_list(text: str) -> list: def parse_string_to_list(text: str) -> list:
""" Transforme le text en liste d'éléments intelligible par le programme """ """ Transforme le text en liste d'éléments intelligible par le programme """
text = list(text) text = list(text)
result = [] result = []
buffer_function = "" buffer_function = ""
buffer_number = "" buffer_number = ""
number_first = False # Savoir dans quel ordre sont les chiffres et les lettres number_first = False # Savoir dans quel ordre sont les chiffres et les lettres
for char in text: for char in text:
if char == 'e': if char == 'e':
result.append(math.e) result.append(math.e)
elif char == '𝜋': elif char == '𝜋':
result.append(math.pi) result.append(math.pi)
elif char.isdigit() or char == ".": elif char.isdigit() or char == ".":
buffer_number += char buffer_number += char
if len(buffer_function) == 0: if len(buffer_function) == 0:
number_first = True number_first = True
elif char.isalpha(): elif char.isalpha():
buffer_function += char buffer_function += char
else: else:
if number_first: if number_first:
result.append(float(buffer_number)) result.append(float(buffer_number))
buffer_number = "" buffer_number = ""
number_first = False number_first = False
if len(buffer_function) != 0: if len(buffer_function) != 0:
result.append('*') result.append('*')
result.append(buffer_function) result.append(buffer_function)
buffer_function = "" buffer_function = ""
elif char == '(': elif char == '(':
result.append('*') result.append('*')
else: else:
if len(buffer_function) != 0: if len(buffer_function) != 0:
result.append(buffer_function) result.append(buffer_function)
buffer_function = "" buffer_function = ""
if len(buffer_number) != 0: if len(buffer_number) != 0:
result.append(float(buffer_number)) result.append(float(buffer_number))
buffer_number = "" buffer_number = ""
number_first = False number_first = False
result.append(char) result.append(char)
if len(buffer_number) != 0: if len(buffer_number) != 0:
result.append(float(buffer_number)) result.append(float(buffer_number))
if len(buffer_function) != 0: if len(buffer_function) != 0:
result.append('*') result.append('*')
result.append(buffer_function) result.append(buffer_function)
elif len(buffer_function) != 0: elif len(buffer_function) != 0:
result.append(buffer_function) result.append(buffer_function)
return result return result
def npi2tree(expr: list) -> Expression: def npi2tree(expr: list) -> Expression:
""" Renvoie l'arbre formé à partir de l'expression donnée""" """ Renvoie l'arbre formé à partir de l'expression donnée"""
pile = Pile_chaine() pile = Pile_chaine()
for val in expr: for val in expr:
if not type(val) is float and val != "x": if not type(val) is float and val != "x":
# on inverse pour avoir les nombres dans le bon ordre # on inverse pour avoir les nombres dans le bon ordre
nombre2 = pile.depiler() nombre2 = pile.depiler()
if not pile.est_vide(): if not pile.est_vide():
pile.empiler(Expression(val, pile.depiler(), nombre2)) pile.empiler(Expression(val, pile.depiler(), nombre2))
else: else:
pile.empiler(Expression(val, nombre2)) pile.empiler(Expression(val, nombre2))
else: else:
pile.empiler(Expression(val)) pile.empiler(Expression(val))
return pile.sommet() return pile.sommet()
def inf2npi(expr: list) -> list: def inf2npi(expr: list) -> list:
"""Transforme une expression infixé en notation polonaise inversée""" """Transforme une expression infixé en notation polonaise inversée"""
operator_stack = Pile_chaine() operator_stack = Pile_chaine()
operator_priority = { operator_priority = {
'+': 1, '+': 1,
'-': 1, '-': 1,
'*': 2, '*': 2,
'^': 3, '^': 3,
'/': 2, '/': 2,
'(': 0, '(': 0,
')': 0 ')': 0
} }
output = [] output = []
for val in expr: for val in expr:
if type(val) is float or val == 'x': if type(val) is float or val == 'x':
output.append(val) output.append(val)
else: else:
if operator_stack.est_vide() or ( val == '(' or operator_priority[val] > operator_priority[operator_stack.sommet()]): if operator_stack.est_vide() or ( val == '(' or operator_priority[val] > operator_priority[operator_stack.sommet()]):
operator_stack.empiler(val) operator_stack.empiler(val)
else: else:
while not operator_stack.est_vide(): while not operator_stack.est_vide():
if operator_stack.sommet() == '(': if operator_stack.sommet() == '(':
operator_stack.depiler() operator_stack.depiler()
else: else:
output.append(operator_stack.depiler()) output.append(operator_stack.depiler())
if val != ')': if val != ')':
operator_stack.empiler(val) operator_stack.empiler(val)
while not operator_stack.est_vide(): while not operator_stack.est_vide():
output.append(operator_stack.depiler()) output.append(operator_stack.depiler())
return output return output
class App(ctk.CTk): class App(ctk.CTk):
"""Classe pour l'interface graphique avec custom tkinter""" """Classe pour l'interface graphique avec custom tkinter"""
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
change_appearance_mode_event('dark') change_appearance_mode_event('dark')
self.fonction_screen_height = None self.fonction_screen_height = None
self.fonction_screen_width = None self.fonction_screen_width = None
self.grid_columnconfigure(0, weight=0) self.grid_columnconfigure(0, weight=0)
self.grid_columnconfigure((1,2,3,4,5), weight=1) self.grid_columnconfigure((1,2,3,4,5), weight=1)
self.grid_rowconfigure(0, weight=0) self.grid_rowconfigure(0, weight=0)
self.grid_rowconfigure((1,2,3,4,5), weight=1) self.grid_rowconfigure((1,2,3,4,5), weight=1)
# touches de la calculette # touches de la calculette
self.keys = [] self.keys = []
for i, val in enumerate(["", "cos", "sin", "tan", "", "x", "e", "", "^", "𝜋", 7, 8, 9, "(", ")", 4, 5, 6, "*", "/", 1, 2, 3, "+", "-", for i, val in enumerate(["", "cos", "sin", "tan", "", "x", "e", "", "^", "𝜋", 7, 8, 9, "(", ")", 4, 5, 6, "*", "/", 1, 2, 3, "+", "-",
"clear", 0, ".", "ln", "exe"]): # add numbers button "clear", 0, ".", "ln", "exe"]): # add numbers button
if type(val) == int: if type(val) == int:
self.keys.append(ctk.CTkButton(self, text=val, command=lambda x=val: self.add_value(x), fg_color=("gray50","gray20"), text_color="gray90", hover_color=("gray40", "gray30"))) self.keys.append(ctk.CTkButton(self, text=val, command=lambda x=val: self.add_value(x), fg_color=("gray50","gray20"), text_color="gray90", hover_color=("gray40", "gray30")))
elif val == "clear": elif val == "clear":
self.keys.append(ctk.CTkButton(self, text=val, command=self.clear_screen)) self.keys.append(ctk.CTkButton(self, text=val, command=self.clear_screen))
elif val == "": elif val == "":
self.keys.append(ctk.CTkButton(self, text=val, command=self.move_cursor_left)) self.keys.append(ctk.CTkButton(self, text=val, command=self.move_cursor_left))
elif val == "": elif val == "":
self.keys.append(ctk.CTkButton(self, text=val, command=self.move_cursor_right)) self.keys.append(ctk.CTkButton(self, text=val, command=self.move_cursor_right))
else: else:
self.keys.append(ctk.CTkButton(self, text=val, command=lambda x=val: self.add_value(x))) self.keys.append(ctk.CTkButton(self, text=val, command=lambda x=val: self.add_value(x)))
self.keys[i].grid(row=i//5+1, column=i % 5+1, sticky="NSEW", padx=5, pady=5) self.keys[i].grid(row=i//5+1, column=i % 5+1, sticky="NSEW", padx=5, pady=5)
# Calcul Frame # Calcul Frame
self.calcul_frame = ctk.CTkFrame(self, corner_radius=0, fg_color="transparent") self.calcul_frame = ctk.CTkFrame(self, corner_radius=0, fg_color="transparent")
self.calcul_screen = ctk.CTkLabel(self.calcul_frame, text="hello") self.calcul_screen = ctk.CTkLabel(self.calcul_frame, text="hello")
self.calcul_screen.pack(fill="both", expand=True) self.calcul_screen.pack(fill="both", expand=True)
self.calcul_entry = ctk.CTkEntry(self.calcul_frame) self.calcul_entry = ctk.CTkEntry(self.calcul_frame)
self.calcul_entry.pack(fill="both", expand=True) self.calcul_entry.pack(fill="both", expand=True)
# Fonction Frame # Fonction Frame
self.fonction_frame = ctk.CTkFrame(self, fg_color="transparent") self.fonction_frame = ctk.CTkFrame(self, fg_color="transparent")
self.fonction_bornes_frame = ctk.CTkFrame(self.fonction_frame, corner_radius=None) self.fonction_bornes_frame = ctk.CTkFrame(self.fonction_frame, corner_radius=None)
self.fonction_bornes_entry = ctk.CTkEntry(self.fonction_bornes_frame) self.fonction_bornes_entry = ctk.CTkEntry(self.fonction_bornes_frame)
self.fonction_bornes_entry.insert(0, "-100,100") self.fonction_bornes_entry.insert(0, "-100,100")
self.fonction_bornes_entry.grid(sticky="ew", row=1, padx=10) self.fonction_bornes_entry.grid(sticky="ew", row=1, padx=10)
self.fonction_bornes_text = ctk.CTkLabel(self.fonction_bornes_frame, text="Entrez les bornes de tracé: (min, max)") self.fonction_bornes_text = ctk.CTkLabel(self.fonction_bornes_frame, text="Entrez les bornes de tracé: (min, max)")
self.fonction_bornes_text.grid(sticky="ew", row=0) self.fonction_bornes_text.grid(sticky="ew", row=0)
self.fonction_bornes_frame.grid(sticky="ns", column=0, rowspan=2) self.fonction_bornes_frame.grid(sticky="ns", column=0, rowspan=2)
self.fonction_screen = ctk.CTkCanvas(self.fonction_frame) self.fonction_screen = ctk.CTkCanvas(self.fonction_frame)
self.fonction_screen.grid(sticky="nsew", column=1, row=0) self.fonction_screen.grid(sticky="nsew", column=1, row=0)
self.fonction_entry = ctk.CTkEntry(self.fonction_frame) self.fonction_entry = ctk.CTkEntry(self.fonction_frame)
self.fonction_entry.grid(sticky='ew', column=1, row=1) self.fonction_entry.grid(sticky='ew', column=1, row=1)
# navigation menu # navigation menu
self.navigation_frame = ctk.CTkFrame(self, corner_radius=0) self.navigation_frame = ctk.CTkFrame(self, corner_radius=0)
self.navigation_frame.grid_rowconfigure(4, weight=1) self.navigation_frame.grid_rowconfigure(4, weight=1)
self.navigation_frame.grid(row=0, rowspan=10, column=0, sticky="nsew") self.navigation_frame.grid(row=0, rowspan=10, column=0, sticky="nsew")
self.titre = ctk.CTkLabel(self.navigation_frame, text="Wx Calculator", compound="left" self.titre = ctk.CTkLabel(self.navigation_frame, text="Wx Calculator", compound="left"
, font=ctk.CTkFont(size=15, weight="bold")) , font=ctk.CTkFont(size=15, weight="bold"))
self.titre.grid(row=0, column=0, padx=20, pady=20) self.titre.grid(row=0, column=0, padx=20, pady=20)
self.home_button = ctk.CTkButton(self.navigation_frame, corner_radius=0, height=40, border_spacing=10 self.home_button = ctk.CTkButton(self.navigation_frame, corner_radius=0, height=40, border_spacing=10
, text="Calcul", fg_color="transparent", text_color=("gray10", "gray90") , text="Calcul", fg_color="transparent", text_color=("gray10", "gray90")
, hover_color=("gray70", "gray30"), anchor="w" , hover_color=("gray70", "gray30"), anchor="w"
, command=lambda: self.select_frame_by_name("Calcul")) , command=lambda: self.select_frame_by_name("Calcul"))
self.home_button.grid(row=1, column=0, sticky="ew") self.home_button.grid(row=1, column=0, sticky="ew")
self.function_button = ctk.CTkButton(self.navigation_frame, corner_radius=0, height=40, border_spacing=10 self.function_button = ctk.CTkButton(self.navigation_frame, corner_radius=0, height=40, border_spacing=10
, text="Fonction", fg_color="transparent", text_color=("gray10", "gray90") , text="Fonction", fg_color="transparent", text_color=("gray10", "gray90")
, hover_color=("gray70", "gray30"), anchor="w" , hover_color=("gray70", "gray30"), anchor="w"
, command=lambda: self.select_frame_by_name("Fonction")) , command=lambda: self.select_frame_by_name("Fonction"))
self.function_button.grid(row=2, column=0, sticky="ew") self.function_button.grid(row=2, column=0, sticky="ew")
self.appearance_mode_menu = ctk.CTkOptionMenu(self.navigation_frame, values=["Light", "Dark", "System"] self.appearance_mode_menu = ctk.CTkOptionMenu(self.navigation_frame, values=["Light", "Dark", "System"]
, command=change_appearance_mode_event) , command=change_appearance_mode_event)
self.appearance_mode_menu.grid(row=4, column=0, padx=20, pady=20, sticky="s") self.appearance_mode_menu.grid(row=4, column=0, padx=20, pady=20, sticky="s")
self.mode = 'Calcul' self.mode = 'Calcul'
# select default frame # select default frame
self.select_frame_by_name("Calcul") self.select_frame_by_name("Calcul")
def select_frame_by_name(self, name): def select_frame_by_name(self, name):
"""Change de mode : passe de calcul à fonction""" """Change de mode : passe de calcul à fonction"""
# set button color for selected button # set button color for selected button
self.home_button.configure(fg_color=("gray75", "gray25") if name == "Calcul" else "transparent") self.home_button.configure(fg_color=("gray75", "gray25") if name == "Calcul" else "transparent")
self.function_button.configure(fg_color=("gray75", "gray25") if name == "Fonction" else "transparent") self.function_button.configure(fg_color=("gray75", "gray25") if name == "Fonction" else "transparent")
# show selected frame # show selected frame
if name == "Calcul": if name == "Calcul":
self.mode = "Calcul" self.mode = "Calcul"
self.calcul_frame.grid(padx=20, pady=20, columnspan=5, column=1, row=0, sticky="nsew") self.calcul_frame.grid(padx=20, pady=20, columnspan=5, column=1, row=0, sticky="nsew")
self.keys[-1]._command = self.calculate self.keys[-1]._command = self.calculate
else: else:
self.calcul_frame.grid_forget() self.calcul_frame.grid_forget()
if name == "Fonction": if name == "Fonction":
self.mode = "Fonction" self.mode = "Fonction"
self.fonction_frame.grid(columnspan=5, column=1, row=0, padx=20, pady=20, sticky="ns") self.fonction_frame.grid(columnspan=5, column=1, row=0, padx=20, pady=20, sticky="ns")
self.fonction_frame.update() self.fonction_frame.update()
self.fonction_screen_width = self.fonction_screen.winfo_width() self.fonction_screen_width = self.fonction_screen.winfo_width()
self.keys[-1]._command = self.draw_graph self.keys[-1]._command = self.draw_graph
self.fonction_screen_height = self.fonction_screen.winfo_height() self.fonction_screen_height = self.fonction_screen.winfo_height()
else: else:
self.fonction_frame.grid_forget() self.fonction_frame.grid_forget()
def add_value(self, value) -> None: def add_value(self, value) -> None:
"""Ajoute le charactère à la suite de l'expression""" """Ajoute le charactère à la suite de l'expression"""
parenthesis = False parenthesis = False
if value == "e": if value == "e":
value += "^" value += "^"
if value == "(": if value == "(":
value += ")" value += ")"
parenthesis = True parenthesis = True
elif value in ["ln", "cos", "sin", "tan", ""]: elif value in ["ln", "cos", "sin", "tan", ""]:
value += "()" value += "()"
parenthesis = True parenthesis = True
if self.mode == 'Fonction': if self.mode == 'Fonction':
self.fonction_entry.insert(ctk.INSERT, value) self.fonction_entry.insert(ctk.INSERT, value)
else: else:
self.calcul_entry.insert(ctk.INSERT, value) self.calcul_entry.insert(ctk.INSERT, value)
if parenthesis: if parenthesis:
self.move_cursor_left() self.move_cursor_left()
def clear_screen(self) -> None: def clear_screen(self) -> None:
"""Enlève l'affichage actuel""" """Enlève l'affichage actuel"""
if self.mode == "Calcul": if self.mode == "Calcul":
self.calcul_screen.configure(text="") self.calcul_screen.configure(text="")
self.calcul_entry.delete('0', 'end') self.calcul_entry.delete('0', 'end')
else: else:
self.fonction_screen.delete('all') self.fonction_screen.delete('all')
self.fonction_entry.delete(0, 'end') self.fonction_entry.delete(0, 'end')
def move_cursor_left(self): def move_cursor_left(self):
"""Déplace le curseur à gauche""" """Déplace le curseur à gauche"""
if self.mode == "Calcul": if self.mode == "Calcul":
pos = self.calcul_entry.index(ctk.INSERT) pos = self.calcul_entry.index(ctk.INSERT)
if pos > 0: if pos > 0:
self.calcul_entry.icursor(pos -1) self.calcul_entry.icursor(pos -1)
else: else:
pos = self.fonction_entry.index(ctk.INSERT) pos = self.fonction_entry.index(ctk.INSERT)
if pos > 0: if pos > 0:
self.fonction_entry.icursor(pos-1) self.fonction_entry.icursor(pos-1)
def move_cursor_right(self): def move_cursor_right(self):
"""Déplace le curseur à droite""" """Déplace le curseur à droite"""
if self.mode == "Calcul": if self.mode == "Calcul":
pos = self.calcul_entry.index(ctk.INSERT) pos = self.calcul_entry.index(ctk.INSERT)
if pos < len(self.calcul_entry.get()): if pos < len(self.calcul_entry.get()):
self.calcul_entry.icursor(pos + 1) self.calcul_entry.icursor(pos + 1)
else: else:
pos = self.fonction_entry.index(ctk.INSERT) pos = self.fonction_entry.index(ctk.INSERT)
if pos < len(self.fonction_entry.get()): if pos < len(self.fonction_entry.get()):
self.fonction_entry.icursor(pos + 1) self.fonction_entry.icursor(pos + 1)
def draw_framing(self, min_x, max_x, min_y, max_y): def draw_framing(self, min_x, max_x, min_y, max_y):
"""Dessine le cadrillage du graphique""" """Dessine le cadrillage du graphique"""
ratio = self.fonction_screen_height / self.fonction_screen_width # longueur par largeur du canvas ratio = self.fonction_screen_height / self.fonction_screen_width # longueur par largeur du canvas
# position du max dans l'interval # position du max dans l'interval
abscisse = max_y / (abs(min_y)+abs(max_y)) abscisse = max_y / (abs(min_y)+abs(max_y))
ordonnee = max_x / (abs(min_x)+abs(max_x)) ordonnee = max_x / (abs(min_x)+abs(max_x))
for i in range(20): # dessin des lignes verticales for i in range(20): # dessin des lignes verticales
if i == int(20 - 20*ordonnee): if i == int(20 - 20*ordonnee):
self.fonction_screen.create_line(0 + i * self.fonction_screen_width/20, 0 self.fonction_screen.create_line(0 + i * self.fonction_screen_width/20, 0
, 0 + i * self.fonction_screen_width/20, self.fonction_screen_height , 0 + i * self.fonction_screen_width/20, self.fonction_screen_height
, fill='red', width=4) , fill='red', width=4)
text = self.fonction_screen.create_text(0 + i * self.fonction_screen_width/20, 10, text=max_y) text = self.fonction_screen.create_text(0 + i * self.fonction_screen_width/20, 10, text=max_y)
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white") rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white")
self.fonction_screen.tag_lower(rect, text) self.fonction_screen.tag_lower(rect, text)
text = self.fonction_screen.create_text(0 + i * self.fonction_screen_width/20, text = self.fonction_screen.create_text(0 + i * self.fonction_screen_width/20,
self.fonction_screen_height - 10, text=min_y) self.fonction_screen_height - 10, text=min_y)
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white") rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white")
self.fonction_screen.tag_lower(rect, text) self.fonction_screen.tag_lower(rect, text)
else: else:
self.fonction_screen.create_line(0 + i * self.fonction_screen_width/20, 0 self.fonction_screen.create_line(0 + i * self.fonction_screen_width/20, 0
, 0 + i * self.fonction_screen_width/20, self.fonction_screen_height , 0 + i * self.fonction_screen_width/20, self.fonction_screen_height
, fill='red') , fill='red')
nb_abscisse_lines = int(20*ratio) # nombre de lignes horizontales à tracer nb_abscisse_lines = int(20*ratio) # nombre de lignes horizontales à tracer
for i in range(nb_abscisse_lines): # dessin des lignes horizontales for i in range(nb_abscisse_lines): # dessin des lignes horizontales
if i == math.ceil(nb_abscisse_lines * abscisse): # tracer de l'axe des abscisses if i == math.ceil(nb_abscisse_lines * abscisse): # tracer de l'axe des abscisses
self.fonction_screen.create_line(0, 0 + i * self.fonction_screen_width/20 self.fonction_screen.create_line(0, 0 + i * self.fonction_screen_width/20
, self.fonction_screen_width , self.fonction_screen_width
, 0 + i * self.fonction_screen_width/20, fill='red' , 0 + i * self.fonction_screen_width/20, fill='red'
, width=4) , width=4)
text = self.fonction_screen.create_text(15, 0 + i * self.fonction_screen_width/20 text = self.fonction_screen.create_text(15, 0 + i * self.fonction_screen_width/20
, text=min_x) , text=min_x)
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text) rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text)
, fill="white") , fill="white")
self.fonction_screen.tag_lower(rect, text) self.fonction_screen.tag_lower(rect, text)
text = self.fonction_screen.create_text(self.fonction_screen_width - 15 text = self.fonction_screen.create_text(self.fonction_screen_width - 15
, 0 + i * self.fonction_screen_width/20 , 0 + i * self.fonction_screen_width/20
, text=max_x) , text=max_x)
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white") rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white")
self.fonction_screen.tag_lower(rect, text) self.fonction_screen.tag_lower(rect, text)
else: else:
self.fonction_screen.create_line(0, 0 + i * self.fonction_screen_width/20 self.fonction_screen.create_line(0, 0 + i * self.fonction_screen_width/20
, self.fonction_screen_width , self.fonction_screen_width
, 0 + i * self.fonction_screen_width/20, fill='red') , 0 + i * self.fonction_screen_width/20, fill='red')
def calculate(self) -> None: def calculate(self) -> None:
"""Calcule dans la partie graphique""" """Calcule dans la partie graphique"""
exp = parse_string_to_list(self.calcul_entry.get()) exp = parse_string_to_list(self.calcul_entry.get())
exp = npi2tree(inf2npi(exp)) exp = npi2tree(inf2npi(exp))
exp.print_tree() exp.print_tree()
try: try:
result = exp.evalue() result = exp.evalue()
except ZeroDivisionError: except ZeroDivisionError:
result = "ERREUR" result = "ERREUR"
self.calcul_screen.configure(text=result) self.calcul_screen.configure(text=result)
def draw_graph(self): def draw_graph(self):
"""Dessine les points du graphique""" """Dessine les points du graphique"""
self.fonction_screen.delete('all') self.fonction_screen.delete('all')
min_x, max_x = map(int, self.fonction_bornes_entry.get().split(',')) min_x, max_x = map(int, self.fonction_bornes_entry.get().split(',')) # bornes du graphique
fonction_points = npi2tree(inf2npi(parse_string_to_list(self.fonction_entry.get()))).valeurs_de_fonction(min_x, max_x) fonction_points = npi2tree(inf2npi(parse_string_to_list(self.fonction_entry.get()))).valeurs_de_fonction(min_x, max_x)
max_y = max(fonction_points, key=lambda item: item[1])[1] max_y = max(fonction_points, key=lambda item: item[1])[1]
min_y = min(fonction_points, key=lambda item: item[1])[1] min_y = min(fonction_points, key=lambda item: item[1])[1]
self.draw_framing(min_x, max_x, min_y, max_y) self.draw_framing(min_x, max_x, min_y, max_y)
for i, (x, y) in enumerate(fonction_points): ratio = (fonction_points[0][0] - min_x) / (abs(max_x)+abs(min_x))
image_x = i * (self.fonction_screen_width/len(fonction_points)) i = len(fonction_points) * ratio
image_y = self.fonction_screen_height - (y - min_y) * self.fonction_screen_height / (abs(max_y)+abs(min_y)) for (x, y) in fonction_points:
self.fonction_screen.create_rectangle(image_x, image_y, image_x, image_y) image_x = i * (self.fonction_screen_width/len(fonction_points))
image_y = self.fonction_screen_height - (y - min_y) * self.fonction_screen_height / (abs(max_y)+abs(min_y))
self.fonction_screen.create_rectangle(image_x, image_y, image_x, image_y)
i += 1

668
package/data_structure.py

@ -1,335 +1,335 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Created on Tue Sep 22 20:44:35 2020 Created on Tue Sep 22 20:44:35 2020
@author: manu @author: manu
""" """
class Maillon: class Maillon:
"""Un maillon d'une liste chaînée.""" """Un maillon d'une liste chaînée."""
def __init__(self, valeur, suivant): def __init__(self, valeur, suivant):
self.valeur = valeur self.valeur = valeur
self.suivant = suivant self.suivant = suivant
def __str__(self): def __str__(self):
"""Renvoie une chane de caractères représentant le maillon.""" """Renvoie une chane de caractères représentant le maillon."""
return str(self.valeur) return str(self.valeur)
class ListeChaine: class ListeChaine:
"""Une liste chaînée.""" """Une liste chaînée."""
def __init__(self, tete=None): def __init__(self, tete=None):
"""Crée une liste vide, ou une liste dont la tete (un maillon) """Crée une liste vide, ou une liste dont la tete (un maillon)
est donnée.""" est donnée."""
self.__tete = tete self.__tete = tete
def est_vide(self): def est_vide(self):
"""Indique si la liste est vide.""" """Indique si la liste est vide."""
return self.__tete is None return self.__tete is None
def tete(self): def tete(self):
"""Renvoie la valeur du premier élément de la liste.""" """Renvoie la valeur du premier élément de la liste."""
if self.est_vide(): if self.est_vide():
raise IndexError("La liste vide n'a pas de tête") raise IndexError("La liste vide n'a pas de tête")
return self.__tete.valeur return self.__tete.valeur
def queue(self): def queue(self):
"""Renvoie la queue de la liste.""" """Renvoie la queue de la liste."""
if self.est_vide(): if self.est_vide():
raise IndexError("La liste vide n'a pas de queue") raise IndexError("La liste vide n'a pas de queue")
return ListeChaine(self.__tete.suivant) return ListeChaine(self.__tete.suivant)
def ajoute(self, valeur): def ajoute(self, valeur):
"""ajoute `valeur` en tête de la liste.""" """ajoute `valeur` en tête de la liste."""
self.__tete = Maillon(valeur, self.__tete) self.__tete = Maillon(valeur, self.__tete)
def __str__(self): def __str__(self):
"""Renvoie une chaîne de caractères représentant la liste.""" """Renvoie une chaîne de caractères représentant la liste."""
maillon = self.__tete maillon = self.__tete
s = '' s = ''
while maillon is not None: while maillon is not None:
s = s + str(maillon.valeur) s = s + str(maillon.valeur)
maillon = maillon.suivant maillon = maillon.suivant
if maillon is not None: if maillon is not None:
s += '->' s += '->'
return s return s
def __len__(self): def __len__(self):
"""Renvoie la longueur de la liste.""" """Renvoie la longueur de la liste."""
maillon = self.__tete maillon = self.__tete
long = 0 long = 0
while maillon is not None: while maillon is not None:
long = long + 1 long = long + 1
maillon = maillon.suivant maillon = maillon.suivant
return long return long
def __getitem__(self, n): def __getitem__(self, n):
"""Renvoie l'élément d'indice n de la liste.""" """Renvoie l'élément d'indice n de la liste."""
maillon = self.__tete maillon = self.__tete
i = 0 i = 0
while i < n and maillon is not None: while i < n and maillon is not None:
i = i + 1 i = i + 1
maillon = maillon.suivant maillon = maillon.suivant
if maillon is None or n < 0: if maillon is None or n < 0:
raise IndexError("Indice non valide") raise IndexError("Indice non valide")
return maillon.valeur return maillon.valeur
def __add__(self, other): def __add__(self, other):
"""Renvoie la liste correspondant à la concaténation des 2 listes.""" """Renvoie la liste correspondant à la concaténation des 2 listes."""
if self.est_vide(): if self.est_vide():
return other return other
v = self.tete() v = self.tete()
q = self.queue() q = self.queue()
return ListeChaine(Maillon(v, (q + other).__tete)) return ListeChaine(Maillon(v, (q + other).__tete))
def reverse(self): def reverse(self):
"""Renvoie une liste correspondant à la liste renversée.""" """Renvoie une liste correspondant à la liste renversée."""
res = ListeChaine() res = ListeChaine()
maillon = self.__tete maillon = self.__tete
while maillon is not None: while maillon is not None:
res.ajoute(maillon.valeur) res.ajoute(maillon.valeur)
maillon = maillon.suivant maillon = maillon.suivant
return res return res
class Pile_lst: class Pile_lst:
"""Implémentation d'une pile par une liste.""" """Implémentation d'une pile par une liste."""
def __init__(self): def __init__(self):
"""Crée une pile vide.""" """Crée une pile vide."""
self.__pile = [] self.__pile = []
def est_vide(self): def est_vide(self):
"""Indique si la pile est vide.""" """Indique si la pile est vide."""
return self.__pile == [] return self.__pile == []
def empiler(self, valeur): def empiler(self, valeur):
"""Empile la valeur.""" """Empile la valeur."""
self.__pile.append(valeur) self.__pile.append(valeur)
def depiler(self): def depiler(self):
"""Dépile le sommet de la pile et le renvoie.""" """Dépile le sommet de la pile et le renvoie."""
return self.__pile.pop() return self.__pile.pop()
def taille(self): def taille(self):
"""Renvoie la taille de la pile.""" """Renvoie la taille de la pile."""
return len(self.__pile) return len(self.__pile)
def sommet(self): def sommet(self):
"""Renvoie le sommet de la pile (sans le dépiler).""" """Renvoie le sommet de la pile (sans le dépiler)."""
return self.__pile[-1] return self.__pile[-1]
def __str__(self): def __str__(self):
s = "|" s = "|"
for val in self.__pile: for val in self.__pile:
s = str(val) + "->" + s s = str(val) + "->" + s
return s return s
class Pile_chaine: class Pile_chaine:
"""Implémentation d'une pile par une liste chaînée.""" """Implémentation d'une pile par une liste chaînée."""
def __init__(self): def __init__(self):
"""Crée une pile vide.""" """Crée une pile vide."""
self.__pile = ListeChaine() self.__pile = ListeChaine()
self.__taille = 0 self.__taille = 0
def est_vide(self): def est_vide(self):
"""Indique si la pile est vide.""" """Indique si la pile est vide."""
return self.__taille == 0 return self.__taille == 0
def empiler(self, valeur): def empiler(self, valeur):
"""Empile la valeur.""" """Empile la valeur."""
self.__pile.ajoute(valeur) self.__pile.ajoute(valeur)
self.__taille += 1 self.__taille += 1
def depiler(self): def depiler(self):
"""Dépile le sommet de la pile et le renvoie.""" """Dépile le sommet de la pile et le renvoie."""
if self.est_vide(): if self.est_vide():
raise IndexError("Impossible de dépiler une pile vide.") raise IndexError("Impossible de dépiler une pile vide.")
valeur = self.__pile.tete() valeur = self.__pile.tete()
self.__pile = self.__pile.queue() self.__pile = self.__pile.queue()
self.__taille -= 1 self.__taille -= 1
return valeur return valeur
def taille(self): def taille(self):
"""Renvoie la taille de la pile.""" """Renvoie la taille de la pile."""
return self.__taille return self.__taille
def sommet(self): def sommet(self):
"""Renvoie le sommet de la pile (sans le dépiler).""" """Renvoie le sommet de la pile (sans le dépiler)."""
if self.est_vide(): if self.est_vide():
raise IndexError("Une pile vide n'a pas de sommet.") raise IndexError("Une pile vide n'a pas de sommet.")
return self.__pile.tete() return self.__pile.tete()
def __str__(self): def __str__(self):
return str(self.__pile) + "->|" return str(self.__pile) + "->|"
class File_lst: class File_lst:
"""Implémentation d'une file par une liste.""" """Implémentation d'une file par une liste."""
def __init__(self): def __init__(self):
"""Crée une file vide.""" """Crée une file vide."""
self.__file = [] self.__file = []
def est_vide(self): def est_vide(self):
"""Indique si la file est vide.""" """Indique si la file est vide."""
return self.__file == [] return self.__file == []
def enfiler(self, valeur): def enfiler(self, valeur):
"""Enfile l'élément valeur.""" """Enfile l'élément valeur."""
self.__file.append(valeur) self.__file.append(valeur)
def defiler(self): def defiler(self):
"""Défile la tête de la file et la renvoie.""" """Défile la tête de la file et la renvoie."""
return self.__file.pop(0) return self.__file.pop(0)
def taille(self): def taille(self):
"""Renvoie la taille de la file.""" """Renvoie la taille de la file."""
return len(self.__file) return len(self.__file)
def tete(self): def tete(self):
"""Renvoie la tête de la file (sans la défiler).""" """Renvoie la tête de la file (sans la défiler)."""
return self.__file[0] return self.__file[0]
def __str__(self): def __str__(self):
s = "tete->" s = "tete->"
for val in self.__file: for val in self.__file:
s += str(val) + "->" s += str(val) + "->"
return s + "queue" return s + "queue"
class Maillon_db: class Maillon_db:
"""Un maillon d'une liste doublement chaînée.""" """Un maillon d'une liste doublement chaînée."""
def __init__(self, precedent, valeur, suivant): def __init__(self, precedent, valeur, suivant):
self.valeur = valeur self.valeur = valeur
self.precedent = precedent self.precedent = precedent
self.suivant = suivant self.suivant = suivant
def __str__(self): def __str__(self):
return str(self.valeur) return str(self.valeur)
class File_chaine: class File_chaine:
"""Implémentation d'une file par une liste doublement chaînée.""" """Implémentation d'une file par une liste doublement chaînée."""
def __init__(self): def __init__(self):
"""Crée une file vide.""" """Crée une file vide."""
self.__debut = self.__fin = None self.__debut = self.__fin = None
self.__taille = 0 self.__taille = 0
def __iter__(self): def __iter__(self):
return self return self
def __next__(self): def __next__(self):
if self.est_vide(): if self.est_vide():
raise StopIteration raise StopIteration
return self.defiler() return self.defiler()
def est_vide(self): def est_vide(self):
"""Indique si la file est vide.""" """Indique si la file est vide."""
return self.__taille == 0 return self.__taille == 0
def enfiler(self, valeur): def enfiler(self, valeur):
"""Enfile l'élément valeur.""" """Enfile l'élément valeur."""
maillon = Maillon_db(self.__fin, valeur, None) maillon = Maillon_db(self.__fin, valeur, None)
if self.est_vide(): if self.est_vide():
self.__debut = self.__fin = maillon self.__debut = self.__fin = maillon
else: else:
self.__fin.suivant = maillon self.__fin.suivant = maillon
self.__fin = maillon self.__fin = maillon
self.__taille += 1 self.__taille += 1
def defiler(self): def defiler(self):
"""Défile la tête de la file et la renvoie.""" """Défile la tête de la file et la renvoie."""
if self.est_vide(): if self.est_vide():
raise IndexError("Impossible de défiler une file vide.") raise IndexError("Impossible de défiler une file vide.")
valeur = self.__debut.valeur valeur = self.__debut.valeur
self.__taille -= 1 self.__taille -= 1
if self.est_vide(): if self.est_vide():
self.__debut = self.__fin = None self.__debut = self.__fin = None
else: else:
self.__debut = self.__debut.suivant self.__debut = self.__debut.suivant
self.__debut.precedent = None self.__debut.precedent = None
return valeur return valeur
def taille(self): def taille(self):
"""Renvoie la taille de la file.""" """Renvoie la taille de la file."""
return self.__taille return self.__taille
def tete(self): def tete(self):
"""Renvoie la tête de la file (sans la défiler).""" """Renvoie la tête de la file (sans la défiler)."""
if self.est_vide(): if self.est_vide():
raise IndexError("Une file vide n'a pas de tête.") raise IndexError("Une file vide n'a pas de tête.")
return self.__debut.valeur return self.__debut.valeur
def __str__(self): def __str__(self):
s = "tete->" s = "tete->"
maillon = self.__debut maillon = self.__debut
while maillon is not None: while maillon is not None:
s += str(maillon) + "->" s += str(maillon) + "->"
maillon = maillon.suivant maillon = maillon.suivant
return s + "queue" return s + "queue"
class Arbre: class Arbre:
def __init__(self, val) -> None: def __init__(self, val) -> None:
self.val = val self.val = val
self.gauche = None self.gauche = None
self.droit = None self.droit = None
def insere_gauche(self, val) -> object: def insere_gauche(self, val) -> object:
"""insere un arbre gauche a la racine de l'arbre""" """insere un arbre gauche a la racine de l'arbre"""
nouvel_arbre = Arbre(val) nouvel_arbre = Arbre(val)
nouvel_arbre.gauche = self.gauche nouvel_arbre.gauche = self.gauche
self.gauche = nouvel_arbre self.gauche = nouvel_arbre
return nouvel_arbre return nouvel_arbre
def insere_droit(self, val) -> object: def insere_droit(self, val) -> object:
"""insere un arbre gauche a la racine de l'arbre""" """insere un arbre gauche a la racine de l'arbre"""
nouvel_arbre = Arbre(val) nouvel_arbre = Arbre(val)
nouvel_arbre.gauche = self.droit nouvel_arbre.gauche = self.droit
self.droit = nouvel_arbre self.droit = nouvel_arbre
return nouvel_arbre return nouvel_arbre
if __name__ == "__main__": if __name__ == "__main__":
f = File_lst() f = File_lst()
print(f.est_vide()) print(f.est_vide())
f.enfiler('A') f.enfiler('A')
f.enfiler('B') f.enfiler('B')
f.enfiler('C') f.enfiler('C')
print(f.est_vide()) print(f.est_vide())
print(f.tete()) print(f.tete())
print(f) print(f)
print(f.taille()) print(f.taille())
print(f.defiler()) print(f.defiler())
print(f.defiler()) print(f.defiler())
print(f.defiler()) print(f.defiler())
print(f.est_vide()) print(f.est_vide())
lst = ListeChaine() lst = ListeChaine()
print(lst.est_vide()) print(lst.est_vide())
lst.ajoute(306) lst.ajoute(306)
lst.ajoute(42) lst.ajoute(42)
lst.ajoute(205) lst.ajoute(205)
print(lst) print(lst)
print(lst.est_vide()) print(lst.est_vide())
print(lst[0]) print(lst[0])
print(lst[1]) print(lst[1])
print(len(lst)) print(len(lst))
lst2 = ListeChaine() lst2 = ListeChaine()
lst2.ajoute(18) lst2.ajoute(18)
lst2.ajoute(45) lst2.ajoute(45)
print(lst + lst2) print(lst + lst2)
p = Pile_lst() p = Pile_lst()
print(p.est_vide()) print(p.est_vide())
p.empiler('A') p.empiler('A')
p.empiler('B') p.empiler('B')
p.empiler('C') p.empiler('C')
print(p.est_vide()) print(p.est_vide())
print(p.sommet()) print(p.sommet())
print(p) print(p)
print(p.taille()) print(p.taille())
print(p.depiler()) print(p.depiler())
print(p.depiler()) print(p.depiler())
print(p.depiler()) print(p.depiler())
print(p.est_vide()) print(p.est_vide())

146
package/expression.py

@ -1,73 +1,73 @@
"""Fichier de classe pour les expressions""" """Fichier de classe pour les expressions"""
import math import math
class Expression: class Expression:
"""manipule les expression sous forme d'arbre""" """manipule les expression sous forme d'arbre"""
def __init__(self, val, fils_gauche=None, fils_droit=None) -> None: def __init__(self, val, fils_gauche=None, fils_droit=None) -> None:
self.val = val self.val = val
self.fils_gauche = fils_gauche self.fils_gauche = fils_gauche
self.fils_droit = fils_droit self.fils_droit = fils_droit
def __str__(self) -> str: def __str__(self) -> str:
"""renvoie l'expression sous forme infixé""" """renvoie l'expression sous forme infixé"""
if self.est_feuille(): if self.est_feuille():
return str(self.val) return str(self.val)
return '('+self.fils_gauche.__str__()+str(self.val)+self.fils_droit.__str__()+')' return '('+self.fils_gauche.__str__()+str(self.val)+self.fils_droit.__str__()+')'
def print_tree(self, level=0): def print_tree(self, level=0):
""" Affiche le noeud joliment""" """ Affiche le noeud joliment"""
if self.fils_gauche is not None: if self.fils_gauche is not None:
self.fils_gauche.print_tree(level + 1) self.fils_gauche.print_tree(level + 1)
print(' ' * 4 * level + '-> ' + str(self.val)) print(' ' * 4 * level + '-> ' + str(self.val))
if self.fils_droit is not None: if self.fils_droit is not None:
self.fils_droit.print_tree(level + 1) self.fils_droit.print_tree(level + 1)
def est_feuille(self) -> bool: def est_feuille(self) -> bool:
""" Renvoie true si le noeud est une feuille""" """ Renvoie true si le noeud est une feuille"""
if self.fils_droit is None and self.fils_gauche is None: if self.fils_droit is None and self.fils_gauche is None:
return True return True
return False return False
def evalue(self, x=0) -> float: def evalue(self, x=0) -> float:
""" Renvoie le résultat de l'expression""" """ Renvoie le résultat de l'expression"""
try: try:
if self.est_feuille(): if self.est_feuille():
if self.val == 'x': if self.val == 'x':
return x return x
return float(self.val) return float(self.val)
elif self.val == '+': elif self.val == '+':
return self.fils_gauche.evalue(x) + self.fils_droit.evalue(x) return self.fils_gauche.evalue(x) + self.fils_droit.evalue(x)
elif self.val == '*': elif self.val == '*':
return self.fils_gauche.evalue(x) * self.fils_droit.evalue(x) return self.fils_gauche.evalue(x) * self.fils_droit.evalue(x)
elif self.val == '/': elif self.val == '/':
return self.fils_gauche.evalue(x) / self.fils_droit.evalue(x) return self.fils_gauche.evalue(x) / self.fils_droit.evalue(x)
elif self.val == '^': elif self.val == '^':
return self.fils_gauche.evalue(x) ** self.fils_droit.evalue(x) return self.fils_gauche.evalue(x) ** self.fils_droit.evalue(x)
elif self.val == '-': elif self.val == '-':
return self.fils_gauche.evalue(x) - self.fils_droit.evalue(x) return self.fils_gauche.evalue(x) - self.fils_droit.evalue(x)
elif self.val == 'ln': elif self.val == 'ln':
return math.log(self.fils_gauche.evalue(x)) return math.log(self.fils_gauche.evalue(x))
elif self.val == '': elif self.val == '':
return math.sqrt(self.fils_gauche.evalue(x)) return math.sqrt(self.fils_gauche.evalue(x))
elif self.val == "cos": elif self.val == "cos":
return math.cos(self.fils_gauche.evalue(x)) return math.cos(self.fils_gauche.evalue(x))
elif self.val == "sin": elif self.val == "sin":
return math.sin(self.fils_gauche.evalue(x)) return math.sin(self.fils_gauche.evalue(x))
elif self.val == "tan": elif self.val == "tan":
return math.tan(self.fils_gauche.evalue(x)) return math.tan(self.fils_gauche.evalue(x))
except ZeroDivisionError: except ZeroDivisionError:
raise ZeroDivisionError raise ZeroDivisionError
def valeurs_de_fonction(self, start, end): def valeurs_de_fonction(self, start, end):
""" Calcul les valeurs entre start et end""" """ Calcul les valeurs entre start et end"""
result = [] result = []
pas = (end - start) / 1000 pas = (end - start) / 1000
while start <= end: while start <= end:
try: try:
result.append((start, self.evalue(start))) result.append((start, self.evalue(start)))
except: except:
pass pass
start += pas start += pas
return result return result

659
package/tk_app.py

@ -1,328 +1,331 @@
"""Classe d'application tk""" """Classe d'application tk"""
import math import math
import tkinter as tk import tkinter as tk
import tkinter.font import tkinter.font
from package.data_structure import Pile_chaine from package.data_structure import Pile_chaine
from package.expression import Expression from package.expression import Expression
def parse_string_to_list(text: str) -> list: def parse_string_to_list(text: str) -> list:
""" Transforme le text en liste d'éléments intelligible par le programme """ """ Transforme le text en liste d'éléments intelligible par le programme """
text = list(text) text = list(text)
result = [] result = []
buffer_function = "" buffer_function = ""
buffer_number = "" buffer_number = ""
number_first = False # Savoir dans quel ordre sont les chiffres et les lettres number_first = False # Savoir dans quel ordre sont les chiffres et les lettres
for char in text: for char in text:
if char == 'e': if char == 'e':
result.append(math.e) result.append(math.e)
elif char == '𝜋': elif char == '𝜋':
result.append(math.pi) result.append(math.pi)
elif char.isdigit() or char == ".": elif char.isdigit() or char == ".":
buffer_number += char buffer_number += char
if len(buffer_function) == 0: if len(buffer_function) == 0:
number_first = True number_first = True
elif char.isalpha(): elif char.isalpha():
buffer_function += char buffer_function += char
else: else:
if number_first: if number_first:
result.append(float(buffer_number)) result.append(float(buffer_number))
buffer_number = "" buffer_number = ""
number_first = False number_first = False
if len(buffer_function) != 0: if len(buffer_function) != 0:
result.append('*') result.append('*')
result.append(buffer_function) result.append(buffer_function)
buffer_function = "" buffer_function = ""
elif char == '(': elif char == '(':
result.append('*') result.append('*')
else: else:
if len(buffer_function) != 0: if len(buffer_function) != 0:
result.append(buffer_function) result.append(buffer_function)
buffer_function = "" buffer_function = ""
if len(buffer_number) != 0: if len(buffer_number) != 0:
result.append(float(buffer_number)) result.append(float(buffer_number))
buffer_number = "" buffer_number = ""
number_first = False number_first = False
result.append(char) result.append(char)
if len(buffer_number) != 0: if len(buffer_number) != 0:
result.append(float(buffer_number)) result.append(float(buffer_number))
if len(buffer_function) != 0: if len(buffer_function) != 0:
result.append('*') result.append('*')
result.append(buffer_function) result.append(buffer_function)
elif len(buffer_function) != 0: elif len(buffer_function) != 0:
result.append(buffer_function) result.append(buffer_function)
return result return result
def npi2tree(expr: list) -> Expression: def npi2tree(expr: list) -> Expression:
""" Renvoie l'arbre formé à partir de l'expression donnée""" """ Renvoie l'arbre formé à partir de l'expression donnée"""
pile = Pile_chaine() pile = Pile_chaine()
for val in expr: for val in expr:
if not type(val) is float and val != "x": if not type(val) is float and val != "x":
# on inverse pour avoir les nombres dans le bon ordre # on inverse pour avoir les nombres dans le bon ordre
nombre2 = pile.depiler() nombre2 = pile.depiler()
if not pile.est_vide(): if not pile.est_vide():
pile.empiler(Expression(val, pile.depiler(), nombre2)) pile.empiler(Expression(val, pile.depiler(), nombre2))
else: else:
pile.empiler(Expression(val, nombre2)) pile.empiler(Expression(val, nombre2))
else: else:
pile.empiler(Expression(val)) pile.empiler(Expression(val))
return pile.sommet() return pile.sommet()
def inf2npi(expr: list) -> list: def inf2npi(expr: list) -> list:
"""Transforme une expression infixé en notation polonaise inversée""" """Transforme une expression infixé en notation polonaise inversée"""
operator_stack = Pile_chaine() operator_stack = Pile_chaine()
operator_priority = { operator_priority = {
'+': 1, '+': 1,
'-': 1, '-': 1,
'*': 2, '*': 2,
'^': 3, '^': 3,
'/': 2, '/': 2,
'(': 0, '(': 0,
')': 0 ')': 0
} }
output = [] output = []
for val in expr: for val in expr:
if type(val) is float or val == 'x': if type(val) is float or val == 'x':
output.append(val) output.append(val)
else: else:
if operator_stack.est_vide() or ( val == '(' or operator_priority[val] > operator_priority[operator_stack.sommet()]): if operator_stack.est_vide() or ( val == '(' or operator_priority[val] > operator_priority[operator_stack.sommet()]):
operator_stack.empiler(val) operator_stack.empiler(val)
else: else:
while not operator_stack.est_vide(): while not operator_stack.est_vide():
if operator_stack.sommet() == '(': if operator_stack.sommet() == '(':
operator_stack.depiler() operator_stack.depiler()
else: else:
output.append(operator_stack.depiler()) output.append(operator_stack.depiler())
if val != ')': if val != ')':
operator_stack.empiler(val) operator_stack.empiler(val)
while not operator_stack.est_vide(): while not operator_stack.est_vide():
output.append(operator_stack.depiler()) output.append(operator_stack.depiler())
return output return output
class App(tk.Tk): class App(tk.Tk):
"""Classe d'application graphique si python < 3.7""" """Classe d'application graphique si python < 3.7"""
def __init__(self) -> None: def __init__(self) -> None:
super().__init__() super().__init__()
self.fonction_screen_height = None self.fonction_screen_height = None
self.fonction_screen_width = None self.fonction_screen_width = None
# touches de la calculette # touches de la calculette
self.keys = [] self.keys = []
for i, val in enumerate(["", "cos", "sin", "tan", "", "x", "e", "", "^", "𝜋", 7, 8, 9, "(", ")", 4, 5, 6, "*", "/", 1, 2, 3, "+", "-", for i, val in enumerate(["", "cos", "sin", "tan", "", "x", "e", "", "^", "𝜋", 7, 8, 9, "(", ")", 4, 5, 6, "*", "/", 1, 2, 3, "+", "-",
"clear", 0, ".", "ln", "exe"]): # add numbers button "clear", 0, ".", "ln", "exe"]): # add numbers button
if type(val) == int: if type(val) == int:
self.keys.append(tk.Button(self, text=val, command=lambda x=val: self.add_value(x), background="blue", fg="gray90")) self.keys.append(tk.Button(self, text=val, command=lambda x=val: self.add_value(x), background="blue", fg="gray90"))
elif val == "clear": elif val == "clear":
self.keys.append(tk.Button(self, text=val, command=self.clear_screen)) self.keys.append(tk.Button(self, text=val, command=self.clear_screen))
elif val == "": elif val == "":
self.keys.append(tk.Button(self, text=val, command=self.move_cursor_left)) self.keys.append(tk.Button(self, text=val, command=self.move_cursor_left))
elif val == "": elif val == "":
self.keys.append(tk.Button(self, text=val, command=self.move_cursor_right)) self.keys.append(tk.Button(self, text=val, command=self.move_cursor_right))
else: else:
self.keys.append(tk.Button(self, text=val, command=lambda x=val: self.add_value(x))) self.keys.append(tk.Button(self, text=val, command=lambda x=val: self.add_value(x)))
self.keys[i].grid(row=i//5+1, column=i % 5+1, sticky="NSEW", padx=5, pady=5) self.keys[i].grid(row=i//5+1, column=i % 5+1, sticky="NSEW", padx=5, pady=5)
# Calcul Frame # Calcul Frame
self.calcul_frame = tk.Frame(self, bg="gray30") self.calcul_frame = tk.Frame(self, bg="gray30")
self.calcul_screen = tk.Label(self.calcul_frame, text="hello") self.calcul_screen = tk.Label(self.calcul_frame, text="hello")
self.calcul_screen.pack(fill="both", expand=True) self.calcul_screen.pack(fill="both", expand=True)
self.calcul_entry = tk.Entry(self.calcul_frame, width=50) self.calcul_entry = tk.Entry(self.calcul_frame, width=50)
self.calcul_entry.pack(fill="both", expand=True) self.calcul_entry.pack(fill="both", expand=True)
# Fonction Frame # Fonction Frame
self.fonction_frame = tk.Frame(self, bg="gray30") self.fonction_frame = tk.Frame(self, bg="gray30")
self.fonction_bornes_frame = tk.Frame(self.fonction_frame) self.fonction_bornes_frame = tk.Frame(self.fonction_frame)
self.fonction_bornes_entry = tk.Entry(self.fonction_bornes_frame) self.fonction_bornes_entry = tk.Entry(self.fonction_bornes_frame)
self.fonction_bornes_entry.insert(0, "-100,100") self.fonction_bornes_entry.insert(0, "-100,100")
self.fonction_bornes_entry.grid(sticky="ew", row=1, padx=10) self.fonction_bornes_entry.grid(sticky="ew", row=1, padx=10)
self.fonction_bornes_text = tk.Label(self.fonction_bornes_frame, text="Entrez les bornes de tracé: (min, max)") self.fonction_bornes_text = tk.Label(self.fonction_bornes_frame, text="Entrez les bornes de tracé: (min, max)")
self.fonction_bornes_text.grid(sticky="ew", row=0) self.fonction_bornes_text.grid(sticky="ew", row=0)
self.fonction_bornes_frame.grid(sticky="ns", column=0, rowspan=2) self.fonction_bornes_frame.grid(sticky="ns", column=0, rowspan=2)
self.fonction_screen = tk.Canvas(self.fonction_frame) self.fonction_screen = tk.Canvas(self.fonction_frame)
self.fonction_screen.grid(sticky="nsew", column=1, row=0) self.fonction_screen.grid(sticky="nsew", column=1, row=0)
self.fonction_entry = tk.Entry(self.fonction_frame) self.fonction_entry = tk.Entry(self.fonction_frame)
self.fonction_entry.grid(sticky='ew', column=1, row=1) self.fonction_entry.grid(sticky='ew', column=1, row=1)
# navigation menu # navigation menu
self.navigation_frame = tk.Frame(self, bg="gray50") self.navigation_frame = tk.Frame(self, bg="gray50")
self.navigation_frame.grid(row=0, rowspan=10, column=0, sticky="nsew") self.navigation_frame.grid(row=0, rowspan=10, column=0, sticky="nsew")
self.titre = tk.Label(self.navigation_frame, text="Wx Calculator", compound="left" self.titre = tk.Label(self.navigation_frame, text="Wx Calculator", compound="left"
, font=tk.font.Font(size=15, weight="bold")) , font=tk.font.Font(size=15, weight="bold"))
self.titre.grid(row=0, column=0, padx=20, pady=20) self.titre.grid(row=0, column=0, padx=20, pady=20)
self.home_button = tk.Button(self.navigation_frame, height=3 self.home_button = tk.Button(self.navigation_frame, height=3
, text="Calcul", bg="gray30", fg="gray10", anchor="w" , text="Calcul", bg="gray30", fg="gray10", anchor="w"
, command=lambda: self.select_frame_by_name("Calcul")) , command=lambda: self.select_frame_by_name("Calcul"))
self.home_button.grid(row=1, column=0, sticky="ew") self.home_button.grid(row=1, column=0, sticky="ew")
self.function_button = tk.Button(self.navigation_frame, height=3 self.function_button = tk.Button(self.navigation_frame, height=3
, text="Fonction", bg="gray30", fg="gray10", anchor="w" , text="Fonction", bg="gray30", fg="gray10", anchor="w"
, command=lambda: self.select_frame_by_name("Fonction")) , command=lambda: self.select_frame_by_name("Fonction"))
self.function_button.grid(row=2, column=0, sticky="ew") self.function_button.grid(row=2, column=0, sticky="ew")
self.mode = 'Calcul' self.mode = 'Calcul'
# select default frame # select default frame
self.select_frame_by_name("Calcul") self.select_frame_by_name("Calcul")
def select_frame_by_name(self, name): def select_frame_by_name(self, name):
"""Change de mode : passe de calcul à fonction""" """Change de mode : passe de calcul à fonction"""
# set button color for selected button # set button color for selected button
self.home_button.configure(bg="gray75" if name == "Calcul" else "gray30") self.home_button.configure(bg="gray75" if name == "Calcul" else "gray30")
self.function_button.configure(bg="gray75" if name == "Fonction" else "gray30") self.function_button.configure(bg="gray75" if name == "Fonction" else "gray30")
# show selected frame # show selected frame
if name == "Calcul": if name == "Calcul":
self.mode = "Calcul" self.mode = "Calcul"
self.calcul_frame.grid(padx=20, pady=20, columnspan=5, column=1, row=0, sticky="nsew") self.calcul_frame.grid(padx=20, pady=20, columnspan=5, column=1, row=0, sticky="nsew")
self.keys[-1].configure(command=self.calculate) self.keys[-1].configure(command=self.calculate)
else: else:
self.calcul_frame.grid_forget() self.calcul_frame.grid_forget()
if name == "Fonction": if name == "Fonction":
self.mode = "Fonction" self.mode = "Fonction"
self.fonction_frame.grid(columnspan=5, column=1, row=0, padx=20, pady=20, sticky="ns") self.fonction_frame.grid(columnspan=5, column=1, row=0, padx=20, pady=20, sticky="ns")
self.fonction_frame.update() self.fonction_frame.update()
self.fonction_screen_width = self.fonction_screen.winfo_width() self.fonction_screen_width = self.fonction_screen.winfo_width()
self.keys[-1].configure(command=self.draw_graph) self.keys[-1].configure(command=self.draw_graph)
self.fonction_screen_height = self.fonction_screen.winfo_height() self.fonction_screen_height = self.fonction_screen.winfo_height()
else: else:
self.fonction_frame.grid_forget() self.fonction_frame.grid_forget()
def add_value(self, value) -> None: def add_value(self, value) -> None:
"""Ajoute le charactère à la suite de l'expression""" """Ajoute le charactère à la suite de l'expression"""
parenthesis = False parenthesis = False
if value == "e": if value == "e":
value += "^" value += "^"
if value == "(": if value == "(":
value += ")" value += ")"
parenthesis = True parenthesis = True
elif value in ["ln", "cos", "sin", "tan", ""]: elif value in ["ln", "cos", "sin", "tan", ""]:
value += "()" value += "()"
parenthesis = True parenthesis = True
if self.mode == 'Fonction': if self.mode == 'Fonction':
self.fonction_entry.insert(tk.INSERT, value) self.fonction_entry.insert(tk.INSERT, value)
else: else:
self.calcul_entry.insert(tk.INSERT, value) self.calcul_entry.insert(tk.INSERT, value)
if parenthesis: if parenthesis:
self.move_cursor_left() self.move_cursor_left()
def clear_screen(self) -> None: def clear_screen(self) -> None:
"""Enlève l'affichage actuel""" """Enlève l'affichage actuel"""
if self.mode == "Calcul": if self.mode == "Calcul":
self.calcul_screen.configure(text="") self.calcul_screen.configure(text="")
self.calcul_entry.delete('0', 'end') self.calcul_entry.delete('0', 'end')
else: else:
self.fonction_screen.delete('all') self.fonction_screen.delete('all')
self.fonction_entry.delete(0, 'end') self.fonction_entry.delete(0, 'end')
def move_cursor_left(self): def move_cursor_left(self):
"""Déplace le curseur à gauche""" """Déplace le curseur à gauche"""
if self.mode == "Calcul": if self.mode == "Calcul":
pos = self.calcul_entry.index(tk.INSERT) pos = self.calcul_entry.index(tk.INSERT)
if pos > 0: if pos > 0:
self.calcul_entry.icursor(pos -1) self.calcul_entry.icursor(pos -1)
else: else:
pos = self.fonction_entry.index(tk.INSERT) pos = self.fonction_entry.index(tk.INSERT)
if pos > 0: if pos > 0:
self.fonction_entry.icursor(pos-1) self.fonction_entry.icursor(pos-1)
def move_cursor_right(self): def move_cursor_right(self):
"""Déplace le curseur à droite""" """Déplace le curseur à droite"""
if self.mode == "Calcul": if self.mode == "Calcul":
pos = self.calcul_entry.index(tk.INSERT) pos = self.calcul_entry.index(tk.INSERT)
if pos < len(self.calcul_entry.get()): if pos < len(self.calcul_entry.get()):
self.calcul_entry.icursor(pos + 1) self.calcul_entry.icursor(pos + 1)
else: else:
pos = self.fonction_entry.index(tk.INSERT) pos = self.fonction_entry.index(tk.INSERT)
if pos < len(self.fonction_entry.get()): if pos < len(self.fonction_entry.get()):
self.fonction_entry.icursor(pos + 1) self.fonction_entry.icursor(pos + 1)
def draw_framing(self, min_x, max_x, min_y, max_y): def draw_framing(self, min_x, max_x, min_y, max_y):
"""Dessine le cadrillage du graphique""" """Dessine le cadrillage du graphique"""
ratio = self.fonction_screen_height / self.fonction_screen_width # longueur par largeur du canvas ratio = self.fonction_screen_height / self.fonction_screen_width # longueur par largeur du canvas
# position du max dans l'interval # position du max dans l'interval
abscisse = max_y / (abs(min_y)+abs(max_y)) abscisse = max_y / (abs(min_y)+abs(max_y))
ordonnee = max_x / (abs(min_x)+abs(max_x)) ordonnee = max_x / (abs(min_x)+abs(max_x))
for i in range(20): # dessin des lignes verticales for i in range(20): # dessin des lignes verticales
if i == int(20 - 20*ordonnee): if i == int(20 - 20*ordonnee):
self.fonction_screen.create_line(0 + i * self.fonction_screen_width/20, 0 self.fonction_screen.create_line(0 + i * self.fonction_screen_width/20, 0
, 0 + i * self.fonction_screen_width/20, self.fonction_screen_height , 0 + i * self.fonction_screen_width/20, self.fonction_screen_height
, fill='red', width=4) , fill='red', width=4)
text = self.fonction_screen.create_text(0 + i * self.fonction_screen_width/20, 10, text=max_y) text = self.fonction_screen.create_text(0 + i * self.fonction_screen_width/20, 10, text=max_y)
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white") rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white")
self.fonction_screen.tag_lower(rect, text) self.fonction_screen.tag_lower(rect, text)
text = self.fonction_screen.create_text(0 + i * self.fonction_screen_width/20, text = self.fonction_screen.create_text(0 + i * self.fonction_screen_width/20,
self.fonction_screen_height - 10, text=min_y) self.fonction_screen_height - 10, text=min_y)
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white") rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white")
self.fonction_screen.tag_lower(rect, text) self.fonction_screen.tag_lower(rect, text)
else: else:
self.fonction_screen.create_line(0 + i * self.fonction_screen_width/20, 0 self.fonction_screen.create_line(0 + i * self.fonction_screen_width/20, 0
, 0 + i * self.fonction_screen_width/20, self.fonction_screen_height , 0 + i * self.fonction_screen_width/20, self.fonction_screen_height
, fill='red') , fill='red')
nb_abscisse_lines = int(20*ratio) # nombre de lignes horizontales à tracer nb_abscisse_lines = int(20*ratio) # nombre de lignes horizontales à tracer
for i in range(nb_abscisse_lines): # dessin des lignes horizontales for i in range(nb_abscisse_lines): # dessin des lignes horizontales
if i == math.ceil(nb_abscisse_lines * abscisse): # tracer de l'axe des abscisses if i == math.ceil(nb_abscisse_lines * abscisse): # tracer de l'axe des abscisses
self.fonction_screen.create_line(0, 0 + i * self.fonction_screen_width/20 self.fonction_screen.create_line(0, 0 + i * self.fonction_screen_width/20
, self.fonction_screen_width , self.fonction_screen_width
, 0 + i * self.fonction_screen_width/20, fill='red' , 0 + i * self.fonction_screen_width/20, fill='red'
, width=4) , width=4)
text = self.fonction_screen.create_text(15, 0 + i * self.fonction_screen_width/20 text = self.fonction_screen.create_text(15, 0 + i * self.fonction_screen_width/20
, text=min_x) , text=min_x)
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text) rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text)
, fill="white") , fill="white")
self.fonction_screen.tag_lower(rect, text) self.fonction_screen.tag_lower(rect, text)
text = self.fonction_screen.create_text(self.fonction_screen_width - 15 text = self.fonction_screen.create_text(self.fonction_screen_width - 15
, 0 + i * self.fonction_screen_width/20 , 0 + i * self.fonction_screen_width/20
, text=max_x) , text=max_x)
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white") rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white")
self.fonction_screen.tag_lower(rect, text) self.fonction_screen.tag_lower(rect, text)
else: else:
self.fonction_screen.create_line(0, 0 + i * self.fonction_screen_width/20 self.fonction_screen.create_line(0, 0 + i * self.fonction_screen_width/20
, self.fonction_screen_width , self.fonction_screen_width
, 0 + i * self.fonction_screen_width/20, fill='red') , 0 + i * self.fonction_screen_width/20, fill='red')
def calculate(self) -> None: def calculate(self) -> None:
"""Calcule dans la partie graphique""" """Calcule dans la partie graphique"""
exp = parse_string_to_list(self.calcul_entry.get()) exp = parse_string_to_list(self.calcul_entry.get())
exp = npi2tree(inf2npi(exp)) exp = npi2tree(inf2npi(exp))
exp.print_tree() exp.print_tree()
try: try:
result = exp.evalue() result = exp.evalue()
except ZeroDivisionError: except ZeroDivisionError:
result = "ERREUR" result = "ERREUR"
self.calcul_screen.configure(text=result) self.calcul_screen.configure(text=result)
def draw_graph(self): def draw_graph(self):
"""Dessine les points du graphique""" """Dessine les points du graphique"""
self.fonction_screen.delete('all') self.fonction_screen.delete('all')
min_x, max_x = map(int, self.fonction_bornes_entry.get().split(',')) min_x, max_x = map(int, self.fonction_bornes_entry.get().split(','))
fonction_points = npi2tree(inf2npi(parse_string_to_list(self.fonction_entry.get()))).valeurs_de_fonction(min_x, max_x) fonction_points = npi2tree(inf2npi(parse_string_to_list(self.fonction_entry.get()))).valeurs_de_fonction(min_x, max_x)
max_y = max(fonction_points, key=lambda item: item[1])[1] max_y = max(fonction_points, key=lambda item: item[1])[1]
min_y = min(fonction_points, key=lambda item: item[1])[1] min_y = min(fonction_points, key=lambda item: item[1])[1]
self.draw_framing(min_x, max_x, min_y, max_y) self.draw_framing(min_x, max_x, min_y, max_y)
for i, (x, y) in enumerate(fonction_points): ratio = (fonction_points[0][0] - min_x) / (abs(max_x)+abs(min_x))
image_x = i * (self.fonction_screen_width/len(fonction_points)) i = len(fonction_points) * ratio
image_y = self.fonction_screen_height - (y - min_y) * self.fonction_screen_height / (abs(max_y)+abs(min_y)) for (x, y) in fonction_points:
self.fonction_screen.create_rectangle(image_x, image_y, image_x, image_y) image_x = i * (self.fonction_screen_width/len(fonction_points))
image_y = self.fonction_screen_height - (y - min_y) * self.fonction_screen_height / (abs(max_y)+abs(min_y))
self.fonction_screen.create_rectangle(image_x, image_y, image_x, image_y)
i += 1
Loading…
Cancel
Save