Refine UI layout and theme behavior
Generalize color selection, reorganize GUI, add presets, dynamic slider adjustments, config defaults, multiple UI updates etc
This commit is contained in:
@@ -14,7 +14,7 @@ except ModuleNotFoundError: # pragma: no cover - fallback for <3.11
|
||||
if "tomllib" not in globals():
|
||||
tomllib = None # type: ignore[assignment]
|
||||
|
||||
PREVIEW_MAX_SIZE = (1200, 800)
|
||||
PREVIEW_MAX_SIZE = (900, 660)
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parents[2]
|
||||
IMAGES_DIR = BASE_DIR / "images"
|
||||
|
||||
@@ -40,9 +40,12 @@ class ImageProcessingMixin:
|
||||
self.orig_img = image
|
||||
self.prepare_preview()
|
||||
self.update_preview()
|
||||
self.status.config(
|
||||
text=f"Geladen: {self.image_path.name} — {self.orig_img.width}x{self.orig_img.height}"
|
||||
)
|
||||
dimensions = f"{self.orig_img.width}x{self.orig_img.height}"
|
||||
status_text = f"Geladen: {self.image_path.name} — {dimensions}"
|
||||
self.status.config(text=status_text)
|
||||
self.status_default_text = status_text
|
||||
if hasattr(self, "filename_label"):
|
||||
self.filename_label.config(text=f"{self.image_path.name} — {dimensions}")
|
||||
|
||||
def save_overlay(self) -> None:
|
||||
if self.orig_img is None:
|
||||
@@ -57,7 +60,7 @@ class ImageProcessingMixin:
|
||||
self.exclude_rects,
|
||||
alpha=int(self.alpha.get()),
|
||||
scale_from_preview=self.preview_img.size,
|
||||
is_purple_fn=self.is_purple_pixel,
|
||||
is_match_fn=self.matches_target_color,
|
||||
)
|
||||
merged = Image.alpha_composite(self.orig_img.convert("RGBA"), overlay)
|
||||
|
||||
@@ -100,18 +103,18 @@ class ImageProcessingMixin:
|
||||
|
||||
stats = self.compute_stats_preview()
|
||||
if stats:
|
||||
p_all, t_all = stats["all"]
|
||||
p_keep, t_keep = stats["keep"]
|
||||
p_ex, t_ex = stats["excl"]
|
||||
r_with = (p_keep / t_keep * 100) if t_keep else 0.0
|
||||
r_no = (p_all / t_all * 100) if t_all else 0.0
|
||||
excl_share = (t_ex / t_all * 100) if t_all else 0.0
|
||||
excl_purp = (p_ex / t_ex * 100) if t_ex else 0.0
|
||||
matches_all, total_all = stats["all"]
|
||||
matches_keep, total_keep = stats["keep"]
|
||||
matches_ex, total_ex = stats["excl"]
|
||||
r_with = (matches_keep / total_keep * 100) if total_keep else 0.0
|
||||
r_no = (matches_all / total_all * 100) if total_all else 0.0
|
||||
excl_share = (total_ex / total_all * 100) if total_all else 0.0
|
||||
excl_match = (matches_ex / total_ex * 100) if total_ex else 0.0
|
||||
self.ratio_label.config(
|
||||
text=(
|
||||
f"Purple (mit Excludes): {r_with:.2f}% | "
|
||||
f"Purple (ohne Excludes): {r_no:.2f}% | "
|
||||
f"Excluded: {excl_share:.2f}% vom Bild, davon {excl_purp:.2f}% purple"
|
||||
f"Treffer (mit Excludes): {r_with:.2f}% | "
|
||||
f"Treffer (ohne Excludes): {r_no:.2f}% | "
|
||||
f"Excluded: {excl_share:.2f}% vom Bild, davon {excl_match:.2f}% Treffer"
|
||||
)
|
||||
)
|
||||
|
||||
@@ -135,7 +138,7 @@ class ImageProcessingMixin:
|
||||
r, g, b, a = pixels[x, y]
|
||||
if a == 0:
|
||||
continue
|
||||
if self.is_purple_pixel(r, g, b):
|
||||
if self.matches_target_color(r, g, b):
|
||||
draw.point((x, y), fill=(255, 0, 0, alpha))
|
||||
merged = Image.alpha_composite(base, overlay)
|
||||
for (x0, y0, x1, y1) in self.exclude_rects:
|
||||
@@ -147,9 +150,9 @@ class ImageProcessingMixin:
|
||||
return None
|
||||
px = self.preview_img.convert("RGBA").load()
|
||||
width, height = self.preview_img.size
|
||||
purple_all = total_all = 0
|
||||
purple_keep = total_keep = 0
|
||||
purple_excl = total_excl = 0
|
||||
matches_all = total_all = 0
|
||||
matches_keep = total_keep = 0
|
||||
matches_excl = total_excl = 0
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
r, g, b, a = px[x, y]
|
||||
@@ -157,19 +160,23 @@ class ImageProcessingMixin:
|
||||
continue
|
||||
is_excluded = self._is_excluded(x, y)
|
||||
total_all += 1
|
||||
if self.is_purple_pixel(r, g, b):
|
||||
purple_all += 1
|
||||
if self.matches_target_color(r, g, b):
|
||||
matches_all += 1
|
||||
if not is_excluded:
|
||||
total_keep += 1
|
||||
if self.is_purple_pixel(r, g, b):
|
||||
purple_keep += 1
|
||||
if self.matches_target_color(r, g, b):
|
||||
matches_keep += 1
|
||||
else:
|
||||
total_excl += 1
|
||||
if self.is_purple_pixel(r, g, b):
|
||||
purple_excl += 1
|
||||
return {"all": (purple_all, total_all), "keep": (purple_keep, total_keep), "excl": (purple_excl, total_excl)}
|
||||
if self.matches_target_color(r, g, b):
|
||||
matches_excl += 1
|
||||
return {
|
||||
"all": (matches_all, total_all),
|
||||
"keep": (matches_keep, total_keep),
|
||||
"excl": (matches_excl, total_excl),
|
||||
}
|
||||
|
||||
def is_purple_pixel(self, r, g, b) -> bool:
|
||||
def matches_target_color(self, r, g, b) -> bool:
|
||||
h, s, v = colorsys.rgb_to_hsv(r / 255.0, g / 255.0, b / 255.0)
|
||||
hue = h * 360.0
|
||||
hmin = float(self.hue_min.get())
|
||||
@@ -209,7 +216,7 @@ class ImageProcessingMixin:
|
||||
*,
|
||||
alpha: int,
|
||||
scale_from_preview: Tuple[int, int],
|
||||
is_purple_fn,
|
||||
is_match_fn,
|
||||
) -> Image.Image:
|
||||
overlay = Image.new("RGBA", image.size, (0, 0, 0, 0))
|
||||
draw = ImageDraw.Draw(overlay)
|
||||
@@ -223,7 +230,7 @@ class ImageProcessingMixin:
|
||||
r, g, b, a = pixels[x, y]
|
||||
if a == 0:
|
||||
continue
|
||||
if is_purple_fn(r, g, b):
|
||||
if is_match_fn(r, g, b):
|
||||
draw.point((x, y), fill=(255, 0, 0, alpha))
|
||||
return overlay
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@ class ResetMixin:
|
||||
self.val_max.set(self.DEFAULTS["val_max"])
|
||||
self.alpha.set(self.DEFAULTS["alpha"])
|
||||
self.update_preview()
|
||||
default_text = getattr(self, "status_default_text", "Standardwerte aktiv.")
|
||||
if hasattr(self, "status"):
|
||||
self.status.config(text=default_text)
|
||||
|
||||
|
||||
__all__ = ["ResetMixin"]
|
||||
|
||||
Reference in New Issue
Block a user