from pathlib import Path from glob import glob from PIL import Image import matplotlib.pylab as plt from matplotlib.widgets import Button from image_preparing import fix_image_resolution class ImageInterface: def __init__(self, base_dir=None): self.base_dir = Path(base_dir or Path(__file__).resolve().parent.parent) self.image_sets = { "source": self._load_images(str(self.base_dir / "images" / "source" / "*.png")), "goal": self._load_images(str(self.base_dir / "images" / "goal" / "*.png")), "shenanigans": self._load_images(str(self.base_dir / "images" / "shenanigans" / "*.png")), } self.current_category = None self.current_index = 0 self.selected_indices = {"source": None, "goal": None} self.figure = None self.image_axis = None self.list_axis = None self.buttons = [] self.key_connection_id = None self.colors = { "figure": "#111111", "panel": "#1a1a1a", "text": "#f2f2f2", "button": "#2b2b2b", "button_hover": "#3a3a3a", } def _load_images(self, pattern): return sorted(glob(pattern)) def _ensure_figure(self): if self.figure is None: self.figure = plt.figure(figsize=(10, 6)) self.key_connection_id = self.figure.canvas.mpl_connect("key_press_event", self._on_key_press) def _clear_layout(self): self._ensure_figure() self.figure.clf() self.image_axis = None self.list_axis = None self.buttons = [] def _make_button(self, rect, label, callback): button_axis = self.figure.add_axes(rect) button_axis.set_facecolor(self.colors["button"]) button_axis.tick_params(left=False, labelleft=False, bottom=False, labelbottom=False) button = Button(button_axis, label, color=self.colors["button"], hovercolor=self.colors["button_hover"]) button.label.set_color(self.colors["text"]) button.on_clicked(callback) self.buttons.append(button) return button def _open_image(self, image_paths, index): if not image_paths: return None safe_index = max(0, min(index, len(image_paths) - 1)) return Image.open(image_paths[safe_index]) def _draw_image_panel(self, axis, image, title): axis.clear() axis.set_title(title) axis.set_facecolor(self.colors["panel"]) axis.axis("off") axis.title.set_color(self.colors["text"]) if image is not None: axis.imshow(image) else: axis.text(0.5, 0.5, "No images found", ha="center", va="center", color=self.colors["text"]) def _draw_list_panel(self, axis, image_paths, current_index): axis.clear() axis.set_facecolor(self.colors["panel"]) axis.axis("off") axis.set_title("Images") axis.title.set_color(self.colors["text"]) if not image_paths: axis.text(0.5, 0.5, "No images found", ha="center", va="center", color=self.colors["text"]) return lines = [] for index, image_path in enumerate(image_paths): name = Path(image_path).name prefix = "> " if index == current_index else " " lines.append(f"{prefix}{name}") axis.text(0.02, 0.98, "\n".join(lines), ha="left", va="top", family="monospace", color=self.colors["text"]) axis.title.set_color(self.colors["text"]) def _show_menu(self): self.current_category = None self._clear_layout() self.figure.patch.set_facecolor(self.colors["figure"]) self._menu_row(0.66, "Source Images", "source", lambda event: self._open_category("source")) self._menu_row(0.50, "Goal Images", "goal", lambda event: self._open_category("goal")) self._make_button([0.08, 0.34, 0.32, 0.12], "Shenanigans Image", lambda event: self._open_shenanigans()) self.figure.canvas.draw_idle() def _open_category(self, category): self.current_category = category self.current_index = 0 self._show_browser() def _open_shenanigans(self): image_paths = self.image_sets.get("source", []) selected_index = self.selected_indices.get("source") if selected_index is None: selected_index = self.current_index if self.current_category == "source" else 0 selected_path = None if image_paths: safe_index = max(0, min(selected_index, len(image_paths) - 1)) selected_path = image_paths[safe_index] fix_image_resolution(selected_path) def _selected_image_name(self, category): selected_index = self.selected_indices.get(category) image_paths = self.image_sets.get(category, []) if selected_index is None or not image_paths: return None safe_index = max(0, min(selected_index, len(image_paths) - 1)) return Path(image_paths[safe_index]).name def _menu_label(self, base_label, category=None): if category is None: return base_label selected_name = self._selected_image_name(category) if selected_name is None: return base_label return f"{base_label} - {selected_name}" def _menu_row(self, y, label, category, callback): self._make_button([0.08, y, 0.32, 0.12], label, callback) selected_name = self._selected_image_name(category) if selected_name is not None: self.figure.text(0.44, y + 0.055, f"- {selected_name}", color=self.colors["text"], va="center", ha="left", fontsize=11) def _select_current_image(self): if self.current_category in ("source", "goal"): self.selected_indices[self.current_category] = self.current_index self._show_menu() def _close_interface(self): if self.figure is not None: plt.close(self.figure) def _show_browser(self): self._clear_layout() self.figure.patch.set_facecolor(self.colors["figure"]) self.figure.suptitle(self.current_category.replace("_", " ").title(), fontsize=16, color=self.colors["text"]) self.image_axis, self.list_axis = self.figure.subplots(1, 2, gridspec_kw={"width_ratios": [3, 1]}) self._refresh_browser_view() self._make_button([0.02, 0.02, 0.12, 0.06], "Back", lambda event: self._show_menu()) if self.current_category in ("source", "goal"): self._make_button([0.76, 0.10, 0.18, 0.06], "Select", lambda event: self._select_current_image()) self._make_button([0.76, 0.02, 0.08, 0.06], "<", lambda event: self.previous_image()) self._make_button([0.86, 0.02, 0.08, 0.06], ">", lambda event: self.next_image()) self.figure.subplots_adjust(left=0.05, right=0.95, bottom=0.12, top=0.90, wspace=0.22) self.figure.canvas.draw_idle() def _refresh_browser_view(self): images = self.image_sets.get(self.current_category, []) current_image = self._open_image(images, self.current_index) category_title = self.current_category.replace("_", " ").title() if self.current_category else "Images" self._draw_image_panel(self.image_axis, current_image, f"{category_title} {self.current_index + 1}/{max(len(images), 1)}") self._draw_list_panel(self.list_axis, images, self.current_index) self.figure.canvas.draw_idle() def _step_image(self, delta): images = self.image_sets.get(self.current_category, []) if images: self.current_index = (self.current_index + delta) % len(images) self._refresh_browser_view() def next_image(self): self._step_image(1) def previous_image(self): self._step_image(-1) def _on_key_press(self, event): if self.current_category is None: if event.key in ("escape", "q"): self._close_interface() return if event.key in ("right", "d"): self.next_image() elif event.key in ("left", "a"): self.previous_image() elif event.key == "escape": self._show_menu() def render(self): self._show_menu() plt.show() def launch_interface(): interface = ImageInterface() interface.render()