Skip to content

Commit a80761b

Browse files
committed
Merge branch 'master' of github.com:Team-Arduino-Logique/Arduino-Logique into 90-dessindeplacement-de-fils-est-tres-lent
2 parents 556aa4e + f65348e commit a80761b

File tree

1 file changed

+121
-56
lines changed

1 file changed

+121
-56
lines changed

menus.py

Lines changed: 121 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from copy import deepcopy
77

8+
from dataclasses import dataclass
89
import tkinter as tk
910
from tkinter import messagebox, filedialog, ttk
1011
import json
@@ -54,6 +55,20 @@
5455
}
5556

5657

58+
@dataclass
59+
class SerialPort:
60+
"""Data class representing the info for a serial port."""
61+
62+
com_port: str | None
63+
"""The COM port to connect to."""
64+
baud_rate: int
65+
"""The baud rate for the port."""
66+
timeout: int
67+
"""The timeout for the port."""
68+
connection: serial.Serial | None
69+
"""The serial connection object."""
70+
71+
5772
class Menus:
5873
"""
5974
Menus class for creating a custom menu bar in a Tkinter application.
@@ -98,40 +113,31 @@ def __init__(
98113
self.menu_bar = tk.Frame(parent, bg="#333333")
99114
"""The frame containing the menu bar buttons."""
100115

116+
self.serial_port = SerialPort(None, 115200, 1, None)
117+
"""The serial port configuration."""
118+
101119
# Define menu items and their corresponding dropdown options
102120
menus = {
103-
"File": ["New", "Open", "Save", "Exit"],
104-
"Controllers": [
105-
"Arduino Mega",
106-
"Arduino Uno",
107-
"Arduino Micro",
108-
"Arduino Mini",
109-
"STM32",
110-
"NodeMCU ESP8266",
111-
"NodeMCU ESP32"
121+
"Fichier": ["Nouveau", "Ouvrir", "Enregistrer", "Quitter"],
122+
"Microcontrôleur": ["Choisir un microcontrôleur", "Table de correspondance", "Configurer le port série"],
123+
"Exporter": [
124+
"Vérifier",
125+
"Téléverser",
112126
],
113-
"Ports": ["Configure Ports"],
114-
"Export": ["Show Correspondence Table"],
115-
"Help": ["Documentation", "About"],
127+
"Aide": ["Documentation", "À propos"],
116128
}
117129

118130
# Mapping menu labels to their handler functions
119131
menu_commands = {
120-
"New": self.new_file,
121-
"Open": self.open_file,
122-
"Save": self.save_file,
123-
"Exit": self.parent.quit,
124-
"Arduino Mega": lambda: self.select_microcontroller("Arduino Mega"),
125-
"Arduino Uno": lambda: self.select_microcontroller("Arduino Uno"),
126-
"Arduino Micro": lambda: self.select_microcontroller("Arduino Micro"),
127-
"Arduino Mini": lambda: self.select_microcontroller("Arduino Mini"),
128-
"STM32": lambda: self.select_microcontroller("STM32"),
129-
"NodeMCU ESP8266": lambda: self.select_microcontroller("NodeMCU ESP8266"),
130-
"NodeMCU ESP32": lambda: self.select_microcontroller("NodeMCU ESP32"),
131-
"Configure Ports": self.configure_ports,
132-
"Show Correspondence Table": self.show_correspondence_table,
132+
"Nouveau": self.new_file,
133+
"Ouvrir": self.open_file,
134+
"Enregistrer": self.save_file,
135+
"Quitter": self.parent.quit,
136+
"Configurer le port série": self.configure_ports,
137+
"Table de correspondance": self.show_correspondence_table,
138+
"Choisir un microcontrôleur": self.select_microcontroller,
133139
"Documentation": self.open_documentation,
134-
"About": self.about,
140+
"À propos": self.about,
135141
}
136142

137143
# Create each menu button and its dropdown
@@ -142,11 +148,33 @@ def __init__(
142148
self.parent.bind("<Button-1>", self.close_dropdown, add="+")
143149
self.canvas.bind("<Button-1>", self.close_dropdown, add="+")
144150

145-
def select_microcontroller(self, microcontroller_name):
151+
def select_microcontroller(self):
146152
"""Handler for microcontroller selection."""
147-
print(f"{microcontroller_name} selected.")
148-
self.selected_microcontroller = microcontroller_name
149-
messagebox.showinfo("Microcontroller Selected", f"{microcontroller_name} has been selected.")
153+
# Create a new top-level window for the dialog
154+
dialog = tk.Toplevel(self.parent)
155+
dialog.title("Choisir un microcontrôleur")
156+
157+
# Set the size and position of the dialog
158+
dialog.geometry("300x150")
159+
160+
# Create a label for the combobox
161+
label = tk.Label(dialog, text="Choisir:")
162+
label.pack(pady=10)
163+
available_microcontrollers = list(MICROCONTROLLER_PINS.keys())
164+
# Create a combobox with the options
165+
combobox = ttk.Combobox(dialog, values=available_microcontrollers)
166+
combobox.pack(pady=10)
167+
168+
# Create a button to confirm the selection
169+
def confirm_selection():
170+
selected_option = combobox.get()
171+
print(f"Selected option: {selected_option}")
172+
self.selected_microcontroller = selected_option
173+
print(f"{selected_option} selected.")
174+
dialog.destroy()
175+
176+
confirm_button = tk.Button(dialog, text="Confirm", command=confirm_selection)
177+
confirm_button.pack(pady=10)
150178

151179
def show_correspondence_table(self):
152180
"""Displays the correspondence table between pin_io objects and microcontroller pins in a table format."""
@@ -173,13 +201,15 @@ def show_correspondence_table(self):
173201
if len(input_pin_ios) > len(input_pins):
174202
messagebox.showerror(
175203
"Too Many Inputs",
176-
f"You have {len(input_pin_ios)} input pin_ios but only {len(input_pins)} available input pins on the microcontroller.",
204+
f"You have {len(input_pin_ios)} input pin_ios but only "
205+
f"{len(input_pins)} available input pins on the microcontroller.",
177206
)
178207
return
179208
if len(output_pin_ios) > len(output_pins):
180209
messagebox.showerror(
181210
"Too Many Outputs",
182-
f"You have {len(output_pin_ios)} output pin_ios but only {len(output_pins)} available output pins on the microcontroller.",
211+
f"You have {len(output_pin_ios)} output pin_ios but only "
212+
f"{len(output_pins)} available output pins on the microcontroller.",
183213
)
184214
return
185215

@@ -248,14 +278,19 @@ def create_menu(self, menu_name, options, menu_commands):
248278
btn.pack(side="left")
249279

250280
# Create the dropdown frame
251-
dropdown = tk.Frame(self.parent, bg="#333333", bd=1, relief="solid", width=200)
281+
dropdown = tk.Frame(self.parent, bg="#333333", bd=1, relief="solid", width=250)
252282

253283
# Calculate dropdown height based on number of options
254284
button_height = 30 # Approximate height of each dropdown button
255285
dropdown_height = button_height * len(options)
256-
dropdown.place(x=0, y=0, width=200, height=dropdown_height) # Initial size based on options
286+
dropdown.place(x=0, y=0, width=250, height=dropdown_height) # Initial size based on options
257287
dropdown.place_forget() # Hide initially
258288

289+
def select_menu_item(option):
290+
"""Wrapper function to close dropdown and execute the menu command."""
291+
self.close_dropdown(None)
292+
menu_commands.get(option, lambda: print(f"{option} selected"))()
293+
259294
# Populate the dropdown with menu options
260295
for option in options:
261296
option_btn = tk.Button(
@@ -267,11 +302,11 @@ def create_menu(self, menu_name, options, menu_commands):
267302
activeforeground="white",
268303
bd=0,
269304
anchor="w",
270-
width=200,
305+
width=250,
271306
padx=20,
272307
pady=5,
273308
font=("FiraCode-Bold", 12),
274-
command=menu_commands.get(option, lambda opt=option: print(f"{opt} selected")),
309+
command=lambda o=option: select_menu_item(o),
275310
borderwidth=0,
276311
highlightthickness=0,
277312
)
@@ -297,7 +332,7 @@ def toggle_dropdown(self, menu_name):
297332
# Position the dropdown below the button
298333
btn_x = child.winfo_rootx() - self.parent.winfo_rootx()
299334
btn_y = child.winfo_rooty() - self.parent.winfo_rooty() + child.winfo_height()
300-
child.dropdown.place(x=btn_x, y=btn_y, width=200)
335+
child.dropdown.place(x=btn_x, y=btn_y, width=250)
301336
print(f"Opened dropdown for {menu_name}")
302337
child.dropdown.lift() # Ensure dropdown is on top
303338
else:
@@ -327,10 +362,14 @@ def close_dropdown(self, event):
327362
Parameters:
328363
- event (tk.Event): The event object.
329364
"""
330-
if not self.is_descendant(event.widget, self.menu_bar) and not any(
331-
self.is_descendant(event.widget, child.dropdown)
332-
for child in self.menu_bar.winfo_children()
333-
if isinstance(child, tk.Button) and hasattr(child, "dropdown")
365+
if (
366+
event is None
367+
or not self.is_descendant(event.widget, self.menu_bar)
368+
and not any(
369+
self.is_descendant(event.widget, child.dropdown)
370+
for child in self.menu_bar.winfo_children()
371+
if isinstance(child, tk.Button) and hasattr(child, "dropdown")
372+
)
334373
):
335374
for child in self.menu_bar.winfo_children():
336375
if isinstance(child, tk.Button) and hasattr(child, "dropdown"):
@@ -400,7 +439,6 @@ def open_file(self):
400439
]
401440
self.board.sketcher.circuit(x_o, y_o, model=model_io)
402441
else:
403-
# TODO add IO
404442
print(f"Unspecified component: {key}")
405443
messagebox.showinfo("Open File", f"Circuit loaded from {file_path}")
406444
except Exception as e:
@@ -429,32 +467,21 @@ def save_file(self):
429467
if "label" in comp_data:
430468
comp_data["label"] = comp_data["type"]
431469
if "wire" in key:
432-
comp_data.pop("XY", None) # Remove XY, will be recalculated anyway
470+
comp_data.pop("XY", None) # Remove XY, will be recalculated anyway
433471
# Save the data to a JSON file
434472
with open(file_path, "w", encoding="utf-8") as file:
435473
json.dump(circuit_data, file, indent=4)
436474
print(f"Circuit saved to {file_path}")
437475
messagebox.showinfo("Save Successful", f"Circuit saved to {file_path}")
438-
except Exception as e:
476+
except (TypeError, KeyError) as e:
439477
print(f"Error saving file: {e}")
440478
messagebox.showerror("Save Error", f"An error occurred while saving the file:\n{e}")
441479
else:
442480
print("Save file cancelled.")
443481

