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.
This commit is contained in:
lm 2025-10-17 17:29:21 +02:00
parent 987c2e9e4a
commit 1d0acba3c3
6 changed files with 45 additions and 4 deletions

View File

@ -6,7 +6,7 @@ import tkinter as tk
from .gui import ColorPickerMixin, ExclusionMixin, ThemeMixin, UIBuilderMixin from .gui import ColorPickerMixin, ExclusionMixin, ThemeMixin, UIBuilderMixin
from .i18n import I18nMixin 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( class ICRAApp(
@ -49,6 +49,7 @@ class ICRAApp(
self._rubber_id = None self._rubber_id = None
self._stroke_preview_id = None self._stroke_preview_id = None
self.exclude_mode = "rect" self.exclude_mode = "rect"
self.reset_exclusions_on_switch = RESET_EXCLUSIONS_ON_IMAGE_CHANGE
self._exclude_mask = None self._exclude_mask = None
self._exclude_mask_dirty = True self._exclude_mask_dirty = True
self._exclude_mask_px = None self._exclude_mask_px = None

View File

@ -155,7 +155,6 @@ class ExclusionMixin:
next_mode = "free" if current == "rect" else "rect" next_mode = "free" if current == "rect" else "rect"
self.exclude_mode = next_mode self.exclude_mode = next_mode
self._current_stroke = None self._current_stroke = None
accent = self._exclusion_preview_colour()
if next_mode == "free": if next_mode == "free":
if self._rubber_id: if self._rubber_id:
try: try:

View File

@ -1,6 +1,14 @@
"""Logic utilities and mixins for processing and configuration.""" """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 .image_processing import ImageProcessingMixin
from .reset import ResetMixin from .reset import ResetMixin
@ -10,6 +18,7 @@ __all__ = [
"IMAGES_DIR", "IMAGES_DIR",
"LANGUAGE", "LANGUAGE",
"PREVIEW_MAX_SIZE", "PREVIEW_MAX_SIZE",
"RESET_EXCLUSIONS_ON_IMAGE_CHANGE",
"SUPPORTED_IMAGE_EXTENSIONS", "SUPPORTED_IMAGE_EXTENSIONS",
"ImageProcessingMixin", "ImageProcessingMixin",
"ResetMixin", "ResetMixin",

View File

@ -92,7 +92,25 @@ def _extract_language(data: dict[str, Any]) -> str:
return sorted(supported)[0] 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() _CONFIG_DATA = _load_config_data()
DEFAULTS = {**_DEFAULTS_BASE, **_extract_default_overrides(_CONFIG_DATA)} DEFAULTS = {**_DEFAULTS_BASE, **_extract_default_overrides(_CONFIG_DATA)}
LANGUAGE = _extract_language(_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"]

View File

@ -89,6 +89,14 @@ class ImageProcessingMixin:
self.image_paths = list(paths) self.image_paths = list(paths)
if not self.image_paths: if not self.image_paths:
return 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.current_image_index = -1
self._display_image_by_index(max(0, start_index)) self._display_image_by_index(max(0, start_index))
@ -115,10 +123,12 @@ class ImageProcessingMixin:
self.image_path = path self.image_path = path
self.orig_img = image self.orig_img = image
self.exclude_shapes = [] if getattr(self, "reset_exclusions_on_switch", False):
self.exclude_shapes = []
self._rubber_start = None self._rubber_start = None
self._rubber_id = None self._rubber_id = None
self._stroke_preview_id = None self._stroke_preview_id = None
self._exclude_canvas_ids = []
self._exclude_mask = None self._exclude_mask = None
self._exclude_mask_px = None self._exclude_mask_px = None
self._exclude_mask_dirty = True self._exclude_mask_dirty = True

View File

@ -2,6 +2,10 @@
# language must correspond to a file name in app/lang (e.g. "en", "de"). # language must correspond to a file name in app/lang (e.g. "en", "de").
language = "en" language = "en"
[options]
# Set to true to clear exclusion shapes whenever the image changes.
reset_exclusions_on_image_change = false
[defaults] [defaults]
# Override any of the following keys to tweak the initial slider values: # 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. # hue_min, hue_max, sat_min, val_min, val_max accept floating point numbers.