From a260d3f23d8d287a9c139d2ebf81da1c37eb8814 Mon Sep 17 00:00:00 2001 From: "barraux.a" Date: Wed, 8 Mar 2023 17:38:20 +0100 Subject: [PATCH] bug racine carre --- main.py | 14 +- package/ctk_app.py | 689 +++++++++++++++++++------------------- package/data_structure.py | 668 ++++++++++++++++++------------------ package/expression.py | 146 ++++---- package/tk_app.py | 659 ++++++++++++++++++------------------ 5 files changed, 1091 insertions(+), 1085 deletions(-) diff --git a/main.py b/main.py index c9d2e7a..d911604 100644 --- a/main.py +++ b/main.py @@ -1,7 +1,7 @@ -# test si custom tkinter fonction sur le pc -try: - from package.ctk_app import App -except ImportError: - from package.tk_app import App - -App().mainloop() +# test si custom tkinter fonction sur le pc +try: + from package.ctk_app import App +except ImportError: + from package.tk_app import App + +App().mainloop() diff --git a/package/ctk_app.py b/package/ctk_app.py index 313c25d..13168c1 100644 --- a/package/ctk_app.py +++ b/package/ctk_app.py @@ -1,343 +1,346 @@ -"""Classe d'application tk et ctk""" - -import math -import customtkinter as ctk -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 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) +"""Classe d'application tk et ctk""" + +import math +import customtkinter as ctk +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 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(',')) # bornes du graphique + 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) + ratio = (fonction_points[0][0] - min_x) / (abs(max_x)+abs(min_x)) + i = len(fonction_points) * ratio + for (x, y) in 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) + i += 1 diff --git a/package/data_structure.py b/package/data_structure.py index 13f4fa1..3a98c64 100644 --- a/package/data_structure.py +++ b/package/data_structure.py @@ -1,335 +1,335 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Tue Sep 22 20:44:35 2020 - -@author: manu -""" - - -class Maillon: - """Un maillon d'une liste chaînée.""" - def __init__(self, valeur, suivant): - self.valeur = valeur - self.suivant = suivant - - def __str__(self): - """Renvoie une chane de caractères représentant le maillon.""" - return str(self.valeur) - - -class ListeChaine: - """Une liste chaînée.""" - def __init__(self, tete=None): - """Crée une liste vide, ou une liste dont la tete (un maillon) - est donnée.""" - self.__tete = tete - - def est_vide(self): - """Indique si la liste est vide.""" - return self.__tete is None - - def tete(self): - """Renvoie la valeur du premier élément de la liste.""" - if self.est_vide(): - raise IndexError("La liste vide n'a pas de tête") - return self.__tete.valeur - - def queue(self): - """Renvoie la queue de la liste.""" - if self.est_vide(): - raise IndexError("La liste vide n'a pas de queue") - return ListeChaine(self.__tete.suivant) - - def ajoute(self, valeur): - """ajoute `valeur` en tête de la liste.""" - self.__tete = Maillon(valeur, self.__tete) - - def __str__(self): - """Renvoie une chaîne de caractères représentant la liste.""" - maillon = self.__tete - s = '' - while maillon is not None: - s = s + str(maillon.valeur) - maillon = maillon.suivant - if maillon is not None: - s += '->' - return s - - def __len__(self): - """Renvoie la longueur de la liste.""" - maillon = self.__tete - long = 0 - while maillon is not None: - long = long + 1 - maillon = maillon.suivant - return long - - def __getitem__(self, n): - """Renvoie l'élément d'indice n de la liste.""" - maillon = self.__tete - i = 0 - while i < n and maillon is not None: - i = i + 1 - maillon = maillon.suivant - if maillon is None or n < 0: - raise IndexError("Indice non valide") - return maillon.valeur - - def __add__(self, other): - """Renvoie la liste correspondant à la concaténation des 2 listes.""" - if self.est_vide(): - return other - v = self.tete() - q = self.queue() - return ListeChaine(Maillon(v, (q + other).__tete)) - - def reverse(self): - """Renvoie une liste correspondant à la liste renversée.""" - res = ListeChaine() - maillon = self.__tete - while maillon is not None: - res.ajoute(maillon.valeur) - maillon = maillon.suivant - return res - - -class Pile_lst: - """Implémentation d'une pile par une liste.""" - def __init__(self): - """Crée une pile vide.""" - self.__pile = [] - - def est_vide(self): - """Indique si la pile est vide.""" - return self.__pile == [] - - def empiler(self, valeur): - """Empile la valeur.""" - self.__pile.append(valeur) - - def depiler(self): - """Dépile le sommet de la pile et le renvoie.""" - return self.__pile.pop() - - def taille(self): - """Renvoie la taille de la pile.""" - return len(self.__pile) - - def sommet(self): - """Renvoie le sommet de la pile (sans le dépiler).""" - return self.__pile[-1] - - def __str__(self): - s = "|" - for val in self.__pile: - s = str(val) + "->" + s - return s - - -class Pile_chaine: - """Implémentation d'une pile par une liste chaînée.""" - def __init__(self): - """Crée une pile vide.""" - self.__pile = ListeChaine() - self.__taille = 0 - - def est_vide(self): - """Indique si la pile est vide.""" - return self.__taille == 0 - - def empiler(self, valeur): - """Empile la valeur.""" - self.__pile.ajoute(valeur) - self.__taille += 1 - - def depiler(self): - """Dépile le sommet de la pile et le renvoie.""" - if self.est_vide(): - raise IndexError("Impossible de dépiler une pile vide.") - valeur = self.__pile.tete() - self.__pile = self.__pile.queue() - self.__taille -= 1 - return valeur - - def taille(self): - """Renvoie la taille de la pile.""" - return self.__taille - - def sommet(self): - """Renvoie le sommet de la pile (sans le dépiler).""" - if self.est_vide(): - raise IndexError("Une pile vide n'a pas de sommet.") - return self.__pile.tete() - - def __str__(self): - return str(self.__pile) + "->|" - - -class File_lst: - """Implémentation d'une file par une liste.""" - def __init__(self): - """Crée une file vide.""" - self.__file = [] - - def est_vide(self): - """Indique si la file est vide.""" - return self.__file == [] - - def enfiler(self, valeur): - """Enfile l'élément valeur.""" - self.__file.append(valeur) - - def defiler(self): - """Défile la tête de la file et la renvoie.""" - return self.__file.pop(0) - - def taille(self): - """Renvoie la taille de la file.""" - return len(self.__file) - - def tete(self): - """Renvoie la tête de la file (sans la défiler).""" - return self.__file[0] - - def __str__(self): - s = "tete->" - for val in self.__file: - s += str(val) + "->" - return s + "queue" - - -class Maillon_db: - """Un maillon d'une liste doublement chaînée.""" - def __init__(self, precedent, valeur, suivant): - self.valeur = valeur - self.precedent = precedent - self.suivant = suivant - - def __str__(self): - return str(self.valeur) - - -class File_chaine: - """Implémentation d'une file par une liste doublement chaînée.""" - def __init__(self): - """Crée une file vide.""" - self.__debut = self.__fin = None - self.__taille = 0 - - def __iter__(self): - return self - - def __next__(self): - if self.est_vide(): - raise StopIteration - return self.defiler() - - def est_vide(self): - """Indique si la file est vide.""" - return self.__taille == 0 - - def enfiler(self, valeur): - """Enfile l'élément valeur.""" - maillon = Maillon_db(self.__fin, valeur, None) - if self.est_vide(): - self.__debut = self.__fin = maillon - else: - self.__fin.suivant = maillon - self.__fin = maillon - self.__taille += 1 - - def defiler(self): - """Défile la tête de la file et la renvoie.""" - if self.est_vide(): - raise IndexError("Impossible de défiler une file vide.") - valeur = self.__debut.valeur - self.__taille -= 1 - if self.est_vide(): - self.__debut = self.__fin = None - else: - self.__debut = self.__debut.suivant - self.__debut.precedent = None - return valeur - - def taille(self): - """Renvoie la taille de la file.""" - return self.__taille - - def tete(self): - """Renvoie la tête de la file (sans la défiler).""" - if self.est_vide(): - raise IndexError("Une file vide n'a pas de tête.") - return self.__debut.valeur - - def __str__(self): - s = "tete->" - maillon = self.__debut - while maillon is not None: - s += str(maillon) + "->" - maillon = maillon.suivant - return s + "queue" - - -class Arbre: - def __init__(self, val) -> None: - self.val = val - self.gauche = None - self.droit = None - - def insere_gauche(self, val) -> object: - """insere un arbre gauche a la racine de l'arbre""" - nouvel_arbre = Arbre(val) - nouvel_arbre.gauche = self.gauche - self.gauche = nouvel_arbre - return nouvel_arbre - - def insere_droit(self, val) -> object: - """insere un arbre gauche a la racine de l'arbre""" - nouvel_arbre = Arbre(val) - nouvel_arbre.gauche = self.droit - self.droit = nouvel_arbre - return nouvel_arbre - - -if __name__ == "__main__": - f = File_lst() - print(f.est_vide()) - f.enfiler('A') - f.enfiler('B') - f.enfiler('C') - print(f.est_vide()) - print(f.tete()) - print(f) - print(f.taille()) - print(f.defiler()) - print(f.defiler()) - print(f.defiler()) - print(f.est_vide()) - lst = ListeChaine() - print(lst.est_vide()) - lst.ajoute(306) - lst.ajoute(42) - lst.ajoute(205) - print(lst) - print(lst.est_vide()) - print(lst[0]) - print(lst[1]) - print(len(lst)) - lst2 = ListeChaine() - lst2.ajoute(18) - lst2.ajoute(45) - print(lst + lst2) - p = Pile_lst() - print(p.est_vide()) - p.empiler('A') - p.empiler('B') - p.empiler('C') - print(p.est_vide()) - print(p.sommet()) - print(p) - print(p.taille()) - print(p.depiler()) - print(p.depiler()) - print(p.depiler()) +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Tue Sep 22 20:44:35 2020 + +@author: manu +""" + + +class Maillon: + """Un maillon d'une liste chaînée.""" + def __init__(self, valeur, suivant): + self.valeur = valeur + self.suivant = suivant + + def __str__(self): + """Renvoie une chane de caractères représentant le maillon.""" + return str(self.valeur) + + +class ListeChaine: + """Une liste chaînée.""" + def __init__(self, tete=None): + """Crée une liste vide, ou une liste dont la tete (un maillon) + est donnée.""" + self.__tete = tete + + def est_vide(self): + """Indique si la liste est vide.""" + return self.__tete is None + + def tete(self): + """Renvoie la valeur du premier élément de la liste.""" + if self.est_vide(): + raise IndexError("La liste vide n'a pas de tête") + return self.__tete.valeur + + def queue(self): + """Renvoie la queue de la liste.""" + if self.est_vide(): + raise IndexError("La liste vide n'a pas de queue") + return ListeChaine(self.__tete.suivant) + + def ajoute(self, valeur): + """ajoute `valeur` en tête de la liste.""" + self.__tete = Maillon(valeur, self.__tete) + + def __str__(self): + """Renvoie une chaîne de caractères représentant la liste.""" + maillon = self.__tete + s = '' + while maillon is not None: + s = s + str(maillon.valeur) + maillon = maillon.suivant + if maillon is not None: + s += '->' + return s + + def __len__(self): + """Renvoie la longueur de la liste.""" + maillon = self.__tete + long = 0 + while maillon is not None: + long = long + 1 + maillon = maillon.suivant + return long + + def __getitem__(self, n): + """Renvoie l'élément d'indice n de la liste.""" + maillon = self.__tete + i = 0 + while i < n and maillon is not None: + i = i + 1 + maillon = maillon.suivant + if maillon is None or n < 0: + raise IndexError("Indice non valide") + return maillon.valeur + + def __add__(self, other): + """Renvoie la liste correspondant à la concaténation des 2 listes.""" + if self.est_vide(): + return other + v = self.tete() + q = self.queue() + return ListeChaine(Maillon(v, (q + other).__tete)) + + def reverse(self): + """Renvoie une liste correspondant à la liste renversée.""" + res = ListeChaine() + maillon = self.__tete + while maillon is not None: + res.ajoute(maillon.valeur) + maillon = maillon.suivant + return res + + +class Pile_lst: + """Implémentation d'une pile par une liste.""" + def __init__(self): + """Crée une pile vide.""" + self.__pile = [] + + def est_vide(self): + """Indique si la pile est vide.""" + return self.__pile == [] + + def empiler(self, valeur): + """Empile la valeur.""" + self.__pile.append(valeur) + + def depiler(self): + """Dépile le sommet de la pile et le renvoie.""" + return self.__pile.pop() + + def taille(self): + """Renvoie la taille de la pile.""" + return len(self.__pile) + + def sommet(self): + """Renvoie le sommet de la pile (sans le dépiler).""" + return self.__pile[-1] + + def __str__(self): + s = "|" + for val in self.__pile: + s = str(val) + "->" + s + return s + + +class Pile_chaine: + """Implémentation d'une pile par une liste chaînée.""" + def __init__(self): + """Crée une pile vide.""" + self.__pile = ListeChaine() + self.__taille = 0 + + def est_vide(self): + """Indique si la pile est vide.""" + return self.__taille == 0 + + def empiler(self, valeur): + """Empile la valeur.""" + self.__pile.ajoute(valeur) + self.__taille += 1 + + def depiler(self): + """Dépile le sommet de la pile et le renvoie.""" + if self.est_vide(): + raise IndexError("Impossible de dépiler une pile vide.") + valeur = self.__pile.tete() + self.__pile = self.__pile.queue() + self.__taille -= 1 + return valeur + + def taille(self): + """Renvoie la taille de la pile.""" + return self.__taille + + def sommet(self): + """Renvoie le sommet de la pile (sans le dépiler).""" + if self.est_vide(): + raise IndexError("Une pile vide n'a pas de sommet.") + return self.__pile.tete() + + def __str__(self): + return str(self.__pile) + "->|" + + +class File_lst: + """Implémentation d'une file par une liste.""" + def __init__(self): + """Crée une file vide.""" + self.__file = [] + + def est_vide(self): + """Indique si la file est vide.""" + return self.__file == [] + + def enfiler(self, valeur): + """Enfile l'élément valeur.""" + self.__file.append(valeur) + + def defiler(self): + """Défile la tête de la file et la renvoie.""" + return self.__file.pop(0) + + def taille(self): + """Renvoie la taille de la file.""" + return len(self.__file) + + def tete(self): + """Renvoie la tête de la file (sans la défiler).""" + return self.__file[0] + + def __str__(self): + s = "tete->" + for val in self.__file: + s += str(val) + "->" + return s + "queue" + + +class Maillon_db: + """Un maillon d'une liste doublement chaînée.""" + def __init__(self, precedent, valeur, suivant): + self.valeur = valeur + self.precedent = precedent + self.suivant = suivant + + def __str__(self): + return str(self.valeur) + + +class File_chaine: + """Implémentation d'une file par une liste doublement chaînée.""" + def __init__(self): + """Crée une file vide.""" + self.__debut = self.__fin = None + self.__taille = 0 + + def __iter__(self): + return self + + def __next__(self): + if self.est_vide(): + raise StopIteration + return self.defiler() + + def est_vide(self): + """Indique si la file est vide.""" + return self.__taille == 0 + + def enfiler(self, valeur): + """Enfile l'élément valeur.""" + maillon = Maillon_db(self.__fin, valeur, None) + if self.est_vide(): + self.__debut = self.__fin = maillon + else: + self.__fin.suivant = maillon + self.__fin = maillon + self.__taille += 1 + + def defiler(self): + """Défile la tête de la file et la renvoie.""" + if self.est_vide(): + raise IndexError("Impossible de défiler une file vide.") + valeur = self.__debut.valeur + self.__taille -= 1 + if self.est_vide(): + self.__debut = self.__fin = None + else: + self.__debut = self.__debut.suivant + self.__debut.precedent = None + return valeur + + def taille(self): + """Renvoie la taille de la file.""" + return self.__taille + + def tete(self): + """Renvoie la tête de la file (sans la défiler).""" + if self.est_vide(): + raise IndexError("Une file vide n'a pas de tête.") + return self.__debut.valeur + + def __str__(self): + s = "tete->" + maillon = self.__debut + while maillon is not None: + s += str(maillon) + "->" + maillon = maillon.suivant + return s + "queue" + + +class Arbre: + def __init__(self, val) -> None: + self.val = val + self.gauche = None + self.droit = None + + def insere_gauche(self, val) -> object: + """insere un arbre gauche a la racine de l'arbre""" + nouvel_arbre = Arbre(val) + nouvel_arbre.gauche = self.gauche + self.gauche = nouvel_arbre + return nouvel_arbre + + def insere_droit(self, val) -> object: + """insere un arbre gauche a la racine de l'arbre""" + nouvel_arbre = Arbre(val) + nouvel_arbre.gauche = self.droit + self.droit = nouvel_arbre + return nouvel_arbre + + +if __name__ == "__main__": + f = File_lst() + print(f.est_vide()) + f.enfiler('A') + f.enfiler('B') + f.enfiler('C') + print(f.est_vide()) + print(f.tete()) + print(f) + print(f.taille()) + print(f.defiler()) + print(f.defiler()) + print(f.defiler()) + print(f.est_vide()) + lst = ListeChaine() + print(lst.est_vide()) + lst.ajoute(306) + lst.ajoute(42) + lst.ajoute(205) + print(lst) + print(lst.est_vide()) + print(lst[0]) + print(lst[1]) + print(len(lst)) + lst2 = ListeChaine() + lst2.ajoute(18) + lst2.ajoute(45) + print(lst + lst2) + p = Pile_lst() + print(p.est_vide()) + p.empiler('A') + p.empiler('B') + p.empiler('C') + print(p.est_vide()) + print(p.sommet()) + print(p) + print(p.taille()) + print(p.depiler()) + print(p.depiler()) + print(p.depiler()) print(p.est_vide()) \ No newline at end of file diff --git a/package/expression.py b/package/expression.py index a702d07..de70f5d 100644 --- a/package/expression.py +++ b/package/expression.py @@ -1,73 +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 - +"""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 + diff --git a/package/tk_app.py b/package/tk_app.py index e96d138..8c4293c 100644 --- a/package/tk_app.py +++ b/package/tk_app.py @@ -1,328 +1,331 @@ -"""Classe d'application tk""" - -import math -import tkinter as tk -import tkinter.font -from package.data_structure import Pile_chaine -from package.expression import Expression - - -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 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) \ No newline at end of file +"""Classe d'application tk""" + +import math +import tkinter as tk +import tkinter.font +from package.data_structure import Pile_chaine +from package.expression import Expression + + +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 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) + ratio = (fonction_points[0][0] - min_x) / (abs(max_x)+abs(min_x)) + i = len(fonction_points) * ratio + for (x, y) in 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) + i += 1 \ No newline at end of file