diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..4a50b42 Binary files /dev/null and b/.DS_Store differ diff --git a/PlantDashboard/__pycache__/main_dashboard.cpython-313.pyc b/PlantDashboard/__pycache__/main_dashboard.cpython-313.pyc new file mode 100644 index 0000000..287e694 Binary files /dev/null and b/PlantDashboard/__pycache__/main_dashboard.cpython-313.pyc differ diff --git a/PlantDashboard/main_dashboard.py b/PlantDashboard/main_dashboard.py index be5fcf1..fca9b7d 100644 --- a/PlantDashboard/main_dashboard.py +++ b/PlantDashboard/main_dashboard.py @@ -1,17 +1,11 @@ import tkinter as tk from tkinter import ttk, filedialog, messagebox -import matplotlib.pyplot as plt -from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg -import pandas as pd -import numpy as np from PIL import Image, ImageTk import json import os -from datetime import datetime, timedelta -import joblib +from datetime import datetime from plant_model import PlantGrowthModel from data_handler import DataHandler -from image_generator import ImageGenerator class PlantGrowthDashboard: def __init__(self, root): @@ -29,7 +23,6 @@ class PlantGrowthDashboard: # Initialize components self.plant_model = PlantGrowthModel() self.data_handler = DataHandler() - self.image_generator = ImageGenerator() # Variables - fixed plant type self.current_plant = "tomato" # Fixed plant type @@ -110,10 +103,9 @@ class PlantGrowthDashboard: ttk.Button(control_frame, text="šŸ“· Load Plant Image", command=self.load_baseline_image).grid(row=2, column=0, columnspan=2, pady=(0, 10), sticky=(tk.W, tk.E)) - # Environmental parameters ttk.Label(control_frame, text="Parameters:", - font=('Arial', 9, 'bold')).grid(row=3, column=0, columnspan=2, sticky=tk.W, pady=(0, 4)) - + font=('Arial', 9, 'bold')).grid(row=3, column=0, columnspan=2, sticky=tk.W, pady=(0, 4)) + param_labels = { 'temperature': 'šŸŒ”ļø Temp (°C)', 'humidity': 'šŸ’§ Humidity (%)', @@ -124,7 +116,19 @@ class PlantGrowthDashboard: 'water': 'šŸ’¦ Water (%)', 'co2': '🫧 CO2' } - + + # Define bounds for each parameter (min, max) + param_bounds = { + 'temperature': (10, 40), + 'humidity': (20, 90), + 'soil_acidity': (4.0, 9.0), + 'pressure': (950, 1100), + 'brightness': (100, 100000), + 'nutrients': (0, 100), + 'water': (10, 100), + 'co2': (300, 1000) + } + row = 4 for param, label in param_labels.items(): # Compact parameter layout @@ -133,21 +137,12 @@ class PlantGrowthDashboard: ttk.Label(param_frame, text=label, width=11, font=('Arial', 8)).pack(side=tk.LEFT) - # Scale ranges based on parameter type - if param == 'co2': - scale_max = 1000 - elif param == 'brightness': - scale_max = 100000 - elif param == 'pressure': - scale_max = 1100 - elif param == 'soil_acidity': - scale_max = 14 - else: - scale_max = 100 - - scale = ttk.Scale(param_frame, from_=0, to=scale_max, - variable=self.env_params[param], orient=tk.HORIZONTAL, - command=lambda x, p=param: self.on_param_change(p)) + # Get bounds for this parameter + scale_min, scale_max = param_bounds.get(param, (0, 100)) + + scale = ttk.Scale(param_frame, from_=scale_min, to=scale_max, + variable=self.env_params[param], orient=tk.HORIZONTAL, + command=lambda x, p=param: self.on_param_change(p)) scale.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(4, 4)) # Value label @@ -212,7 +207,7 @@ class PlantGrowthDashboard: info_frame.pack(fill=tk.X, pady=(8, 0)) self.plant_info_text = tk.Text(info_frame, height=8, width=35, wrap=tk.WORD, - font=('Arial', 8), bg='#f8f9fa') + font=('Arial', 8), bg="#000000") self.plant_info_text.pack(fill=tk.BOTH, expand=True) # Submit button @@ -283,10 +278,31 @@ class PlantGrowthDashboard: 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() - + + def update_parameter_label(self, param, value): + """Update the value label for a specific parameter""" + try: + # Get the value label for this parameter + value_label = getattr(self, f"{param}_value_label") + + # Format the value based on parameter type + if param == 'soil_acidity': + value_label.config(text=f"{value:.1f}") + elif param in ['brightness', 'pressure', 'co2']: + value_label.config(text=f"{value:.0f}") + else: + value_label.config(text=f"{value:.1f}") + except AttributeError: + # Handle case where label doesn't exist + print(f"Warning: No label found for parameter {param}") + def load_baseline_image(self): + self.results_text.delete(1.0, tk.END) + file_path = filedialog.askopenfilename( title="Select baseline plant image", filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp *.gif")] @@ -308,15 +324,27 @@ 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), - 'results': self.results_text.get(1.0, tk.END) + 'plant_info': self.plant_info_text.get(1.0, tk.END) } - # Show confirmation dialog - messagebox.showinfo("Submission Successful", - "Plant information and photo have been submitted successfully!\n\n" - f"Submission ID: {datetime.now().strftime('%Y%m%d_%H%M%S')}") + #Remove plant_info_text + self.plant_info_text.delete(1.0, tk.END) + data_dir = "../data" + os.makedirs(data_dir, exist_ok=True) + current_date = datetime.now().strftime('%Y%m%d') + filename = f"{current_date}-{current_date}.txt" + filepath = os.path.join(data_dir, filename) + with open(filepath, 'w') as f: + json.dump(submission_data, f, indent=4) + + # Here call the bot pipeline to store results on files in plant_data + + # Here update the informations in the last box from plant_data/texts + + # Here update the informations in growth evolution from plant_data/images + + print(f"Submission data saved to: {filepath}") except Exception as e: messagebox.showerror("Submission Error", f"Error submitting data: {str(e)}") @@ -331,10 +359,7 @@ class PlantGrowthDashboard: prediction = self.plant_model.predict_growth(params) # Update initial plant display - self.update_initial_plant_display(params, prediction) - - # Generate plant evolution images - self.generate_plant_evolution(params, prediction) + self.update_initial_plant_display() # Update results text self.update_results_display(prediction) @@ -342,125 +367,35 @@ class PlantGrowthDashboard: except Exception as e: messagebox.showerror("Prediction Error", f"Error generating prediction: {str(e)}") - def update_initial_plant_display(self, params, prediction): + def update_initial_plant_display(self): """Update the initial plant state display""" try: + initial_image = None # Generate initial plant image (stage 0) - initial_image = self.image_generator.generate_plant_image( - self.current_plant, 0.5, prediction['health_score'], 0 - ) + try: + if self.baseline_image_path != None and os.path.exists(self.baseline_image_path): + initial_image = Image.open(self.baseline_image_path) + except Exception as e: + print(f"Error loading image from {self.baseline_image_path}: {e}") # Resize image to fit better in square layout - initial_image = initial_image.resize((280, 210), Image.Resampling.LANCZOS) - # Convert to PhotoImage and display - photo = ImageTk.PhotoImage(initial_image) - self.initial_plant_label.configure(image=photo, text="") - self.initial_plant_label.image = photo # Keep reference + if initial_image != None: + initial_image = initial_image.resize((280, 210), Image.Resampling.LANCZOS) - # Update plant information - self.update_plant_info(params, prediction) + # Convert to PhotoImage and display + photo = ImageTk.PhotoImage(initial_image) + self.initial_plant_label.configure(image=photo, text="") + self.initial_plant_label.image = photo # Keep reference except Exception as e: messagebox.showerror("Image Error", f"Could not generate initial plant image: {str(e)}") - - def update_plant_info(self, params, prediction): - """Update plant information display""" - self.plant_info_text.delete(1.0, tk.END) - - info_text = f"""🌱 PLANT STATUS -{'='*22} -Plant Type: Tomato -Health Score: {prediction['health_score']:.1f}% -Growth Rate: {prediction['growth_rate']:.2f} cm/day - -šŸŒ”ļø CONDITIONS: -Temperature: {params['temperature']:.1f}°C -Humidity: {params['humidity']:.1f}% -Soil pH: {params['soil_acidity']:.1f} -Light: {params['brightness']:.0f} lux -Water: {params['water']:.1f}% -Nutrients: {params['nutrients']:.1f}% - -šŸ“Š FORECAST: -Final Height: {prediction['final_height']:.1f} cm -Expected Yield: {prediction.get('yield', 'N/A')} -Optimal Conditions: {prediction['optimal_conditions']:.1f}% -""" - - self.plant_info_text.insert(1.0, info_text) - - def generate_plant_evolution(self, params, prediction): - """Generate and display plant evolution images""" - try: - # Generate plant evolution images - images = self.image_generator.generate_evolution( - self.current_plant, params, prediction, self.baseline_image_path - ) - - if images: - # Clear previous images - for widget in self.image_display_frame.winfo_children(): - widget.destroy() - - # Display evolution stages in a square grid - stages_per_row = 2 # Square layout - for i, image in enumerate(images): - row = i // stages_per_row - col = i % stages_per_row - - stage_frame = ttk.Frame(self.image_display_frame) - stage_frame.grid(row=row, column=col, padx=4, pady=4, sticky=(tk.W, tk.E)) - - # Stage label - ttk.Label(stage_frame, text=f"Stage {i+1}", - font=('Arial', 9, 'bold')).pack() - - # Resize image for compact display - resized_image = image.resize((180, 135), Image.Resampling.LANCZOS) - - # Convert PIL image to PhotoImage - photo = ImageTk.PhotoImage(resized_image) - image_label = tk.Label(stage_frame, image=photo) - image_label.image = photo # Keep a reference - image_label.pack() - - # Configure grid weights for even distribution - for col in range(stages_per_row): - self.image_display_frame.columnconfigure(col, weight=1) - - except Exception as e: - messagebox.showerror("Evolution Error", f"Could not generate evolution images: {str(e)}") - def update_results_display(self, prediction): self.results_text.delete(1.0, tk.END) - results = f"""🌱 GROWTH FORECAST -{'='*18} - -Final Height: {prediction['final_height']:.1f} cm -Growth Rate: {prediction['growth_rate']:.2f} cm/day -Health Score: {prediction['health_score']:.1f}/100 - -šŸ“… Growth Phases: -""" - - for phase in prediction.get('phases', []): - results += f"• {phase['name']}: Days {phase['start']}-{phase['end']}\n" - - results += f"\nāœ… Optimal Conditions: {prediction['optimal_conditions']:.1f}%" - results += f"\nšŸ… Expected Yield: {prediction.get('yield', 'N/A')}" - - # Add environmental summary - results += f"\n\nšŸŒ”ļø ENVIRONMENT SUMMARY:" - results += f"\nTemperature: {self.env_params['temperature'].get():.1f}°C" - results += f"\nHumidity: {self.env_params['humidity'].get():.1f}%" - results += f"\nSoil pH: {self.env_params['soil_acidity'].get():.1f}" - results += f"\nLight Level: {self.env_params['brightness'].get():.0f} lux" - results += f"\nWater Level: {self.env_params['water'].get():.1f}%" - results += f"\nNutrients: {self.env_params['nutrients'].get():.1f}%" - results += f"\nCO2 Level: {self.env_params['co2'].get():.0f} ppm" + #Here update the results display after submitting the photo and the message with the parameters and receveing the output + results = "" self.results_text.insert(1.0, results) diff --git a/PlantDashboard/requirements.txt b/PlantDashboard/requirements.txt index 8059b13..90880d9 100644 --- a/PlantDashboard/requirements.txt +++ b/PlantDashboard/requirements.txt @@ -28,6 +28,8 @@ colorlog>=6.6.0 psutil>=5.8.0 memory-profiler>=0.60.0 +tkcalendar + # Note: tkinter comes pre-installed with most Python distributions # If tkinter is not available, install it using your system package manager: # Ubuntu/Debian: sudo apt-get install python3-tk diff --git a/PlantDashboard/demo_launcher.py b/PlantDashboard/utils/demo_launcher.py similarity index 96% rename from PlantDashboard/demo_launcher.py rename to PlantDashboard/utils/demo_launcher.py index 00db47a..efd1869 100644 --- a/PlantDashboard/demo_launcher.py +++ b/PlantDashboard/utils/demo_launcher.py @@ -49,7 +49,7 @@ def main(): print("\nšŸš€ Starting graphics demo...") try: - from graphics_demo import PlantGrowthGraphicsDemo + from PlantDashboard.utils.graphics_demo import PlantGrowthGraphicsDemo import tkinter as tk root = tk.Tk() diff --git a/PlantDashboard/graphics_demo.py b/PlantDashboard/utils/graphics_demo.py similarity index 100% rename from PlantDashboard/graphics_demo.py rename to PlantDashboard/utils/graphics_demo.py diff --git a/PlantDashboard/install_requirements.py b/PlantDashboard/utils/install_requirements.py similarity index 100% rename from PlantDashboard/install_requirements.py rename to PlantDashboard/utils/install_requirements.py diff --git a/path.txt b/path.txt index 0803001..cb50234 100644 --- a/path.txt +++ b/path.txt @@ -4,7 +4,6 @@ ROADMAP -add the possibility to retrieve all the data when the user click the submit button -format the data in an usable format - -resolve numpy.float64 bug -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 @@ -23,8 +22,6 @@ ROADMAP -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) - -make the plant information as a text field, remove all the connections with other functions - -set the value of light as Daily Light Integral (DLI) -forecast for a period of time: @@ -36,6 +33,3 @@ ROADMAP -final updates of README.md, hackathon page, forgejo, github page, small design adjustments. - -DURING DINNER - -update all on forgejo, hackathon page, forgejo \ No newline at end of file