444-
def Arduino(self):
445-
"""Handler for the 'Arduino' menu item."""
446-
print("Arduino")
447-
messagebox.showinfo("Arduino", "Arduino choice functionality not implemented yet.")
448-
449-
def ESP32(self):
450-
"""Handler for the 'ESP32' menu item."""
451-
print("ESP32")
452-
messagebox.showinfo("ESP32", "ESP32 choice functionality not implemented yet.")
453-
454482
def configure_ports(self):
455483
"""Handler for the 'Configure Ports' menu item."""
456484
print("Configure Ports")
457-
458485
options = [comport.device for comport in serial.tools.list_ports.comports()]
459486
if len(options) == 0:
460487
message = "No COM ports available. Please connect a device and try again."
@@ -479,7 +506,7 @@ def configure_ports(self):
479506
def confirm_selection():
480507
selected_option = combobox.get()
481508
print(f"Selected option: {selected_option}")
482-
self.com_port = selected_option
509+
self.serial_port.com_port = selected_option
483510
dialog.destroy()
484511

485512
confirm_button = tk.Button(dialog, text="Confirm", command=confirm_selection)
@@ -494,3 +521,41 @@ def about(self):
494521
"""Handler for the 'About' menu item."""
495522
print("About this software")
496523
messagebox.showinfo("About", "ArduinoLogique v1.0\nSimulateur de circuits logiques")
524+
525+
def open_port(self):
526+
"""Handler for the 'Open Port' menu item."""
527+
try:
528+
self.serial_port.connection = serial.Serial(
529+
port=self.serial_port.com_port, baudrate=self.serial_port.baud_rate, timeout=self.serial_port.timeout
530+
)
531+
print(f"Port série {self.serial_port.com_port} ouvert avec succès.")
532+
except serial.SerialException as e:
533+
print(f"Erreur lors de l'ouverture du port {self.serial_port.com_port}: {e}")
534+
535+
def send_data(self, data):
536+
"""
537+
Send a string of data to the microcontroller through the serial port.
538+
"""
539+
if self.serial_port.connection and self.serial_port.connection.is_open:
540+
try:
541+
# Convertir la chaîne en bytes et l'envoyer
542+
self.serial_port.connection.write(data.encode("utf-8"))
543+
print(f"Données envoyées: {data}")
544+
except serial.SerialException as e:
545+
print(f"Erreur lors de l'envoi des données: {e}")
546+
else:
547+
print("Le port série n'est pas ouvert. Impossible d'envoyer les données.")
548+
549+
def close_port(self):
550+
"""Close the serial port."""
551+
if self.serial_port.connection and self.serial_port.connection.is_open:
552+
self.serial_port.connection.close()
553+
print(f"Port série {self.serial_port.com_port} fermé.")
554+
else:
555+
print("Le port série est déjà fermé.")
556+
557+
def download_script(self, script):
558+
"""Upload the script to the microcontroller through the serial port."""
559+
self.open_port()
560+
self.send_data(script)
561+
self.close_port()

0 commit comments

Comments
 (0)