"""Application composition root.""" from __future__ import annotations import tkinter as tk from .gui import ColorPickerMixin, ExclusionMixin, ThemeMixin, UIBuilderMixin from .i18n import I18nMixin from .logic import DEFAULTS, LANGUAGE, RESET_EXCLUSIONS_ON_IMAGE_CHANGE, ImageProcessingMixin, ResetMixin class ICRAApp( I18nMixin, ThemeMixin, UIBuilderMixin, ImageProcessingMixin, ExclusionMixin, ColorPickerMixin, ResetMixin, ): """Tkinter based application for isolating configurable colour ranges.""" def __init__(self, root: tk.Tk): self.root = root self.init_i18n(LANGUAGE) self.root.title(self._t("app.title")) self._setup_window() # Theme and styling self.init_theme() # Tkinter state variables self.DEFAULTS = DEFAULTS.copy() self.hue_min = tk.DoubleVar(value=self.DEFAULTS["hue_min"]) self.hue_max = tk.DoubleVar(value=self.DEFAULTS["hue_max"]) self.sat_min = tk.DoubleVar(value=self.DEFAULTS["sat_min"]) self.val_min = tk.DoubleVar(value=self.DEFAULTS["val_min"]) self.val_max = tk.DoubleVar(value=self.DEFAULTS["val_max"]) self.alpha = tk.IntVar(value=self.DEFAULTS["alpha"]) self.ref_hue = None # Debounce for heavy preview updates self.update_delay_ms = 400 self._update_job = None # Exclusion rectangles (preview coordinates) self.exclude_shapes: list[dict[str, object]] = [] self._rubber_start = None self._rubber_id = None self._stroke_preview_id = None self.exclude_mode = "rect" self.reset_exclusions_on_switch = RESET_EXCLUSIONS_ON_IMAGE_CHANGE self._exclude_mask = None self._exclude_mask_dirty = True self._exclude_mask_px = None self._exclude_canvas_ids: list[int] = [] self._current_stroke: list[tuple[int, int]] | None = None self.free_draw_width = 14 self.pick_mode = False # Image references self.image_path = None self.orig_img = None self.preview_img = None self.preview_tk = None self.overlay_tk = None self.image_paths = [] self.current_image_index = -1 # Build UI self.setup_ui() self._init_copy_menu() self.bring_to_front() def _setup_window(self) -> None: self.root.overrideredirect(True) screen_width = self.root.winfo_screenwidth() screen_height = self.root.winfo_screenheight() self.root.geometry(f"{screen_width}x{screen_height}+0+0") self.root.configure(bg="#f2f2f7") def start_app() -> None: """Entry point used by the CLI script.""" root = tk.Tk() app = ICRAApp(root) root.mainloop() __all__ = ["ICRAApp", "start_app"]