Skip to content

Commit f65348e

Browse files
authored
Merge pull request #83 from Team-Arduino-Logique/81-ajouter-le-module-de-communication-avec-le-mc
81 ajouter le module de communication avec le mc
2 parents d113ebf + 0b50f5d commit f65348e

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

@@ -246,14 +276,19 @@ def create_menu(self, menu_name, options, menu_commands):
246276
btn.pack(side="left")
247277

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

251281
# Calculate dropdown height based on number of options
252282
button_height = 30 # Approximate height of each dropdown button
253283
dropdown_height = button_height * len(options)
254-
dropdown.place(x=0, y=0, width=200, height=dropdown_height) # Initial size based on options
284+
dropdown.place(x=0, y=0, width=250, height=dropdown_height) # Initial size based on options
255285
dropdown.place_forget() # Hide initially
256286

287+
def select_menu_item(option):
288+
"""Wrapper function to close dropdown and execute the menu command."""
289+
self.close_dropdown(None)
290+
menu_commands.get(option, lambda: print(f"{option} selected"))()
291+
257292
# Populate the dropdown with menu options
258293
for option in options:
259294
option_btn = tk.Button(
@@ -265,11 +300,11 @@ def create_menu(self, menu_name, options, menu_commands):
265300
activeforeground="white",
266301
bd=0,
267302
anchor="w",
268-
width=200,
303+
width=250,
269304
padx=20,
270305
pady=5,
271306
font=("FiraCode-Bold", 12),
272-
command=menu_commands.get(option, lambda opt=option: print(f"{opt} selected")),
307+
command=lambda o=option: select_menu_item(o),
273308
)
274309
option_btn.pack(fill="both")
275310

@@ -293,7 +328,7 @@ def toggle_dropdown(self, menu_name):
293328
# Position the dropdown below the button
294329
btn_x = child.winfo_rootx() - self.parent.winfo_rootx()
295330
btn_y = child.winfo_rooty() - self.parent.winfo_rooty() + child.winfo_height()
296-
child.dropdown.place(x=btn_x, y=btn_y, width=200)
331+
child.dropdown.place(x=btn_x, y=btn_y, width=250)
297332
print(f"Opened dropdown for {menu_name}")
298333
child.dropdown.lift() # Ensure dropdown is on top
299334
else:
@@ -323,10 +358,14 @@ def close_dropdown(self, event):
323358
Parameters:
324359
- event (tk.Event): The event object.
325360
"""
326-
if not self.is_descendant(event.widget, self.menu_bar) and not any(
327-
self.is_descendant(event.widget, child.dropdown)
328-
for child in self.menu_bar.winfo_children()
329-
if isinstance(child, tk.Button) and hasattr(child, "dropdown")
361+
if (
362+
event is None
363+
or not self.is_descendant(event.widget, self.menu_bar)
364+
and not any(
365+
self.is_descendant(event.widget, child.dropdown)
366+
for child in self.menu_bar.winfo_children()
367+
if isinstance(child, tk.Button) and hasattr(child, "dropdown")
368+
)
330369
):
331370
for child in self.menu_bar.winfo_children():
332371
if isinstance(child, tk.Button) and hasattr(child, "dropdown"):
@@ -396,7 +435,6 @@ def open_file(self):
396435
]
397436
self.board.sketcher.circuit(x_o, y_o, model=model_io)
398437
else:
399-
# TODO add IO
400438
print(f"Unspecified component: {key}")
401439
messagebox.showinfo("Open File", f"Circuit loaded from {file_path}")
402440
except Exception as e:
@@ -425,32 +463,21 @@ def save_file(self):
425463
if "label" in comp_data:
426464
comp_data["label"] = comp_data["type"]
427465
if "wire" in key:
428-
comp_data.pop("XY", None) # Remove XY, will be recalculated anyway
466+
comp_data.pop("XY", None) # Remove XY, will be recalculated anyway
429467
# Save the data to a JSON file
430468
with open(file_path, "w", encoding="utf-8") as file:
431469
json.dump(circuit_data, file, indent=4)
432470
print(f"Circuit saved to {file_path}")
433471
messagebox.showinfo("Save Successful", f"Circuit saved to {file_path}")
434-
except Exception as e:
472+
except (TypeError, KeyError) as e:
435473
print(f"Error saving file: {e}")
436474
messagebox.showerror("Save Error", f"An error occurred while saving the file:\n{e}")
437475
else:
438476
print("Save file cancelled.")
439477

440-
def Arduino(self):
441-
"""Handler for the 'Arduino' menu item."""
442-
print("Arduino")
443-
messagebox.showinfo("Arduino", "Arduino choice functionality not implemented yet.")
444-
445-
def ESP32(self):
446-
"""Handler for the 'ESP32' menu item."""
447-
print("ESP32")
448-
messagebox.showinfo("ESP32", "ESP32 choice functionality not implemented yet.")
449-
450478
def configure_ports(self):
451479
"""Handler for the 'Configure Ports' menu item."""
452480
print("Configure Ports")
453-
454481
options = [comport.device for comport in serial.tools.list_ports.comports()]
455482
if len(options) == 0:
456483
message = "No COM ports available. Please connect a device and try again."
@@ -475,7 +502,7 @@ def configure_ports(self):
475502
def confirm_selection():
476503
selected_option = combobox.get()
477504
print(f"Selected option: {selected_option}")
478-
self.com_port = selected_option
505+
self.serial_port.com_port = selected_option
479506
dialog.destroy()
480507

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

0 commit comments

Comments
 (0)