BARRAUX Arthur
2 years ago
4 changed files with 650 additions and 416 deletions
@ -1,418 +1,9 @@ |
|||
import math |
|||
import customtkinter as ctk |
|||
from data_structure import Pile_chaine |
|||
|
|||
|
|||
class Expression: |
|||
"""manipule les expression sous forme d'arbre""" |
|||
def __init__(self, val, fils_gauche=None, fils_droit=None) -> None: |
|||
self.val = val |
|||
self.fils_gauche = fils_gauche |
|||
self.fils_droit = fils_droit |
|||
|
|||
def __str__(self) -> str: |
|||
"""renvoie l'expression sous forme infixé""" |
|||
if self.est_feuille(): |
|||
return str(self.val) |
|||
return '('+self.fils_gauche.__str__()+str(self.val)+self.fils_droit.__str__()+')' |
|||
|
|||
def print_tree(self, level=0): |
|||
""" Affiche le noeud joliment""" |
|||
if self.fils_gauche is not None: |
|||
self.fils_gauche.print_tree(level + 1) |
|||
print(' ' * 4 * level + '-> ' + str(self.val)) |
|||
if self.fils_droit is not None: |
|||
self.fils_droit.print_tree(level + 1) |
|||
|
|||
def est_feuille(self) -> bool: |
|||
""" Renvoie true si le noeud est une feuille""" |
|||
if self.fils_droit is None and self.fils_gauche is None: |
|||
return True |
|||
return False |
|||
|
|||
def evalue(self, x=0) -> float: |
|||
""" Renvoie le résultat de l'expression""" |
|||
try: |
|||
if self.est_feuille(): |
|||
if self.val == 'x': |
|||
return x |
|||
return float(self.val) |
|||
elif self.val == '+': |
|||
return self.fils_gauche.evalue(x) + self.fils_droit.evalue(x) |
|||
elif self.val == '*': |
|||
return self.fils_gauche.evalue(x) * self.fils_droit.evalue(x) |
|||
elif self.val == '/': |
|||
return self.fils_gauche.evalue(x) / self.fils_droit.evalue(x) |
|||
elif self.val == '^': |
|||
return self.fils_gauche.evalue(x) ** self.fils_droit.evalue(x) |
|||
elif self.val == '-': |
|||
return self.fils_gauche.evalue(x) - self.fils_droit.evalue(x) |
|||
elif self.val == 'ln': |
|||
return math.log(self.fils_gauche.evalue(x)) |
|||
elif self.val == '√': |
|||
return math.sqrt(self.fils_gauche.evalue(x)) |
|||
elif self.val == "cos": |
|||
return math.cos(self.fils_gauche.evalue(x)) |
|||
elif self.val == "sin": |
|||
return math.sin(self.fils_gauche.evalue(x)) |
|||
elif self.val == "tan": |
|||
return math.tan(self.fils_gauche.evalue(x)) |
|||
|
|||
except ZeroDivisionError: |
|||
raise ZeroDivisionError |
|||
|
|||
def valeurs_de_fonction(self, start, end): |
|||
""" Calcul les valeurs entre start et end""" |
|||
result = [] |
|||
pas = (end - start) / 1000 |
|||
while start <= end: |
|||
try: |
|||
result.append((start, self.evalue(start))) |
|||
except: |
|||
pass |
|||
start += pas |
|||
return result |
|||
|
|||
|
|||
def change_appearance_mode_event(new_appearance_mode: str) -> None: |
|||
"""Change le thème de sombre à Claire""" |
|||
ctk.set_appearance_mode(new_appearance_mode) |
|||
|
|||
|
|||
class App(ctk.CTk): |
|||
"""Classe pour l'interface graphique""" |
|||
def __init__(self) -> None: |
|||
super().__init__() |
|||
|
|||
change_appearance_mode_event('dark') |
|||
|
|||
self.fonction_screen_height = None |
|||
self.fonction_screen_width = None |
|||
self.grid_columnconfigure(0, weight=0) |
|||
self.grid_columnconfigure((1,2,3,4,5), weight=1) |
|||
self.grid_rowconfigure(0, weight=0) |
|||
self.grid_rowconfigure((1,2,3,4,5), weight=1) |
|||
|
|||
# touches de la calculette |
|||
|
|||
self.keys = [] |
|||
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 |
|||
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"))) |
|||
elif val == "clear": |
|||
self.keys.append(ctk.CTkButton(self, text=val, command=self.clear_screen)) |
|||
elif val == "←": |
|||
self.keys.append(ctk.CTkButton(self, text=val, command=self.move_cursor_left)) |
|||
elif val == "→": |
|||
self.keys.append(ctk.CTkButton(self, text=val, command=self.move_cursor_right)) |
|||
else: |
|||
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) |
|||
|
|||
# Calcul Frame |
|||
|
|||
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.pack(fill="both", expand=True) |
|||
self.calcul_entry = ctk.CTkEntry(self.calcul_frame) |
|||
self.calcul_entry.pack(fill="both", expand=True) |
|||
|
|||
# Fonction Frame |
|||
|
|||
self.fonction_frame = ctk.CTkFrame(self, fg_color="transparent") |
|||
|
|||
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.insert(0, "-100,100") |
|||
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.grid(sticky="ew", row=0) |
|||
|
|||
self.fonction_bornes_frame.grid(sticky="ns", column=0, rowspan=2) |
|||
|
|||
|
|||
self.fonction_screen = ctk.CTkCanvas(self.fonction_frame) |
|||
self.fonction_screen.grid(sticky="nsew", column=1, row=0) |
|||
self.fonction_entry = ctk.CTkEntry(self.fonction_frame) |
|||
self.fonction_entry.grid(sticky='ew', column=1, row=1) |
|||
|
|||
# navigation menu |
|||
self.navigation_frame = ctk.CTkFrame(self, corner_radius=0) |
|||
self.navigation_frame.grid_rowconfigure(4, weight=1) |
|||
self.navigation_frame.grid(row=0, rowspan=10, column=0, sticky="nsew") |
|||
|
|||
self.titre = ctk.CTkLabel(self.navigation_frame, text="Wx Calculator", compound="left" |
|||
, font=ctk.CTkFont(size=15, weight="bold")) |
|||
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 |
|||
, text="Calcul", fg_color="transparent", text_color=("gray10", "gray90") |
|||
, hover_color=("gray70", "gray30"), anchor="w" |
|||
, command=lambda: self.select_frame_by_name("Calcul")) |
|||
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 |
|||
, text="Fonction", fg_color="transparent", text_color=("gray10", "gray90") |
|||
, hover_color=("gray70", "gray30"), anchor="w" |
|||
, command=lambda: self.select_frame_by_name("Fonction")) |
|||
self.function_button.grid(row=2, column=0, sticky="ew") |
|||
|
|||
self.appearance_mode_menu = ctk.CTkOptionMenu(self.navigation_frame, values=["Light", "Dark", "System"] |
|||
, command=change_appearance_mode_event) |
|||
self.appearance_mode_menu.grid(row=4, column=0, padx=20, pady=20, sticky="s") |
|||
self.mode = 'Calcul' |
|||
|
|||
# select default frame |
|||
|
|||
self.select_frame_by_name("Calcul") |
|||
|
|||
def select_frame_by_name(self, name): |
|||
"""Change de mode : passe de calcul à fonction""" |
|||
# set button color for selected button |
|||
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") |
|||
|
|||
# show selected frame |
|||
if name == "Calcul": |
|||
self.mode = "Calcul" |
|||
self.calcul_frame.grid(padx=20, pady=20, columnspan=5, column=1, row=0, sticky="nsew") |
|||
self.keys[-1]._command = self.calculate |
|||
|
|||
else: |
|||
self.calcul_frame.grid_forget() |
|||
if name == "Fonction": |
|||
self.mode = "Fonction" |
|||
self.fonction_frame.grid(columnspan=5, column=1, row=0, padx=20, pady=20, sticky="ns") |
|||
self.fonction_frame.update() |
|||
self.fonction_screen_width = self.fonction_screen.winfo_width() |
|||
self.keys[-1]._command = self.draw_graph |
|||
self.fonction_screen_height = self.fonction_screen.winfo_height() |
|||
else: |
|||
self.fonction_frame.grid_forget() |
|||
|
|||
def add_value(self, value) -> None: |
|||
"""Ajoute le charactère à la suite de l'expression""" |
|||
parenthesis = False |
|||
if value == "e": |
|||
value += "^" |
|||
if value == "(": |
|||
value += ")" |
|||
parenthesis = True |
|||
elif value in ["ln", "cos", "sin", "tan", "√"]: |
|||
value += "()" |
|||
parenthesis = True |
|||
if self.mode == 'Fonction': |
|||
self.fonction_entry.insert(ctk.INSERT, value) |
|||
else: |
|||
self.calcul_entry.insert(ctk.INSERT, value) |
|||
if parenthesis: |
|||
self.move_cursor_left() |
|||
|
|||
def clear_screen(self) -> None: |
|||
"""Enlève l'affichage actuel""" |
|||
if self.mode == "Calcul": |
|||
self.calcul_screen.configure(text="") |
|||
self.calcul_entry.delete('0', 'end') |
|||
else: |
|||
self.fonction_screen.delete('all') |
|||
self.fonction_entry.delete(0, 'end') |
|||
|
|||
def move_cursor_left(self): |
|||
"""Déplace le curseur à gauche""" |
|||
if self.mode == "Calcul": |
|||
pos = self.calcul_entry.index(ctk.INSERT) |
|||
if pos > 0: |
|||
self.calcul_entry.icursor(pos -1) |
|||
else: |
|||
pos = self.fonction_entry.index(ctk.INSERT) |
|||
if pos > 0: |
|||
self.fonction_entry.icursor(pos-1) |
|||
|
|||
def move_cursor_right(self): |
|||
"""Déplace le curseur à droite""" |
|||
if self.mode == "Calcul": |
|||
pos = self.calcul_entry.index(ctk.INSERT) |
|||
if pos < len(self.calcul_entry.get()): |
|||
self.calcul_entry.icursor(pos + 1) |
|||
else: |
|||
pos = self.fonction_entry.index(ctk.INSERT) |
|||
if pos < len(self.fonction_entry.get()): |
|||
self.fonction_entry.icursor(pos + 1) |
|||
|
|||
def draw_framing(self, min_x, max_x, min_y, max_y): |
|||
"""Dessine le cadrillage du graphique""" |
|||
ratio = self.fonction_screen_height / self.fonction_screen_width # longueur par largeur du canvas |
|||
|
|||
# position du max dans l'interval |
|||
abscisse = max_y / (abs(min_y)+abs(max_y)) |
|||
ordonnee = max_x / (abs(min_x)+abs(max_x)) |
|||
|
|||
for i in range(20): # dessin des lignes verticales |
|||
if i == int(20 - 20*ordonnee): |
|||
self.fonction_screen.create_line(0 + i * self.fonction_screen_width/20, 0 |
|||
, 0 + i * self.fonction_screen_width/20, self.fonction_screen_height |
|||
, fill='red', width=4) |
|||
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") |
|||
self.fonction_screen.tag_lower(rect, text) |
|||
|
|||
text = self.fonction_screen.create_text(0 + i * self.fonction_screen_width/20, |
|||
self.fonction_screen_height - 10, text=min_y) |
|||
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white") |
|||
self.fonction_screen.tag_lower(rect, text) |
|||
else: |
|||
self.fonction_screen.create_line(0 + i * self.fonction_screen_width/20, 0 |
|||
, 0 + i * self.fonction_screen_width/20, self.fonction_screen_height |
|||
, fill='red') |
|||
nb_abscisse_lines = int(20*ratio) # nombre de lignes horizontales à tracer |
|||
|
|||
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 |
|||
self.fonction_screen.create_line(0, 0 + i * self.fonction_screen_width/20 |
|||
, self.fonction_screen_width |
|||
, 0 + i * self.fonction_screen_width/20, fill='red' |
|||
, width=4) |
|||
text = self.fonction_screen.create_text(15, 0 + i * self.fonction_screen_width/20 |
|||
, text=min_x) |
|||
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text) |
|||
, fill="white") |
|||
self.fonction_screen.tag_lower(rect, text) |
|||
|
|||
text = self.fonction_screen.create_text(self.fonction_screen_width - 15 |
|||
, 0 + i * self.fonction_screen_width/20 |
|||
, text=max_x) |
|||
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white") |
|||
self.fonction_screen.tag_lower(rect, text) |
|||
else: |
|||
self.fonction_screen.create_line(0, 0 + i * self.fonction_screen_width/20 |
|||
, self.fonction_screen_width |
|||
, 0 + i * self.fonction_screen_width/20, fill='red') |
|||
|
|||
def calculate(self) -> None: |
|||
"""Calcule dans la partie graphique""" |
|||
exp = parse_string_to_list(self.calcul_entry.get()) |
|||
exp = npi2tree(inf2npi(exp)) |
|||
exp.print_tree() |
|||
# test si custom tkinter fonction sur le pc |
|||
try: |
|||
result = exp.evalue() |
|||
except ZeroDivisionError: |
|||
result = "ERREUR" |
|||
self.calcul_screen.configure(text=result) |
|||
|
|||
def draw_graph(self): |
|||
"""Dessine les points du graphique""" |
|||
self.fonction_screen.delete('all') |
|||
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) |
|||
max_y = max(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) |
|||
for i, (x, y) in enumerate(fonction_points): |
|||
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) |
|||
|
|||
|
|||
def parse_string_to_list(text: str) -> list: |
|||
""" Transforme le text en liste d'éléments intelligible par le programme """ |
|||
text = list(text) |
|||
result = [] |
|||
buffer_function = "" |
|||
buffer_number = "" |
|||
number_first = False # Savoir dans quel ordre sont les chiffres et les lettres |
|||
for char in text: |
|||
if char == 'e': |
|||
result.append(math.e) |
|||
elif char == '𝜋': |
|||
result.append(math.pi) |
|||
elif char.isdigit() or char == ".": |
|||
buffer_number += char |
|||
if len(buffer_function) == 0: |
|||
number_first = True |
|||
elif char.isalpha(): |
|||
buffer_function += char |
|||
else: |
|||
if number_first: |
|||
result.append(float(buffer_number)) |
|||
buffer_number = "" |
|||
number_first = False |
|||
if len(buffer_function) != 0: |
|||
result.append('*') |
|||
result.append(buffer_function) |
|||
buffer_function = "" |
|||
elif char == '(': |
|||
result.append('*') |
|||
|
|||
else: |
|||
if len(buffer_function) != 0: |
|||
result.append(buffer_function) |
|||
buffer_function = "" |
|||
if len(buffer_number) != 0: |
|||
result.append(float(buffer_number)) |
|||
buffer_number = "" |
|||
number_first = False |
|||
result.append(char) |
|||
if len(buffer_number) != 0: |
|||
result.append(float(buffer_number)) |
|||
if len(buffer_function) != 0: |
|||
result.append('*') |
|||
result.append(buffer_function) |
|||
elif len(buffer_function) != 0: |
|||
result.append(buffer_function) |
|||
return result |
|||
|
|||
def npi2tree(expr: list) -> Expression: |
|||
""" Renvoie l'arbre formé à partir de l'expression donnée""" |
|||
pile = Pile_chaine() |
|||
for val in expr: |
|||
if not type(val) is float and val != "x": |
|||
# on inverse pour avoir les nombres dans le bon ordre |
|||
nombre2 = pile.depiler() |
|||
if not pile.est_vide(): |
|||
pile.empiler(Expression(val, pile.depiler(), nombre2)) |
|||
else: |
|||
pile.empiler(Expression(val, nombre2)) |
|||
else: |
|||
pile.empiler(Expression(val)) |
|||
return pile.sommet() |
|||
|
|||
|
|||
def inf2npi(expr: list) -> list: |
|||
"""Transforme une expression infixé en notation polonaise inversée""" |
|||
operator_stack = Pile_chaine() |
|||
operator_priority = { |
|||
'+': 1, |
|||
'-': 1, |
|||
'*': 2, |
|||
'^': 3, |
|||
'/': 2, |
|||
'(': 0, |
|||
')': 0 |
|||
} |
|||
output = [] |
|||
for val in expr: |
|||
if type(val) is float or val == 'x': |
|||
output.append(val) |
|||
else: |
|||
if operator_stack.est_vide() or ( val == '(' or operator_priority[val] > operator_priority[operator_stack.sommet()]): |
|||
operator_stack.empiler(val) |
|||
else: |
|||
while not operator_stack.est_vide(): |
|||
if operator_stack.sommet() == '(': |
|||
operator_stack.depiler() |
|||
else: |
|||
output.append(operator_stack.depiler()) |
|||
if val != ')': |
|||
operator_stack.empiler(val) |
|||
while not operator_stack.est_vide(): |
|||
output.append(operator_stack.depiler()) |
|||
return output |
|||
|
|||
from package.app import Ctk_app |
|||
gui = Ctk_app() |
|||
except ImportError: |
|||
from package.app import Tk_app |
|||
gui = Tk_app() |
|||
|
|||
# [3, '-', 6, '*', 4, '+', 3] |
|||
gui = App() |
|||
gui.mainloop() |
|||
|
@ -0,0 +1,570 @@ |
|||
"""Classe d'application tk et ctk""" |
|||
|
|||
import math |
|||
import customtkinter as ctk |
|||
import tkinter as tk |
|||
from package.data_structure import Pile_chaine |
|||
from package.expression import Expression |
|||
|
|||
|
|||
def change_appearance_mode_event(new_appearance_mode: str) -> None: |
|||
"""Change le thème de sombre à Claire""" |
|||
ctk.set_appearance_mode(new_appearance_mode) |
|||
|
|||
def parse_string_to_list(text: str) -> list: |
|||
""" Transforme le text en liste d'éléments intelligible par le programme """ |
|||
text = list(text) |
|||
result = [] |
|||
buffer_function = "" |
|||
buffer_number = "" |
|||
number_first = False # Savoir dans quel ordre sont les chiffres et les lettres |
|||
for char in text: |
|||
if char == 'e': |
|||
result.append(math.e) |
|||
elif char == '𝜋': |
|||
result.append(math.pi) |
|||
elif char.isdigit() or char == ".": |
|||
buffer_number += char |
|||
if len(buffer_function) == 0: |
|||
number_first = True |
|||
elif char.isalpha(): |
|||
buffer_function += char |
|||
else: |
|||
if number_first: |
|||
result.append(float(buffer_number)) |
|||
buffer_number = "" |
|||
number_first = False |
|||
if len(buffer_function) != 0: |
|||
result.append('*') |
|||
result.append(buffer_function) |
|||
buffer_function = "" |
|||
elif char == '(': |
|||
result.append('*') |
|||
|
|||
else: |
|||
if len(buffer_function) != 0: |
|||
result.append(buffer_function) |
|||
buffer_function = "" |
|||
if len(buffer_number) != 0: |
|||
result.append(float(buffer_number)) |
|||
buffer_number = "" |
|||
number_first = False |
|||
result.append(char) |
|||
if len(buffer_number) != 0: |
|||
result.append(float(buffer_number)) |
|||
if len(buffer_function) != 0: |
|||
result.append('*') |
|||
result.append(buffer_function) |
|||
elif len(buffer_function) != 0: |
|||
result.append(buffer_function) |
|||
return result |
|||
|
|||
def npi2tree(expr: list) -> Expression: |
|||
""" Renvoie l'arbre formé à partir de l'expression donnée""" |
|||
pile = Pile_chaine() |
|||
for val in expr: |
|||
if not type(val) is float and val != "x": |
|||
# on inverse pour avoir les nombres dans le bon ordre |
|||
nombre2 = pile.depiler() |
|||
if not pile.est_vide(): |
|||
pile.empiler(Expression(val, pile.depiler(), nombre2)) |
|||
else: |
|||
pile.empiler(Expression(val, nombre2)) |
|||
else: |
|||
pile.empiler(Expression(val)) |
|||
return pile.sommet() |
|||
|
|||
def inf2npi(expr: list) -> list: |
|||
"""Transforme une expression infixé en notation polonaise inversée""" |
|||
operator_stack = Pile_chaine() |
|||
operator_priority = { |
|||
'+': 1, |
|||
'-': 1, |
|||
'*': 2, |
|||
'^': 3, |
|||
'/': 2, |
|||
'(': 0, |
|||
')': 0 |
|||
} |
|||
output = [] |
|||
for val in expr: |
|||
if type(val) is float or val == 'x': |
|||
output.append(val) |
|||
else: |
|||
if operator_stack.est_vide() or ( val == '(' or operator_priority[val] > operator_priority[operator_stack.sommet()]): |
|||
operator_stack.empiler(val) |
|||
else: |
|||
while not operator_stack.est_vide(): |
|||
if operator_stack.sommet() == '(': |
|||
operator_stack.depiler() |
|||
else: |
|||
output.append(operator_stack.depiler()) |
|||
if val != ')': |
|||
operator_stack.empiler(val) |
|||
while not operator_stack.est_vide(): |
|||
output.append(operator_stack.depiler()) |
|||
return output |
|||
|
|||
|
|||
class Ctk_app(ctk.CTk): |
|||
"""Classe pour l'interface graphique avec custom tkinter""" |
|||
def __init__(self) -> None: |
|||
super().__init__() |
|||
|
|||
change_appearance_mode_event('dark') |
|||
|
|||
self.fonction_screen_height = None |
|||
self.fonction_screen_width = None |
|||
self.grid_columnconfigure(0, weight=0) |
|||
self.grid_columnconfigure((1,2,3,4,5), weight=1) |
|||
self.grid_rowconfigure(0, weight=0) |
|||
self.grid_rowconfigure((1,2,3,4,5), weight=1) |
|||
|
|||
# touches de la calculette |
|||
|
|||
self.keys = [] |
|||
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 |
|||
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"))) |
|||
elif val == "clear": |
|||
self.keys.append(ctk.CTkButton(self, text=val, command=self.clear_screen)) |
|||
elif val == "←": |
|||
self.keys.append(ctk.CTkButton(self, text=val, command=self.move_cursor_left)) |
|||
elif val == "→": |
|||
self.keys.append(ctk.CTkButton(self, text=val, command=self.move_cursor_right)) |
|||
else: |
|||
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) |
|||
|
|||
# Calcul Frame |
|||
|
|||
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.pack(fill="both", expand=True) |
|||
self.calcul_entry = ctk.CTkEntry(self.calcul_frame) |
|||
self.calcul_entry.pack(fill="both", expand=True) |
|||
|
|||
# Fonction Frame |
|||
|
|||
self.fonction_frame = ctk.CTkFrame(self, fg_color="transparent") |
|||
|
|||
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.insert(0, "-100,100") |
|||
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.grid(sticky="ew", row=0) |
|||
|
|||
self.fonction_bornes_frame.grid(sticky="ns", column=0, rowspan=2) |
|||
|
|||
|
|||
self.fonction_screen = ctk.CTkCanvas(self.fonction_frame) |
|||
self.fonction_screen.grid(sticky="nsew", column=1, row=0) |
|||
self.fonction_entry = ctk.CTkEntry(self.fonction_frame) |
|||
self.fonction_entry.grid(sticky='ew', column=1, row=1) |
|||
|
|||
# navigation menu |
|||
self.navigation_frame = ctk.CTkFrame(self, corner_radius=0) |
|||
self.navigation_frame.grid_rowconfigure(4, weight=1) |
|||
self.navigation_frame.grid(row=0, rowspan=10, column=0, sticky="nsew") |
|||
|
|||
self.titre = ctk.CTkLabel(self.navigation_frame, text="Wx Calculator", compound="left" |
|||
, font=ctk.CTkFont(size=15, weight="bold")) |
|||
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 |
|||
, text="Calcul", fg_color="transparent", text_color=("gray10", "gray90") |
|||
, hover_color=("gray70", "gray30"), anchor="w" |
|||
, command=lambda: self.select_frame_by_name("Calcul")) |
|||
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 |
|||
, text="Fonction", fg_color="transparent", text_color=("gray10", "gray90") |
|||
, hover_color=("gray70", "gray30"), anchor="w" |
|||
, command=lambda: self.select_frame_by_name("Fonction")) |
|||
self.function_button.grid(row=2, column=0, sticky="ew") |
|||
|
|||
self.appearance_mode_menu = ctk.CTkOptionMenu(self.navigation_frame, values=["Light", "Dark", "System"] |
|||
, command=change_appearance_mode_event) |
|||
self.appearance_mode_menu.grid(row=4, column=0, padx=20, pady=20, sticky="s") |
|||
self.mode = 'Calcul' |
|||
|
|||
# select default frame |
|||
|
|||
self.select_frame_by_name("Calcul") |
|||
|
|||
def select_frame_by_name(self, name): |
|||
"""Change de mode : passe de calcul à fonction""" |
|||
# set button color for selected button |
|||
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") |
|||
|
|||
# show selected frame |
|||
if name == "Calcul": |
|||
self.mode = "Calcul" |
|||
self.calcul_frame.grid(padx=20, pady=20, columnspan=5, column=1, row=0, sticky="nsew") |
|||
self.keys[-1]._command = self.calculate |
|||
|
|||
else: |
|||
self.calcul_frame.grid_forget() |
|||
if name == "Fonction": |
|||
self.mode = "Fonction" |
|||
self.fonction_frame.grid(columnspan=5, column=1, row=0, padx=20, pady=20, sticky="ns") |
|||
self.fonction_frame.update() |
|||
self.fonction_screen_width = self.fonction_screen.winfo_width() |
|||
self.keys[-1]._command = self.draw_graph |
|||
self.fonction_screen_height = self.fonction_screen.winfo_height() |
|||
else: |
|||
self.fonction_frame.grid_forget() |
|||
|
|||
def add_value(self, value) -> None: |
|||
"""Ajoute le charactère à la suite de l'expression""" |
|||
parenthesis = False |
|||
if value == "e": |
|||
value += "^" |
|||
if value == "(": |
|||
value += ")" |
|||
parenthesis = True |
|||
elif value in ["ln", "cos", "sin", "tan", "√"]: |
|||
value += "()" |
|||
parenthesis = True |
|||
if self.mode == 'Fonction': |
|||
self.fonction_entry.insert(ctk.INSERT, value) |
|||
else: |
|||
self.calcul_entry.insert(ctk.INSERT, value) |
|||
if parenthesis: |
|||
self.move_cursor_left() |
|||
|
|||
def clear_screen(self) -> None: |
|||
"""Enlève l'affichage actuel""" |
|||
if self.mode == "Calcul": |
|||
self.calcul_screen.configure(text="") |
|||
self.calcul_entry.delete('0', 'end') |
|||
else: |
|||
self.fonction_screen.delete('all') |
|||
self.fonction_entry.delete(0, 'end') |
|||
|
|||
def move_cursor_left(self): |
|||
"""Déplace le curseur à gauche""" |
|||
if self.mode == "Calcul": |
|||
pos = self.calcul_entry.index(ctk.INSERT) |
|||
if pos > 0: |
|||
self.calcul_entry.icursor(pos -1) |
|||
else: |
|||
pos = self.fonction_entry.index(ctk.INSERT) |
|||
if pos > 0: |
|||
self.fonction_entry.icursor(pos-1) |
|||
|
|||
def move_cursor_right(self): |
|||
"""Déplace le curseur à droite""" |
|||
if self.mode == "Calcul": |
|||
pos = self.calcul_entry.index(ctk.INSERT) |
|||
if pos < len(self.calcul_entry.get()): |
|||
self.calcul_entry.icursor(pos + 1) |
|||
else: |
|||
pos = self.fonction_entry.index(ctk.INSERT) |
|||
if pos < len(self.fonction_entry.get()): |
|||
self.fonction_entry.icursor(pos + 1) |
|||
|
|||
def draw_framing(self, min_x, max_x, min_y, max_y): |
|||
"""Dessine le cadrillage du graphique""" |
|||
ratio = self.fonction_screen_height / self.fonction_screen_width # longueur par largeur du canvas |
|||
|
|||
# position du max dans l'interval |
|||
abscisse = max_y / (abs(min_y)+abs(max_y)) |
|||
ordonnee = max_x / (abs(min_x)+abs(max_x)) |
|||
|
|||
for i in range(20): # dessin des lignes verticales |
|||
if i == int(20 - 20*ordonnee): |
|||
self.fonction_screen.create_line(0 + i * self.fonction_screen_width/20, 0 |
|||
, 0 + i * self.fonction_screen_width/20, self.fonction_screen_height |
|||
, fill='red', width=4) |
|||
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") |
|||
self.fonction_screen.tag_lower(rect, text) |
|||
|
|||
text = self.fonction_screen.create_text(0 + i * self.fonction_screen_width/20, |
|||
self.fonction_screen_height - 10, text=min_y) |
|||
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white") |
|||
self.fonction_screen.tag_lower(rect, text) |
|||
else: |
|||
self.fonction_screen.create_line(0 + i * self.fonction_screen_width/20, 0 |
|||
, 0 + i * self.fonction_screen_width/20, self.fonction_screen_height |
|||
, fill='red') |
|||
nb_abscisse_lines = int(20*ratio) # nombre de lignes horizontales à tracer |
|||
|
|||
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 |
|||
self.fonction_screen.create_line(0, 0 + i * self.fonction_screen_width/20 |
|||
, self.fonction_screen_width |
|||
, 0 + i * self.fonction_screen_width/20, fill='red' |
|||
, width=4) |
|||
text = self.fonction_screen.create_text(15, 0 + i * self.fonction_screen_width/20 |
|||
, text=min_x) |
|||
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text) |
|||
, fill="white") |
|||
self.fonction_screen.tag_lower(rect, text) |
|||
|
|||
text = self.fonction_screen.create_text(self.fonction_screen_width - 15 |
|||
, 0 + i * self.fonction_screen_width/20 |
|||
, text=max_x) |
|||
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white") |
|||
self.fonction_screen.tag_lower(rect, text) |
|||
else: |
|||
self.fonction_screen.create_line(0, 0 + i * self.fonction_screen_width/20 |
|||
, self.fonction_screen_width |
|||
, 0 + i * self.fonction_screen_width/20, fill='red') |
|||
|
|||
def calculate(self) -> None: |
|||
"""Calcule dans la partie graphique""" |
|||
exp = parse_string_to_list(self.calcul_entry.get()) |
|||
exp = npi2tree(inf2npi(exp)) |
|||
exp.print_tree() |
|||
try: |
|||
result = exp.evalue() |
|||
except ZeroDivisionError: |
|||
result = "ERREUR" |
|||
self.calcul_screen.configure(text=result) |
|||
|
|||
def draw_graph(self): |
|||
"""Dessine les points du graphique""" |
|||
self.fonction_screen.delete('all') |
|||
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) |
|||
max_y = max(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) |
|||
for i, (x, y) in enumerate(fonction_points): |
|||
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) |
|||
|
|||
|
|||
class Tk_app(tk.Tk): |
|||
"""Classe d'application graphique si python < 3.7""" |
|||
def __init__(self) -> None: |
|||
super().__init__() |
|||
|
|||
self.fonction_screen_height = None |
|||
self.fonction_screen_width = None |
|||
|
|||
# touches de la calculette |
|||
|
|||
self.keys = [] |
|||
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 |
|||
if type(val) == int: |
|||
self.keys.append(tk.Button(self, text=val, command=lambda x=val: self.add_value(x), background="blue", fg="gray90")) |
|||
elif val == "clear": |
|||
self.keys.append(tk.Button(self, text=val, command=self.clear_screen)) |
|||
elif val == "←": |
|||
self.keys.append(tk.Button(self, text=val, command=self.move_cursor_left)) |
|||
elif val == "→": |
|||
self.keys.append(tk.Button(self, text=val, command=self.move_cursor_right)) |
|||
else: |
|||
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) |
|||
|
|||
# Calcul Frame |
|||
|
|||
self.calcul_frame = tk.Frame(self, bg="gray30") |
|||
|
|||
self.calcul_screen = tk.Label(self.calcul_frame, text="hello") |
|||
self.calcul_screen.pack(fill="both", expand=True) |
|||
self.calcul_entry = tk.Entry(self.calcul_frame, width=50) |
|||
self.calcul_entry.pack(fill="both", expand=True) |
|||
|
|||
# Fonction Frame |
|||
|
|||
self.fonction_frame = tk.Frame(self, bg="gray30") |
|||
|
|||
self.fonction_bornes_frame = tk.Frame(self.fonction_frame) |
|||
|
|||
self.fonction_bornes_entry = tk.Entry(self.fonction_bornes_frame) |
|||
self.fonction_bornes_entry.insert(0, "-100,100") |
|||
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.grid(sticky="ew", row=0) |
|||
|
|||
self.fonction_bornes_frame.grid(sticky="ns", column=0, rowspan=2) |
|||
|
|||
|
|||
self.fonction_screen = tk.Canvas(self.fonction_frame) |
|||
self.fonction_screen.grid(sticky="nsew", column=1, row=0) |
|||
self.fonction_entry = tk.Entry(self.fonction_frame) |
|||
self.fonction_entry.grid(sticky='ew', column=1, row=1) |
|||
|
|||
# navigation menu |
|||
self.navigation_frame = tk.Frame(self, bg="gray50") |
|||
self.navigation_frame.grid(row=0, rowspan=10, column=0, sticky="nsew") |
|||
|
|||
self.titre = tk.Label(self.navigation_frame, text="Wx Calculator", compound="left" |
|||
, font=tk.font.Font(size=15, weight="bold")) |
|||
self.titre.grid(row=0, column=0, padx=20, pady=20) |
|||
|
|||
self.home_button = tk.Button(self.navigation_frame, height=3 |
|||
, text="Calcul", bg="gray30", fg="gray10", anchor="w" |
|||
, command=lambda: self.select_frame_by_name("Calcul")) |
|||
self.home_button.grid(row=1, column=0, sticky="ew") |
|||
|
|||
self.function_button = tk.Button(self.navigation_frame, height=3 |
|||
, text="Fonction", bg="gray30", fg="gray10", anchor="w" |
|||
, command=lambda: self.select_frame_by_name("Fonction")) |
|||
self.function_button.grid(row=2, column=0, sticky="ew") |
|||
|
|||
self.mode = 'Calcul' |
|||
|
|||
# select default frame |
|||
|
|||
self.select_frame_by_name("Calcul") |
|||
|
|||
def select_frame_by_name(self, name): |
|||
"""Change de mode : passe de calcul à fonction""" |
|||
# set button color for selected button |
|||
self.home_button.configure(bg="gray75" if name == "Calcul" else "gray30") |
|||
self.function_button.configure(bg="gray75" if name == "Fonction" else "gray30") |
|||
|
|||
# show selected frame |
|||
if name == "Calcul": |
|||
self.mode = "Calcul" |
|||
self.calcul_frame.grid(padx=20, pady=20, columnspan=5, column=1, row=0, sticky="nsew") |
|||
self.keys[-1].configure(command=self.calculate) |
|||
|
|||
else: |
|||
self.calcul_frame.grid_forget() |
|||
if name == "Fonction": |
|||
self.mode = "Fonction" |
|||
self.fonction_frame.grid(columnspan=5, column=1, row=0, padx=20, pady=20, sticky="ns") |
|||
self.fonction_frame.update() |
|||
self.fonction_screen_width = self.fonction_screen.winfo_width() |
|||
self.keys[-1].configure(command=self.draw_graph) |
|||
self.fonction_screen_height = self.fonction_screen.winfo_height() |
|||
else: |
|||
self.fonction_frame.grid_forget() |
|||
|
|||
def add_value(self, value) -> None: |
|||
"""Ajoute le charactère à la suite de l'expression""" |
|||
parenthesis = False |
|||
if value == "e": |
|||
value += "^" |
|||
if value == "(": |
|||
value += ")" |
|||
parenthesis = True |
|||
elif value in ["ln", "cos", "sin", "tan", "√"]: |
|||
value += "()" |
|||
parenthesis = True |
|||
if self.mode == 'Fonction': |
|||
self.fonction_entry.insert(tk.INSERT, value) |
|||
else: |
|||
self.calcul_entry.insert(tk.INSERT, value) |
|||
if parenthesis: |
|||
self.move_cursor_left() |
|||
|
|||
def clear_screen(self) -> None: |
|||
"""Enlève l'affichage actuel""" |
|||
if self.mode == "Calcul": |
|||
self.calcul_screen.configure(text="") |
|||
self.calcul_entry.delete('0', 'end') |
|||
else: |
|||
self.fonction_screen.delete('all') |
|||
self.fonction_entry.delete(0, 'end') |
|||
|
|||
def move_cursor_left(self): |
|||
"""Déplace le curseur à gauche""" |
|||
if self.mode == "Calcul": |
|||
pos = self.calcul_entry.index(tk.INSERT) |
|||
if pos > 0: |
|||
self.calcul_entry.icursor(pos -1) |
|||
else: |
|||
pos = self.fonction_entry.index(tk.INSERT) |
|||
if pos > 0: |
|||
self.fonction_entry.icursor(pos-1) |
|||
|
|||
def move_cursor_right(self): |
|||
"""Déplace le curseur à droite""" |
|||
if self.mode == "Calcul": |
|||
pos = self.calcul_entry.index(tk.INSERT) |
|||
if pos < len(self.calcul_entry.get()): |
|||
self.calcul_entry.icursor(pos + 1) |
|||
else: |
|||
pos = self.fonction_entry.index(tk.INSERT) |
|||
if pos < len(self.fonction_entry.get()): |
|||
self.fonction_entry.icursor(pos + 1) |
|||
|
|||
def draw_framing(self, min_x, max_x, min_y, max_y): |
|||
"""Dessine le cadrillage du graphique""" |
|||
ratio = self.fonction_screen_height / self.fonction_screen_width # longueur par largeur du canvas |
|||
|
|||
# position du max dans l'interval |
|||
abscisse = max_y / (abs(min_y)+abs(max_y)) |
|||
ordonnee = max_x / (abs(min_x)+abs(max_x)) |
|||
|
|||
for i in range(20): # dessin des lignes verticales |
|||
if i == int(20 - 20*ordonnee): |
|||
self.fonction_screen.create_line(0 + i * self.fonction_screen_width/20, 0 |
|||
, 0 + i * self.fonction_screen_width/20, self.fonction_screen_height |
|||
, fill='red', width=4) |
|||
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") |
|||
self.fonction_screen.tag_lower(rect, text) |
|||
|
|||
text = self.fonction_screen.create_text(0 + i * self.fonction_screen_width/20, |
|||
self.fonction_screen_height - 10, text=min_y) |
|||
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white") |
|||
self.fonction_screen.tag_lower(rect, text) |
|||
else: |
|||
self.fonction_screen.create_line(0 + i * self.fonction_screen_width/20, 0 |
|||
, 0 + i * self.fonction_screen_width/20, self.fonction_screen_height |
|||
, fill='red') |
|||
nb_abscisse_lines = int(20*ratio) # nombre de lignes horizontales à tracer |
|||
|
|||
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 |
|||
self.fonction_screen.create_line(0, 0 + i * self.fonction_screen_width/20 |
|||
, self.fonction_screen_width |
|||
, 0 + i * self.fonction_screen_width/20, fill='red' |
|||
, width=4) |
|||
text = self.fonction_screen.create_text(15, 0 + i * self.fonction_screen_width/20 |
|||
, text=min_x) |
|||
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text) |
|||
, fill="white") |
|||
self.fonction_screen.tag_lower(rect, text) |
|||
|
|||
text = self.fonction_screen.create_text(self.fonction_screen_width - 15 |
|||
, 0 + i * self.fonction_screen_width/20 |
|||
, text=max_x) |
|||
rect = self.fonction_screen.create_rectangle(self.fonction_screen.bbox(text), fill="white") |
|||
self.fonction_screen.tag_lower(rect, text) |
|||
else: |
|||
self.fonction_screen.create_line(0, 0 + i * self.fonction_screen_width/20 |
|||
, self.fonction_screen_width |
|||
, 0 + i * self.fonction_screen_width/20, fill='red') |
|||
|
|||
def calculate(self) -> None: |
|||
"""Calcule dans la partie graphique""" |
|||
exp = parse_string_to_list(self.calcul_entry.get()) |
|||
exp = npi2tree(inf2npi(exp)) |
|||
exp.print_tree() |
|||
try: |
|||
result = exp.evalue() |
|||
except ZeroDivisionError: |
|||
result = "ERREUR" |
|||
self.calcul_screen.configure(text=result) |
|||
|
|||
def draw_graph(self): |
|||
"""Dessine les points du graphique""" |
|||
self.fonction_screen.delete('all') |
|||
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) |
|||
max_y = max(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) |
|||
for i, (x, y) in enumerate(fonction_points): |
|||
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) |
@ -0,0 +1,73 @@ |
|||
"""Fichier de classe pour les expressions""" |
|||
import math |
|||
|
|||
class Expression: |
|||
"""manipule les expression sous forme d'arbre""" |
|||
def __init__(self, val, fils_gauche=None, fils_droit=None) -> None: |
|||
self.val = val |
|||
self.fils_gauche = fils_gauche |
|||
self.fils_droit = fils_droit |
|||
|
|||
def __str__(self) -> str: |
|||
"""renvoie l'expression sous forme infixé""" |
|||
if self.est_feuille(): |
|||
return str(self.val) |
|||
return '('+self.fils_gauche.__str__()+str(self.val)+self.fils_droit.__str__()+')' |
|||
|
|||
def print_tree(self, level=0): |
|||
""" Affiche le noeud joliment""" |
|||
if self.fils_gauche is not None: |
|||
self.fils_gauche.print_tree(level + 1) |
|||
print(' ' * 4 * level + '-> ' + str(self.val)) |
|||
if self.fils_droit is not None: |
|||
self.fils_droit.print_tree(level + 1) |
|||
|
|||
def est_feuille(self) -> bool: |
|||
""" Renvoie true si le noeud est une feuille""" |
|||
if self.fils_droit is None and self.fils_gauche is None: |
|||
return True |
|||
return False |
|||
|
|||
def evalue(self, x=0) -> float: |
|||
""" Renvoie le résultat de l'expression""" |
|||
try: |
|||
if self.est_feuille(): |
|||
if self.val == 'x': |
|||
return x |
|||
return float(self.val) |
|||
elif self.val == '+': |
|||
return self.fils_gauche.evalue(x) + self.fils_droit.evalue(x) |
|||
elif self.val == '*': |
|||
return self.fils_gauche.evalue(x) * self.fils_droit.evalue(x) |
|||
elif self.val == '/': |
|||
return self.fils_gauche.evalue(x) / self.fils_droit.evalue(x) |
|||
elif self.val == '^': |
|||
return self.fils_gauche.evalue(x) ** self.fils_droit.evalue(x) |
|||
elif self.val == '-': |
|||
return self.fils_gauche.evalue(x) - self.fils_droit.evalue(x) |
|||
elif self.val == 'ln': |
|||
return math.log(self.fils_gauche.evalue(x)) |
|||
elif self.val == '√': |
|||
return math.sqrt(self.fils_gauche.evalue(x)) |
|||
elif self.val == "cos": |
|||
return math.cos(self.fils_gauche.evalue(x)) |
|||
elif self.val == "sin": |
|||
return math.sin(self.fils_gauche.evalue(x)) |
|||
elif self.val == "tan": |
|||
return math.tan(self.fils_gauche.evalue(x)) |
|||
|
|||
except ZeroDivisionError: |
|||
raise ZeroDivisionError |
|||
|
|||
def valeurs_de_fonction(self, start, end): |
|||
""" Calcul les valeurs entre start et end""" |
|||
result = [] |
|||
pas = (end - start) / 1000 |
|||
while start <= end: |
|||
try: |
|||
result.append((start, self.evalue(start))) |
|||
except: |
|||
pass |
|||
start += pas |
|||
return result |
|||
|
Loading…
Reference in new issue