Compare commits
2 Commits
4fc0182798
...
1d0acba3c3
| Author | SHA1 | Date |
|---|---|---|
|
|
1d0acba3c3 | |
|
|
987c2e9e4a |
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,13 @@ class ExclusionMixin:
|
||||||
self.canvas_orig.delete(preview_id)
|
self.canvas_orig.delete(preview_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
accent = self._exclusion_preview_colour()
|
||||||
self._stroke_preview_id = self.canvas_orig.create_line(
|
self._stroke_preview_id = self.canvas_orig.create_line(
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
fill="yellow",
|
fill=accent,
|
||||||
width=2,
|
width=2,
|
||||||
smooth=True,
|
smooth=True,
|
||||||
capstyle="round",
|
capstyle="round",
|
||||||
|
|
@ -39,7 +40,8 @@ class ExclusionMixin:
|
||||||
self.canvas_orig.delete(self._rubber_id)
|
self.canvas_orig.delete(self._rubber_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
self._rubber_id = self.canvas_orig.create_rectangle(x, y, x, y, outline="yellow", width=2)
|
accent = self._exclusion_preview_colour()
|
||||||
|
self._rubber_id = self.canvas_orig.create_rectangle(x, y, x, y, outline=accent, width=2)
|
||||||
|
|
||||||
def _exclude_drag(self, event):
|
def _exclude_drag(self, event):
|
||||||
mode = getattr(self, "exclude_mode", "rect")
|
mode = getattr(self, "exclude_mode", "rect")
|
||||||
|
|
@ -168,6 +170,7 @@ class ExclusionMixin:
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
self._stroke_preview_id = None
|
self._stroke_preview_id = None
|
||||||
|
self._rubber_id = None
|
||||||
message_key = "status.free_draw_enabled" if next_mode == "free" else "status.free_draw_disabled"
|
message_key = "status.free_draw_enabled" if next_mode == "free" else "status.free_draw_disabled"
|
||||||
if hasattr(self, "status"):
|
if hasattr(self, "status"):
|
||||||
try:
|
try:
|
||||||
|
|
@ -186,6 +189,10 @@ class ExclusionMixin:
|
||||||
compressed.append(point)
|
compressed.append(point)
|
||||||
return compressed
|
return compressed
|
||||||
|
|
||||||
|
def _exclusion_preview_colour(self) -> str:
|
||||||
|
is_dark = getattr(self, "theme", "light") == "dark"
|
||||||
|
return "#ffd700" if is_dark else "#c56217"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _close_polygon(points: list[tuple[int, int]]) -> list[tuple[int, int]]:
|
def _close_polygon(points: list[tuple[int, int]]) -> list[tuple[int, int]]:
|
||||||
"""Ensure the polygon is closed by repeating the start if necessary."""
|
"""Ensure the polygon is closed by repeating the start if necessary."""
|
||||||
|
|
|
||||||
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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"]
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue