From 1d0acba3c371017665515360789c9e2706731a18 Mon Sep 17 00:00:00 2001 From: lm Date: Fri, 17 Oct 2025 17:29:21 +0200 Subject: [PATCH] Add exclusion persistence option Introduce a config flag to reset exclusions when images change (default off), keep shapes when navigating, and theme preview accents accordingly. --- app/app.py | 3 ++- app/gui/exclusions.py | 1 - app/logic/__init__.py | 11 ++++++++++- app/logic/constants.py | 18 ++++++++++++++++++ app/logic/image_processing.py | 12 +++++++++++- config.toml | 4 ++++ 6 files changed, 45 insertions(+), 4 deletions(-) diff --git a/app/app.py b/app/app.py index aaf6cc2..ede1227 100644 --- a/app/app.py +++ b/app/app.py @@ -6,7 +6,7 @@ import tkinter as tk from .gui import ColorPickerMixin, ExclusionMixin, ThemeMixin, UIBuilderMixin from .i18n import I18nMixin -from .logic import DEFAULTS, LANGUAGE, ImageProcessingMixin, ResetMixin +from .logic import DEFAULTS, LANGUAGE, RESET_EXCLUSIONS_ON_IMAGE_CHANGE, ImageProcessingMixin, ResetMixin class ICRAApp( @@ -49,6 +49,7 @@ class ICRAApp( 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 diff --git a/app/gui/exclusions.py b/app/gui/exclusions.py index 8176f8d..639eddb 100644 --- a/app/gui/exclusions.py +++ b/app/gui/exclusions.py @@ -155,7 +155,6 @@ class ExclusionMixin: next_mode = "free" if current == "rect" else "rect" self.exclude_mode = next_mode self._current_stroke = None - accent = self._exclusion_preview_colour() if next_mode == "free": if self._rubber_id: try: diff --git a/app/logic/__init__.py b/app/logic/__init__.py index 37b5aec..c8e3624 100644 --- a/app/logic/__init__.py +++ b/app/logic/__init__.py @@ -1,6 +1,14 @@ """Logic utilities and mixins for processing and configuration.""" -from .constants import BASE_DIR, DEFAULTS, IMAGES_DIR, LANGUAGE, PREVIEW_MAX_SIZE, SUPPORTED_IMAGE_EXTENSIONS +from .constants import ( + BASE_DIR, + DEFAULTS, + IMAGES_DIR, + LANGUAGE, + PREVIEW_MAX_SIZE, + RESET_EXCLUSIONS_ON_IMAGE_CHANGE, + SUPPORTED_IMAGE_EXTENSIONS, +) from .image_processing import ImageProcessingMixin from .reset import ResetMixin @@ -10,6 +18,7 @@ __all__ = [ "IMAGES_DIR", "LANGUAGE", "PREVIEW_MAX_SIZE", + "RESET_EXCLUSIONS_ON_IMAGE_CHANGE", "SUPPORTED_IMAGE_EXTENSIONS", "ImageProcessingMixin", "ResetMixin", diff --git a/app/logic/constants.py b/app/logic/constants.py index 7e4e659..34c5076 100644 --- a/app/logic/constants.py +++ b/app/logic/constants.py @@ -92,7 +92,25 @@ def _extract_language(data: dict[str, Any]) -> str: return sorted(supported)[0] +_CONFIG_DATA = _load_config_data() + +_OPTION_DEFAULTS = {"reset_exclusions_on_image_change": False} + + +def _extract_options(data: dict[str, Any]) -> dict[str, Any]: + section = data.get("options") + if not isinstance(section, dict): + return {} + result: dict[str, Any] = {} + value = section.get("reset_exclusions_on_image_change") + if isinstance(value, bool): + result["reset_exclusions_on_image_change"] = value + return result + + _CONFIG_DATA = _load_config_data() DEFAULTS = {**_DEFAULTS_BASE, **_extract_default_overrides(_CONFIG_DATA)} LANGUAGE = _extract_language(_CONFIG_DATA) +OPTIONS = {**_OPTION_DEFAULTS, **_extract_options(_CONFIG_DATA)} +RESET_EXCLUSIONS_ON_IMAGE_CHANGE = OPTIONS["reset_exclusions_on_image_change"] diff --git a/app/logic/image_processing.py b/app/logic/image_processing.py index 8fd7e41..ab15edd 100644 --- a/app/logic/image_processing.py +++ b/app/logic/image_processing.py @@ -89,6 +89,14 @@ class ImageProcessingMixin: self.image_paths = list(paths) if not self.image_paths: return + self.exclude_shapes = [] + self._rubber_start = None + self._rubber_id = None + self._stroke_preview_id = None + self._exclude_canvas_ids = [] + self._exclude_mask = None + self._exclude_mask_px = None + self._exclude_mask_dirty = True self.current_image_index = -1 self._display_image_by_index(max(0, start_index)) @@ -115,10 +123,12 @@ class ImageProcessingMixin: self.image_path = path self.orig_img = image - self.exclude_shapes = [] + if getattr(self, "reset_exclusions_on_switch", False): + self.exclude_shapes = [] self._rubber_start = None self._rubber_id = None self._stroke_preview_id = None + self._exclude_canvas_ids = [] self._exclude_mask = None self._exclude_mask_px = None self._exclude_mask_dirty = True diff --git a/config.toml b/config.toml index 9930654..fb26ca5 100644 --- a/config.toml +++ b/config.toml @@ -2,6 +2,10 @@ # language must correspond to a file name in app/lang (e.g. "en", "de"). language = "en" +[options] +# Set to true to clear exclusion shapes whenever the image changes. +reset_exclusions_on_image_change = false + [defaults] # Override any of the following keys to tweak the initial slider values: # hue_min, hue_max, sat_min, val_min, val_max accept floating point numbers.