import os import subprocess from PIL import Image import customtkinter as ctk def parse_float(val_str: str) -> float: """Safely parse a string to float, handling both '.' and ',' as decimal separators.""" if not val_str: return None clean_val = val_str.replace(',', '.') try: return float(clean_val) except ValueError: return None def format_float(val: float, precision: int = 1) -> str: """Format a float for display.""" if val is None: return "" return f"{val:.{precision}f}" def get_bmi_status(bmi): if bmi < 16: return "Severely Underweight", "bmi_severely_underweight.svg", "#3498db" if bmi < 18.5: return "Underweight", "bmi_underweight.svg", "#3498db" if bmi < 25: return "Normal", "bmi_normal.svg", "#2ecc71" if bmi < 30: return "Overweight", "bmi_overweight.svg", "#f1c40f" return "Obese", "bmi_severely_obese.svg", "#e74c3c" def get_bmi_icon(bmi: float, size: tuple = (100, 100)) -> ctk.CTkImage: """ Returns a CTkImage for the given BMI. Converts SVG to PNG using rsvg-convert if needed. """ if bmi is None: return None _, filename, _ = get_bmi_status(bmi) base_dir = os.path.dirname(os.path.abspath(__file__)) assets_dir = os.path.join(base_dir, "assets", "bmi_icons") cache_dir = os.path.join(assets_dir, "png_cache") svg_path = os.path.join(assets_dir, filename) png_filename = filename.replace(".svg", f"_{size[0]}x{size[1]}.png") png_path = os.path.join(cache_dir, png_filename) if not os.path.exists(svg_path): print(f"Warning: Icon not found at {svg_path}") return None if not os.path.exists(cache_dir): os.makedirs(cache_dir) # Convert if PNG doesn't exist if not os.path.exists(png_path): try: subprocess.run([ "rsvg-convert", "-w", str(size[0]), "-h", str(size[1]), "-o", png_path, svg_path ], check=True) except (subprocess.CalledProcessError, FileNotFoundError) as e: print(f"Error converting SVG: {e}") return None try: img = Image.open(png_path) return ctk.CTkImage(light_image=img, dark_image=img, size=size) except Exception as e: print(f"Error loading image: {e}") return None