From e9315cb55bbabca204c58b3344d2e3332022de51 Mon Sep 17 00:00:00 2001 From: giusber2005 <156716475+giusber2005@users.noreply.github.com> Date: Sat, 2 Aug 2025 02:41:58 +0200 Subject: [PATCH] late night working --- .../main_dashboard.cpython-313.pyc | Bin 22946 -> 25513 bytes PlantDashboard/main_dashboard.py | 194 ++++++++++++------ path.txt | 25 +-- 3 files changed, 139 insertions(+), 80 deletions(-) diff --git a/PlantDashboard/__pycache__/main_dashboard.cpython-313.pyc b/PlantDashboard/__pycache__/main_dashboard.cpython-313.pyc index 287e6941f5b75d9a78dd225b0cab66110b3ae52b..9566346d917b42c30e28ae93cd529c9e4daa09c0 100644 GIT binary patch delta 8416 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 USy8)wsuBLA3zAMv7TfB-0T{nvY5)KL delta 6374 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 diff --git a/PlantDashboard/main_dashboard.py b/PlantDashboard/main_dashboard.py index fca9b7d..fb446ff 100644 --- a/PlantDashboard/main_dashboard.py +++ b/PlantDashboard/main_dashboard.py @@ -3,9 +3,10 @@ from tkinter import ttk, filedialog, messagebox from PIL import Image, ImageTk import json import os -from datetime import datetime +from datetime import datetime, date from plant_model import PlantGrowthModel from data_handler import DataHandler +from tkcalendar import DateEntry class PlantGrowthDashboard: def __init__(self, root): @@ -35,10 +36,10 @@ class PlantGrowthDashboard: 'humidity': 65.0, 'soil_acidity': 6.5, 'pressure': 1013.25, - 'brightness': 50000.0, + 'brightness': 30, 'nutrients': 75.0, 'water': 80.0, - 'co2': 400.0 + 'co2': 850 } self.env_params = { @@ -53,7 +54,6 @@ class PlantGrowthDashboard: } self.setup_ui() - self.update_prediction() def setup_ui(self): # Main container with square layout @@ -94,8 +94,6 @@ class PlantGrowthDashboard: ttk.Radiobutton(mode_frame, text="Controlled", variable=self.ambient_mode, value="controlled", command=self.on_mode_change).pack(anchor=tk.W) - ttk.Radiobutton(mode_frame, text="Semi-Controlled", variable=self.ambient_mode, - value="semi-controlled", command=self.on_mode_change).pack(anchor=tk.W) ttk.Radiobutton(mode_frame, text="Open", variable=self.ambient_mode, value="open", command=self.on_mode_change).pack(anchor=tk.W) @@ -109,12 +107,12 @@ class PlantGrowthDashboard: param_labels = { 'temperature': '🌡️ Temp (°C)', 'humidity': '💧 Humidity (%)', - 'soil_acidity': '🧪 pH', - 'pressure': '🌬️ Pressure', - 'brightness': '☀️ Light', + 'soil_acidity': '🧪 (pH)', + 'pressure': '🌬️ Pressure (Pa)', + 'brightness': '☀️ Light (DLI)', 'nutrients': '🌿 Nutrients (%)', 'water': '💦 Water (%)', - 'co2': '🫧 CO2' + 'co2': '🫧 CO2 (ppm)' } # Define bounds for each parameter (min, max) @@ -123,10 +121,10 @@ class PlantGrowthDashboard: 'humidity': (20, 90), 'soil_acidity': (4.0, 9.0), 'pressure': (950, 1100), - 'brightness': (100, 100000), + 'brightness': (5, 50), 'nutrients': (0, 100), 'water': (10, 100), - 'co2': (300, 1000) + 'co2': (400, 1200) } row = 4 @@ -158,6 +156,32 @@ class PlantGrowthDashboard: ttk.Button(control_frame, text="🔄 Set to Default", command=self.set_to_default).grid(row=row, column=0, columnspan=2, pady=(10, 0), sticky=(tk.W, tk.E)) + row += 1 + + # ADD EMPTY SPACER + spacer = ttk.Label(control_frame, text="") + spacer.grid(row=row, column=0, columnspan=2, pady=10) + + # Add this after the parameters section + ttk.Label(control_frame, text="Final date of growth:", + font=('Arial', 9, 'bold')).grid(row=row, column=0, columnspan=2, sticky=tk.W, pady=(10, 4)) + + row += 1 + # Compact date entry with calendar popup + # To get the selected dat simply call self.date_entry.get_date() + self.date_entry = DateEntry(control_frame, + width=12, + background='darkblue', + foreground='white', + borderwidth=2, + font=('Arial', 8), + date_pattern='dd/mm/yyyy', + state='readonly', # Add this + cursor='hand2') # Add this + self.date_entry.grid(row=row, column=0, columnspan=2, sticky=tk.W, pady=2) + + row += 1 + control_frame.columnconfigure(0, weight=1) control_frame.columnconfigure(1, weight=1) @@ -218,29 +242,57 @@ class PlantGrowthDashboard: command=self.submit_plant_data).pack(fill=tk.X) def setup_plant_evolution_tab(self, notebook): - """Evolution tab""" + """Evolution tab with single image display""" evolution_frame = ttk.Frame(notebook) notebook.add(evolution_frame, text="🌿 Growth Evolution") - # Create scrollable frame for plant images - canvas_scroll = tk.Canvas(evolution_frame) - scrollbar = ttk.Scrollbar(evolution_frame, orient="vertical", command=canvas_scroll.yview) - self.scrollable_frame = ttk.Frame(canvas_scroll) + # Create main container for image display + self.image_display_frame = ttk.Frame(evolution_frame) + self.image_display_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) - self.scrollable_frame.bind( - "", - lambda e: canvas_scroll.configure(scrollregion=canvas_scroll.bbox("all")) + # Create label for image or fallback text + self.evolution_image_label = ttk.Label( + self.image_display_frame, + text="Plant state after the prediction will appear here", + font=('Arial', 12), + foreground='gray', + anchor='center' ) - - canvas_scroll.create_window((0, 0), window=self.scrollable_frame, anchor="nw") - canvas_scroll.configure(yscrollcommand=scrollbar.set) - - canvas_scroll.pack(side="left", fill="both", expand=True) - scrollbar.pack(side="right", fill="y") - - self.image_display_frame = ttk.Frame(self.scrollable_frame) - self.image_display_frame.pack(fill=tk.BOTH, expand=True, padx=6, pady=6) - + self.evolution_image_label.pack(expand=True) + + def update_evolution_image(self, filename=None): + """Update the evolution tab with an image from file or show fallback text""" + if filename and os.path.exists(filename): + try: + # Load and display the image + from PIL import Image, ImageTk + + # Open and resize image if needed + pil_image = Image.open(filename) + # Optional: resize to fit the display area + pil_image = pil_image.resize((400, 300), Image.Resampling.LANCZOS) + + # Convert to PhotoImage for tkinter + photo_image = ImageTk.PhotoImage(pil_image) + + # Display the image + self.evolution_image_label.config(image=photo_image, text="") + self.evolution_image_label.image = photo_image # Keep reference to prevent garbage collection + + except Exception as e: + print(f"Error loading image {filename}: {e}") + # Show fallback text on error + self.evolution_image_label.config( + image="", + text="Plant state after the prediction will appear here" + ) + else: + # Show fallback text when no filename or file doesn't exist + self.evolution_image_label.config( + image="", + text="Plant state after the prediction will appear here" + ) + def setup_results_panel(self, parent): results_frame = ttk.LabelFrame(parent, text="Growth Prediction", padding="6") results_frame.grid(row=1, column=2, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(6, 0)) @@ -249,15 +301,39 @@ class PlantGrowthDashboard: ttk.Label(results_frame, text="Forecast Results:", font=('Arial', 9, 'bold')).grid(row=0, column=0, sticky=tk.W, pady=(0, 4)) - self.results_text = tk.Text(results_frame, height=20, width=26, wrap=tk.WORD, font=('Arial', 8)) + self.results_text = tk.Text(results_frame, height=10, width=26, wrap=tk.WORD, font=('Arial', 8), state='disabled') self.results_text.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) + img = Image.open("public/TransparentFlower.png") + + # Resize if needed + img = img.resize((180, 290), Image.Resampling.LANCZOS) + photo = ImageTk.PhotoImage(img) + + static_image_label = ttk.Label(results_frame, image=photo) + static_image_label.image = photo # Keep reference + static_image_label.grid(row=2, column=0, pady=(6, 0)) + results_frame.columnconfigure(0, weight=1) results_frame.rowconfigure(1, weight=1) def on_mode_change(self): - self.update_prediction() + #KEY FOCUS IS TO IMPLEMENT THIS PART + """Handle mode changes""" + current_mode = self.ambient_mode.get() + if current_mode == "controlled": + print("Switched to Controlled mode") + # Enable all parameter controls + # No need to call the meteo api + + elif current_mode == "open": + print("Switched to Open mode") + # Disable most parameter controls (temp, humidity, light) + + # Call the meteo api to retrieve all the parameters, set variable meteo_values to retrieve when submitiing all + # Inside the retrieving of all the data check if the mode is one, select which data to use + def on_param_change(self, param): value = self.env_params[param].get() @@ -269,19 +345,16 @@ class PlantGrowthDashboard: value_label.config(text=f"{value:.0f}") else: value_label.config(text=f"{value:.1f}") - - # Auto-update prediction when parameters change - self.update_prediction() def set_to_default(self): """Reset all parameters to default values""" for param_name, default_value in self.default_params.items(): self.env_params[param_name].set(default_value) - self.update_parameter_label(param_name, default_value) self.ambient_mode.set("controlled") - self.update_prediction() + + self.date_entry.set_date(date.today()) def update_parameter_label(self, param, value): """Update the value label for a specific parameter""" @@ -302,6 +375,7 @@ class PlantGrowthDashboard: def load_baseline_image(self): self.results_text.delete(1.0, tk.END) + self.set_to_default() file_path = filedialog.askopenfilename( title="Select baseline plant image", @@ -309,7 +383,6 @@ class PlantGrowthDashboard: ) if file_path: self.baseline_image_path = file_path - self.update_prediction() def submit_plant_data(self): """Submit plant information and photo""" @@ -324,7 +397,9 @@ class PlantGrowthDashboard: 'timestamp': datetime.now().isoformat(), 'parameters': params, 'baseline_image_path': self.baseline_image_path, - 'plant_info': self.plant_info_text.get(1.0, tk.END) + 'plant_info': self.plant_info_text.get(1.0, tk.END), + 'start date': datetime.now().date().isoformat(), + 'end_date': self.date_entry.get_date().isoformat() } #Remove plant_info_text @@ -339,34 +414,35 @@ class PlantGrowthDashboard: json.dump(submission_data, f, indent=4) # Here call the bot pipeline to store results on files in plant_data + # results are in the form of (text, image) + results = "come bot please" + + text = getattr(results, 'text', None) + image_filename = getattr(results, 'image', None) + images_dir = "./plant_data" + os.makedirs(data_dir, exist_ok=True) + image_path = os.path.join(images_dir, image_filename) + self.updating_evolution_and_forecasts(text, image_path) # Here update the informations in the last box from plant_data/texts # Here update the informations in growth evolution from plant_data/images + #Calling a small advertment to notify the user that the image has been generated + messagebox.showinfo("Submission successful, go to growth evolution to see the results") print(f"Submission data saved to: {filepath}") except Exception as e: messagebox.showerror("Submission Error", f"Error submitting data: {str(e)}") - - def update_prediction(self): - try: - # Get current parameters - params = {param: var.get() for param, var in self.env_params.items()} - params['plant_type'] = self.current_plant - params['ambient_mode'] = self.ambient_mode.get() - - # Generate prediction - prediction = self.plant_model.predict_growth(params) - - # Update initial plant display - self.update_initial_plant_display() - - # Update results text - self.update_results_display(prediction) - - except Exception as e: - messagebox.showerror("Prediction Error", f"Error generating prediction: {str(e)}") - + + def updating_evolution_and_forecasts(self, text, image_path): + self.results_text.config(state='normal') # Enable editing + self.results_text.delete(1.0, tk.END) # Clear existing content + if text != None: + self.results_text.insert(1.0, text) # Insert new text + self.results_text.config(state='disabled') # Disable editing again + + self.update_evolution_image(image_path) + def update_initial_plant_display(self): """Update the initial plant state display""" try: diff --git a/path.txt b/path.txt index cb50234..ad13395 100644 --- a/path.txt +++ b/path.txt @@ -1,35 +1,18 @@ ROADMAP - -understand codebase - - -add the possibility to retrieve all the data when the user click the submit button - -format the data in an usable format -differences between the three modes: Open mode: -the temp, humidity, light, water parameters are setted by the meteo api, the rest is setted by the user - + -all the parameters are controlled by the user SemiControlled mode: -the user choose how to set the parameters + -all parameters free Controlled mode: -all the values are set by the user - - - -default modes are setted with general values in an optimal condition for your plant + -the user choose which parameters are free and which are controlled by the meteo api - -The forecast results will have as input the text description obtained by the model - (the output will be in the text folder inside data/texts folder, format "date1-date2-enum.txt") - -The growth evolution will have as input the photo/s obtained by the model - (the output will be in the text folder inside data/images folder, format "date1-date2-enum.jpg) - - -set the value of light as Daily Light Integral (DLI) - - -forecast for a period of time: - -create a calendar widget with start and end data - -based on how much does it take to create an image, for each subperiod of time create a new image to show - (it this will be coded add all a button to iterate over the folder) - - -when the user click the load plant image, all previous record will be eliminated + -make the calendar widget working -final updates of README.md, hackathon page, forgejo, github page, small design adjustments.