first round of development
This commit is contained in:
parent
a014734a9d
commit
8df9fa61f3
8 changed files with 82 additions and 151 deletions
BIN
.DS_Store
vendored
Normal file
BIN
.DS_Store
vendored
Normal file
Binary file not shown.
BIN
PlantDashboard/__pycache__/main_dashboard.cpython-313.pyc
Normal file
BIN
PlantDashboard/__pycache__/main_dashboard.cpython-313.pyc
Normal file
Binary file not shown.
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
6
path.txt
6
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
|
Loading…
Add table
Add a link
Reference in a new issue