import tkinter as tk from tkinter import ttk import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import seaborn as sns import numpy as np from PIL import Image, ImageDraw, ImageFont import random import math from datetime import datetime class PlantGrowthGraphicsDemo: def __init__(self, root): self.root = root self.root.title("๐ŸŒฑ Plant Growth Graphics Demo") self.root.geometry("1400x900") self.root.configure(bg='#f0f0f0') # Demo variables self.current_plant = tk.StringVar(value="tomato") self.ambient_mode = tk.StringVar(value="controlled") # Environmental parameters for demo self.env_params = { 'temperature': tk.DoubleVar(value=22.0), 'humidity': tk.DoubleVar(value=65.0), 'soil_acidity': tk.DoubleVar(value=6.5), 'pressure': tk.DoubleVar(value=1013.25), 'brightness': tk.DoubleVar(value=50.0), 'nutrients': tk.DoubleVar(value=75.0), 'water': tk.DoubleVar(value=80.0), 'co2': tk.DoubleVar(value=40.0) } # Plant colors for visualization self.plant_colors = { 'tomato': {'stem': '#228B22', 'leaf': '#32CD32', 'fruit': '#FF6347'}, 'basil': {'stem': '#228B22', 'leaf': '#90EE90', 'fruit': '#FFFFFF'}, 'mint': {'stem': '#228B22', 'leaf': '#98FB98', 'fruit': '#FFFFFF'}, 'lettuce': {'stem': '#228B22', 'leaf': '#ADFF2F', 'fruit': '#FFFFFF'}, 'rosemary': {'stem': '#8B4513', 'leaf': '#556B2F', 'fruit': '#FFFFFF'}, 'strawberry': {'stem': '#228B22', 'leaf': '#32CD32', 'fruit': '#FF1493'} } self.setup_ui() self.update_all_graphics() # Auto-update timer for dynamic effects self.auto_update() def setup_ui(self): # Main container main_frame = ttk.Frame(self.root, padding="10") main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # Configure grid weights self.root.columnconfigure(0, weight=1) self.root.rowconfigure(0, weight=1) main_frame.columnconfigure(1, weight=1) main_frame.rowconfigure(1, weight=1) # Title with animated effect self.title_label = ttk.Label(main_frame, text="๐ŸŒฑ Plant Growth Graphics Demo", font=('Arial', 16, 'bold')) self.title_label.grid(row=0, column=0, columnspan=3, pady=(0, 20)) # Left panel - Controls self.setup_control_panel(main_frame) # Center panel - Main Visualization self.setup_main_visualization(main_frame) # Right panel - Additional Graphics self.setup_additional_graphics(main_frame) def setup_control_panel(self, parent): control_frame = ttk.LabelFrame(parent, text="๐ŸŽ›๏ธ Graphics Controls", padding="10") control_frame.grid(row=1, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(0, 10)) # Plant selection with immediate visual feedback ttk.Label(control_frame, text="Plant Type:", font=('Arial', 10, 'bold')).grid(row=0, column=0, sticky=tk.W, pady=5) plant_combo = ttk.Combobox(control_frame, textvariable=self.current_plant, values=["tomato", "basil", "mint", "lettuce", "rosemary", "strawberry"]) plant_combo.grid(row=0, column=1, sticky=(tk.W, tk.E), pady=5) plant_combo.bind('<>', self.on_plant_change) # Ambient mode with visual indicators ttk.Label(control_frame, text="Visual Mode:", font=('Arial', 10, 'bold')).grid(row=1, column=0, sticky=tk.W, pady=5) mode_frame = ttk.Frame(control_frame) mode_frame.grid(row=1, column=1, sticky=(tk.W, tk.E), pady=5) ttk.Radiobutton(mode_frame, text="๐ŸŽฏ Controlled", variable=self.ambient_mode, value="controlled", command=self.update_all_graphics).pack(side=tk.LEFT) ttk.Radiobutton(mode_frame, text="๐ŸŒŠ Dynamic", variable=self.ambient_mode, value="semi-controlled", command=self.update_all_graphics).pack(side=tk.LEFT) ttk.Radiobutton(mode_frame, text="๐ŸŒช๏ธ Chaotic", variable=self.ambient_mode, value="open", command=self.update_all_graphics).pack(side=tk.LEFT) # Visual effects controls ttk.Separator(control_frame, orient='horizontal').grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=10) ttk.Label(control_frame, text="๐ŸŽจ Visual Parameters:", font=('Arial', 10, 'bold')).grid(row=3, column=0, columnspan=2, pady=(10, 5)) # Parameter sliders with real-time visual updates param_labels = { 'temperature': '๐ŸŒก๏ธ Temperature', 'humidity': '๐Ÿ’ง Humidity', 'soil_acidity': '๐Ÿงช Soil pH', 'pressure': '๐ŸŒฌ๏ธ Pressure', 'brightness': 'โ˜€๏ธ Light', 'nutrients': '๐ŸŒฟ Nutrients', 'water': '๐Ÿ’ฆ Water', 'co2': '๐Ÿซง CO2' } row = 4 for param, label in param_labels.items(): ttk.Label(control_frame, text=label).grid(row=row, column=0, sticky=tk.W, pady=2) # Create frame for slider and value display slider_frame = ttk.Frame(control_frame) slider_frame.grid(row=row, column=1, sticky=(tk.W, tk.E), pady=2) scale = ttk.Scale(slider_frame, from_=0, to=100, 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) # Value display value_label = ttk.Label(slider_frame, text="0", width=4) value_label.pack(side=tk.RIGHT) # Store reference for updates setattr(self, f"{param}_label", value_label) row += 1 # Action buttons with visual feedback button_frame = ttk.Frame(control_frame) button_frame.grid(row=row, column=0, columnspan=2, pady=20) ttk.Button(button_frame, text="๐ŸŽฌ Animate Growth", command=self.animate_growth).pack(fill=tk.X, pady=2) ttk.Button(button_frame, text="๐ŸŽฒ Randomize", command=self.randomize_parameters).pack(fill=tk.X, pady=2) ttk.Button(button_frame, text="๐Ÿ”„ Reset Demo", command=self.reset_demo).pack(fill=tk.X, pady=2) control_frame.columnconfigure(1, weight=1) def setup_main_visualization(self, parent): viz_frame = ttk.LabelFrame(parent, text="๐Ÿ“Š Main Visualization", padding="10") viz_frame.grid(row=1, column=1, sticky=(tk.W, tk.E, tk.N, tk.S), padx=5) # Notebook for different visualization modes self.notebook = ttk.Notebook(viz_frame) self.notebook.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # Growth Chart Tab self.setup_growth_chart_tab() # Plant Evolution Tab self.setup_plant_evolution_tab() # Parameter Heatmap Tab self.setup_heatmap_tab() # 3D Visualization Tab self.setup_3d_visualization_tab() viz_frame.columnconfigure(0, weight=1) viz_frame.rowconfigure(0, weight=1) def setup_growth_chart_tab(self): chart_frame = ttk.Frame(self.notebook) self.notebook.add(chart_frame, text="๐Ÿ“ˆ Growth Chart") # Create matplotlib figure self.fig, self.ax = plt.subplots(figsize=(8, 6)) self.fig.patch.set_facecolor('#f0f0f0') self.canvas = FigureCanvasTkAgg(self.fig, chart_frame) self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) def setup_plant_evolution_tab(self): evolution_frame = ttk.Frame(self.notebook) self.notebook.add(evolution_frame, text="๐ŸŒฑ Plant Evolution") # Create scrollable frame for plant images canvas_frame = tk.Canvas(evolution_frame, bg='white') scrollbar = ttk.Scrollbar(evolution_frame, orient="vertical", command=canvas_frame.yview) self.scrollable_frame = ttk.Frame(canvas_frame) self.scrollable_frame.bind( "", lambda e: canvas_frame.configure(scrollregion=canvas_frame.bbox("all")) ) canvas_frame.create_window((0, 0), window=self.scrollable_frame, anchor="nw") canvas_frame.configure(yscrollcommand=scrollbar.set) canvas_frame.pack(side="left", fill="both", expand=True) scrollbar.pack(side="right", fill="y") # Plant evolution display self.plant_images_frame = ttk.Frame(self.scrollable_frame) self.plant_images_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10) def setup_heatmap_tab(self): heatmap_frame = ttk.Frame(self.notebook) self.notebook.add(heatmap_frame, text="๐Ÿ”ฅ Parameter Heatmap") # Create seaborn heatmap self.heatmap_fig, self.heatmap_ax = plt.subplots(figsize=(8, 6)) self.heatmap_fig.patch.set_facecolor('#f0f0f0') self.heatmap_canvas = FigureCanvasTkAgg(self.heatmap_fig, heatmap_frame) self.heatmap_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) def setup_3d_visualization_tab(self): viz_3d_frame = ttk.Frame(self.notebook) self.notebook.add(viz_3d_frame, text="๐ŸŽฏ 3D Analysis") # Create 3D plot self.fig_3d = plt.figure(figsize=(8, 6)) self.fig_3d.patch.set_facecolor('#f0f0f0') self.ax_3d = self.fig_3d.add_subplot(111, projection='3d') self.canvas_3d = FigureCanvasTkAgg(self.fig_3d, viz_3d_frame) self.canvas_3d.get_tk_widget().pack(fill=tk.BOTH, expand=True) def setup_additional_graphics(self, parent): additional_frame = ttk.LabelFrame(parent, text="๐Ÿ“‹ Live Stats & Info", padding="10") additional_frame.grid(row=1, column=2, sticky=(tk.W, tk.E, tk.N, tk.S), padx=(10, 0)) # Real-time statistics display stats_frame = ttk.LabelFrame(additional_frame, text="๐Ÿ“Š Live Statistics", padding="5") stats_frame.pack(fill=tk.X, pady=(0, 10)) self.stats_text = tk.Text(stats_frame, height=8, width=25, wrap=tk.WORD, font=('Courier', 9), bg='#f8f9fa') self.stats_text.pack(fill=tk.BOTH, expand=True) # Visual health indicator health_frame = ttk.LabelFrame(additional_frame, text="๐Ÿฅ Plant Health", padding="5") health_frame.pack(fill=tk.X, pady=(0, 10)) self.health_canvas = tk.Canvas(health_frame, height=100, bg='white') self.health_canvas.pack(fill=tk.X) # Parameter radar chart radar_frame = ttk.LabelFrame(additional_frame, text="๐ŸŽฏ Parameter Radar", padding="5") radar_frame.pack(fill=tk.BOTH, expand=True) self.radar_fig, self.radar_ax = plt.subplots(figsize=(4, 4), subplot_kw=dict(projection='polar')) self.radar_fig.patch.set_facecolor('#f0f0f0') self.radar_canvas = FigureCanvasTkAgg(self.radar_fig, radar_frame) self.radar_canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True) def on_plant_change(self, event=None): self.update_all_graphics() def on_param_change(self, param): # Update value display value = self.env_params[param].get() label = getattr(self, f"{param}_label") label.config(text=f"{value:.1f}") # Update graphics in real-time self.update_all_graphics() def update_all_graphics(self): self.update_growth_chart() self.update_plant_evolution() self.update_parameter_heatmap() self.update_3d_visualization() self.update_statistics() self.update_health_indicator() self.update_radar_chart() def update_growth_chart(self): self.ax.clear() # Generate mock growth data based on parameters days = np.arange(0, 100, 1) # Base growth influenced by parameters temp_factor = self.env_params['temperature'].get() / 100 water_factor = self.env_params['water'].get() / 100 light_factor = self.env_params['brightness'].get() / 100 # Create realistic growth curve growth_rate = (temp_factor + water_factor + light_factor) / 3 if self.ambient_mode.get() == "open": # Add chaos/randomness noise = np.random.normal(0, 0.1, len(days)) heights = np.cumsum(np.maximum(0, growth_rate + noise)) * 2 elif self.ambient_mode.get() == "semi-controlled": # Add some variation noise = np.random.normal(0, 0.05, len(days)) heights = np.cumsum(np.maximum(0, growth_rate + noise)) * 2 else: # Controlled growth heights = np.cumsum([growth_rate] * len(days)) * 2 # Apply plant-specific characteristics plant_multipliers = { 'tomato': 1.5, 'basil': 0.8, 'mint': 0.6, 'lettuce': 0.4, 'rosemary': 1.2, 'strawberry': 0.5 } multiplier = plant_multipliers.get(self.current_plant.get(), 1.0) heights = heights * multiplier # Plot with plant-specific colors colors = self.plant_colors[self.current_plant.get()] self.ax.plot(days, heights, color=colors['leaf'], linewidth=2, marker='o', markersize=2) # Add growth phases with different colors phase_colors = ['lightblue', 'lightgreen', 'lightyellow', 'lightcoral'] phase_names = ['Germination', 'Seedling', 'Vegetative', 'Mature'] for i, (color, name) in enumerate(zip(phase_colors, phase_names)): start_day = i * 25 end_day = (i + 1) * 25 if end_day <= len(days): self.ax.axvspan(start_day, end_day, alpha=0.3, color=color, label=name) self.ax.set_xlabel('Days') self.ax.set_ylabel('Height (cm)') self.ax.set_title(f'{self.current_plant.get().title()} Growth Simulation') self.ax.grid(True, alpha=0.3) self.ax.legend(loc='upper left') # Add current parameter indicators current_day = int(self.env_params['temperature'].get()) if current_day < len(heights): self.ax.axvline(current_day, color='red', linestyle='--', alpha=0.7, label='Current Day') self.ax.plot(current_day, heights[current_day], 'ro', markersize=8) self.canvas.draw() def update_plant_evolution(self): # Clear previous images for widget in self.plant_images_frame.winfo_children(): widget.destroy() # Generate evolution stages stages = ['Seed', 'Sprout', 'Young', 'Mature', 'Full Growth'] for i, stage in enumerate(stages): stage_frame = ttk.Frame(self.plant_images_frame) stage_frame.pack(fill=tk.X, pady=5) # Stage label ttk.Label(stage_frame, text=f"Stage {i+1}: {stage}", font=('Arial', 10, 'bold')).pack() # Generate plant image plant_image = self.generate_plant_stage_image(i+1) # Convert to PhotoImage and display photo = tk.PhotoImage(data=self.pil_to_tk_data(plant_image)) image_label = tk.Label(stage_frame, image=photo) image_label.image = photo # Keep reference image_label.pack() def generate_plant_stage_image(self, stage): """Generate a plant image for a specific growth stage""" img = Image.new('RGB', (200, 150), color='#87CEEB') # Sky blue draw = ImageDraw.Draw(img) # Draw ground ground_y = 130 draw.rectangle([0, ground_y, 200, 150], fill='#8B4513') # Brown ground # Get plant colors colors = self.plant_colors[self.current_plant.get()] # Calculate plant size based on stage and parameters health_factor = (sum(param.get() for param in self.env_params.values()) / len(self.env_params)) / 100 plant_height = stage * 15 * health_factor plant_width = plant_height * 0.6 center_x = 100 base_y = ground_y top_y = base_y - plant_height # Draw stem stem_width = max(2, int(plant_height * 0.1)) draw.rectangle([center_x - stem_width//2, int(top_y), center_x + stem_width//2, base_y], fill=colors['stem']) # Draw leaves num_leaves = min(stage * 2, 8) for i in range(num_leaves): leaf_y = base_y - (i + 1) * (plant_height / (num_leaves + 1)) side = 1 if i % 2 == 0 else -1 leaf_x = center_x + side * (plant_width * 0.3) leaf_size = plant_width * 0.2 # Draw leaf based on plant type if self.current_plant.get() == 'lettuce': # Broad leaves draw.ellipse([leaf_x - leaf_size, leaf_y - leaf_size//2, leaf_x + leaf_size, leaf_y + leaf_size//2], fill=colors['leaf']) else: # Regular leaves draw.ellipse([leaf_x - leaf_size//2, leaf_y - leaf_size//3, leaf_x + leaf_size//2, leaf_y + leaf_size//3], fill=colors['leaf']) # Draw fruits for mature stages if stage >= 4 and self.current_plant.get() in ['tomato', 'strawberry']: fruit_color = colors['fruit'] for i in range(min(stage - 3, 3)): fruit_x = center_x + random.randint(-15, 15) fruit_y = int(top_y + random.randint(10, int(plant_height//2))) fruit_size = 5 + stage draw.ellipse([fruit_x - fruit_size, fruit_y - fruit_size, fruit_x + fruit_size, fruit_y + fruit_size], fill=fruit_color) # Add stage indicator draw.text((5, 5), f"Stage {stage}", fill='black') return img def pil_to_tk_data(self, pil_image): """Convert PIL image to tkinter PhotoImage data""" import io import base64 # Convert to PNG bytes buffer = io.BytesIO() pil_image.save(buffer, format='PNG') # Encode to base64 img_data = base64.b64encode(buffer.getvalue()) return img_data def update_parameter_heatmap(self): self.heatmap_ax.clear() # Create parameter correlation matrix param_names = list(self.env_params.keys()) param_values = [param.get() for param in self.env_params.values()] # Create a correlation-like matrix for visualization matrix = np.zeros((len(param_names), len(param_names))) for i, val_i in enumerate(param_values): for j, val_j in enumerate(param_values): if i == j: matrix[i][j] = val_i else: # Create interesting correlations correlation = abs(val_i - val_j) / 100 matrix[i][j] = correlation * 50 # Create heatmap sns.heatmap(matrix, annot=True, fmt='.1f', cmap='RdYlGn', xticklabels=[name.replace('_', ' ').title() for name in param_names], yticklabels=[name.replace('_', ' ').title() for name in param_names], ax=self.heatmap_ax, cbar_kws={'label': 'Parameter Intensity'}) self.heatmap_ax.set_title(f'{self.current_plant.get().title()} Parameter Heatmap') plt.setp(self.heatmap_ax.get_xticklabels(), rotation=45, ha='right') plt.setp(self.heatmap_ax.get_yticklabels(), rotation=0) self.heatmap_canvas.draw() def update_3d_visualization(self): self.ax_3d.clear() # Create 3D scatter plot of parameters temp = self.env_params['temperature'].get() humidity = self.env_params['humidity'].get() light = self.env_params['brightness'].get() # Generate some sample data points around current parameters n_points = 50 temps = np.random.normal(temp, 5, n_points) humids = np.random.normal(humidity, 5, n_points) lights = np.random.normal(light, 5, n_points) # Color points based on "health" (distance from optimal) optimal_temp, optimal_humid, optimal_light = 25, 60, 50 distances = np.sqrt((temps - optimal_temp)**2 + (humids - optimal_humid)**2 + (lights - optimal_light)**2) colors = plt.cm.RdYlGn_r(distances / distances.max()) scatter = self.ax_3d.scatter(temps, humids, lights, c=colors, s=50, alpha=0.7) # Highlight current point self.ax_3d.scatter([temp], [humidity], [light], c='red', s=200, marker='*') self.ax_3d.set_xlabel('Temperature (ยฐC)') self.ax_3d.set_ylabel('Humidity (%)') self.ax_3d.set_zlabel('Light Intensity') self.ax_3d.set_title(f'3D Parameter Space - {self.current_plant.get().title()}') self.canvas_3d.draw() def update_statistics(self): self.stats_text.delete(1.0, tk.END) # Calculate mock statistics params = {name: param.get() for name, param in self.env_params.items()} avg_param = sum(params.values()) / len(params) health_score = min(100, avg_param * 1.2) growth_rate = health_score / 20 stats_text = f"""๐ŸŒฑ PLANT STATISTICS {'='*25} Plant Type: {self.current_plant.get().title()} Mode: {self.ambient_mode.get().title()} ๐Ÿ“Š Current Metrics: Health Score: {health_score:.1f}% Growth Rate: {growth_rate:.2f} cm/day Avg Parameter: {avg_param:.1f} ๐ŸŒก๏ธ Environment: Temperature: {params['temperature']:.1f}ยฐC Humidity: {params['humidity']:.1f}% Soil pH: {params['soil_acidity']:.1f} Light: {params['brightness']:.1f} lux ๐Ÿ’ง Resources: Water: {params['water']:.1f}% Nutrients: {params['nutrients']:.1f}% CO2: {params['co2']:.1f} ppm โฐ Updated: {datetime.now().strftime('%H:%M:%S')} """ self.stats_text.insert(1.0, stats_text) def update_health_indicator(self): self.health_canvas.delete("all") # Calculate health score params = list(self.env_params.values()) health_score = sum(param.get() for param in params) / len(params) # Draw health bar bar_width = 180 bar_height = 20 x_start = 10 y_start = 40 # Background self.health_canvas.create_rectangle(x_start, y_start, x_start + bar_width, y_start + bar_height, fill='lightgray', outline='black') # Health bar health_width = (health_score / 100) * bar_width if health_score > 70: color = 'green' elif health_score > 40: color = 'orange' else: color = 'red' self.health_canvas.create_rectangle(x_start, y_start, x_start + health_width, y_start + bar_height, fill=color, outline='') # Health text self.health_canvas.create_text(100, 25, text=f"Health: {health_score:.1f}%", font=('Arial', 12, 'bold')) # Status emoji if health_score > 80: emoji = "๐ŸŒŸ" status = "Excellent" elif health_score > 60: emoji = "๐Ÿ˜Š" status = "Good" elif health_score > 40: emoji = "๐Ÿ˜" status = "Fair" else: emoji = "๐Ÿ˜Ÿ" status = "Poor" self.health_canvas.create_text(100, 75, text=f"{emoji} {status}", font=('Arial', 10)) def update_radar_chart(self): self.radar_ax.clear() # Parameter names and values param_names = ['Temp', 'Humid', 'pH', 'Press', 'Light', 'Nutri', 'Water', 'CO2'] param_values = [param.get() for param in self.env_params.values()] # Number of variables N = len(param_names) # Compute angle for each axis angles = [n / float(N) * 2 * np.pi for n in range(N)] angles += angles[:1] # Complete the circle # Add values param_values += param_values[:1] # Complete the circle # Plot self.radar_ax.plot(angles, param_values, 'o-', linewidth=2, color=self.plant_colors[self.current_plant.get()]['leaf']) self.radar_ax.fill(angles, param_values, alpha=0.25, color=self.plant_colors[self.current_plant.get()]['leaf']) # Add labels self.radar_ax.set_xticks(angles[:-1]) self.radar_ax.set_xticklabels(param_names) self.radar_ax.set_ylim(0, 100) self.radar_ax.set_title(f'{self.current_plant.get().title()} Parameters', pad=20, fontsize=10) self.radar_ax.grid(True) self.radar_canvas.draw() def animate_growth(self): """Animate parameter changes to show dynamic growth""" def animate_step(step): if step < 50: # 50 animation steps # Gradually change parameters for param in self.env_params.values(): current = param.get() target = random.uniform(20, 80) new_value = current + (target - current) * 0.1 param.set(new_value) self.update_all_graphics() self.root.after(100, lambda: animate_step(step + 1)) animate_step(0) def randomize_parameters(self): """Randomize all parameters for demo purposes""" for param in self.env_params.values(): param.set(random.uniform(10, 90)) self.update_all_graphics() def reset_demo(self): """Reset all parameters to default values""" defaults = { 'temperature': 22.0, 'humidity': 65.0, 'soil_acidity': 6.5, 'pressure': 50.0, # Normalized for demo 'brightness': 50.0, 'nutrients': 75.0, 'water': 80.0, 'co2': 40.0 } for param_name, default_value in defaults.items(): self.env_params[param_name].set(default_value) self.current_plant.set("tomato") self.ambient_mode.set("controlled") self.update_all_graphics() def auto_update(self): """Auto-update for dynamic effects""" if self.ambient_mode.get() == "open": # Add small random variations in open mode for param in self.env_params.values(): current = param.get() variation = random.uniform(-1, 1) new_value = max(0, min(100, current + variation)) param.set(new_value) self.update_all_graphics() # Schedule next update self.root.after(2000, self.auto_update) # Update every 2 seconds def main(): root = tk.Tk() app = PlantGrowthGraphicsDemo(root) root.mainloop() if __name__ == "__main__": main()