Compare commits

...

4 Commits

7 changed files with 46 additions and 23 deletions

View File

@ -1,8 +1,6 @@
# ICRS (Interactive Color Range Analyzer)
<img src="app/assets/logo.png" alt="ICRA Logo" width="120"/>
<img src="app/assets/logo.png" alt="ICRS Logo" width="120"/>
ICRS is a small Tkinter tool for analysing colour ranges in images. You load a picture, pick or click a reference colour, adjust hue/saturation/value sliders, and the app marks matching pixels while showing quick stats.
**ICRA** (Interactive Color Range Analyzer) is a Tkinter-based desktop tool for highlighting customised colour ranges in images. Load a single photo or an entire folder, fine-tune hue/saturation/value sliders, and export overlays complete with quick statistics.
## Features
- Two synced previews (original + overlay)
@ -19,12 +17,12 @@ ICRS is a small Tkinter tool for analysing colour ranges in images. You load a p
## Setup with uv (Windows PowerShell)
```powershell
git clone https://git.lukasmahler.de/lm/ICRS.git
cd ICRS
git clone https://git.lukasmahler.de/lm/ICRA.git
cd ICRA
uv venv
.\.venv\Scripts\Activate
uv pip install .
uv run icrs
uv run icra
```
The launcher copies Tcl/Tk resources into the virtualenv on first run, so no manual environment tweaks are needed. On macOS/Linux replace the activate step with `source .venv/bin/activate`.

View File

@ -1,5 +1,5 @@
"""Application package."""
from .app import ICRSApp, start_app
from .app import ICRAApp, start_app
__all__ = ["ICRSApp", "start_app"]
__all__ = ["ICRAApp", "start_app"]

View File

@ -8,7 +8,7 @@ from .gui import ColorPickerMixin, ExclusionMixin, ThemeMixin, UIBuilderMixin
from .logic import DEFAULTS, ImageProcessingMixin, ResetMixin
class ICRSApp(
class ICRAApp(
ThemeMixin,
UIBuilderMixin,
ImageProcessingMixin,
@ -20,7 +20,7 @@ class ICRSApp(
def __init__(self, root: tk.Tk):
self.root = root
self.root.title("ICRS — Interactive Color Range Analyzer")
self.root.title("ICRA — Interactive Color Range Analyzer")
self._setup_window()
# Theme and styling
@ -71,8 +71,8 @@ class ICRSApp(
def start_app() -> None:
"""Entry point used by the CLI script."""
root = tk.Tk()
app = ICRSApp(root)
app = ICRAApp(root)
root.mainloop()
__all__ = ["ICRSApp", "start_app"]
__all__ = ["ICRAApp", "start_app"]

View File

@ -53,6 +53,10 @@ class ThemeMixin:
if callable(button_refresher):
button_refresher()
nav_refresher = getattr(self, "_refresh_navigation_buttons_theme", None)
if callable(nav_refresher):
nav_refresher()
status_refresher = getattr(self, "_refresh_status_palette", None)
if callable(status_refresher) and hasattr(self, "status"):
status_refresher(status_fg)

View File

@ -14,7 +14,7 @@ class UIBuilderMixin:
self._create_titlebar()
toolbar = ttk.Frame(self.root)
toolbar.pack(fill=tk.X, padx=12, pady=0)
toolbar.pack(fill=tk.X, padx=12, pady=(4, 2))
buttons = [
("📂 Bild laden", self.load_image),
("📁 Ordner laden", self.load_folder),
@ -27,6 +27,7 @@ class UIBuilderMixin:
("🌓 Theme umschalten", self.toggle_theme),
]
self._toolbar_buttons: list[dict[str, object]] = []
self._nav_buttons: list[tk.Button] = []
buttons_frame = ttk.Frame(toolbar)
buttons_frame.pack(side=tk.LEFT)
@ -47,7 +48,7 @@ class UIBuilderMixin:
self._status_palette = {"fg": self.status.cget("foreground")}
palette_frame = ttk.Frame(self.root)
palette_frame.pack(fill=tk.X, padx=12, pady=(0, 8))
palette_frame.pack(fill=tk.X, padx=12, pady=(6, 8))
ttk.Label(palette_frame, text="Beispielfarben:").pack(side=tk.LEFT, padx=(0, 8))
swatch_container = ttk.Frame(palette_frame)
swatch_container.pack(side=tk.LEFT)
@ -326,9 +327,10 @@ class UIBuilderMixin:
return canvas.create_polygon(points, smooth=True, splinesteps=24, **kwargs)
def _create_navigation_button(self, container, symbol: str, command, *, column: int) -> None:
bg = self.root.cget("bg") if hasattr(self.root, "cget") else "#f2f2f7"
palette = self._navigation_palette()
bg = palette["bg"]
fg = palette["fg"]
container.grid_rowconfigure(0, weight=1)
fg = "#f5f5f5" if getattr(self, "theme", "light") == "dark" else "#1f1f1f"
btn = tk.Button(
container,
text=symbol,
@ -345,6 +347,7 @@ class UIBuilderMixin:
width=2,
)
btn.grid(row=0, column=column, sticky="ns", padx=6)
self._nav_buttons.append(btn)
def _create_titlebar(self) -> None:
bar_bg = "#1f1f1f"
@ -371,7 +374,7 @@ class UIBuilderMixin:
title_label = tk.Label(
title_bar,
text="ICRS — Interactive Color Range Analyzer",
text="ICRA — Interactive Color Range Analyzer",
bg=bar_bg,
fg="#f5f5f5",
font=("Segoe UI", 11, "bold"),
@ -443,6 +446,12 @@ class UIBuilderMixin:
"text": "#1f1f1f",
}
def _navigation_palette(self) -> dict[str, str]:
is_dark = getattr(self, "theme", "light") == "dark"
if is_dark:
return {"bg": "#26262a", "fg": "#f5f5f5"}
return {"bg": "#e8e8ee", "fg": "#1f1f1f"}
def _refresh_toolbar_buttons_theme(self) -> None:
if not getattr(self, "_toolbar_buttons", None):
return
@ -457,6 +466,18 @@ class UIBuilderMixin:
canvas.itemconfigure(rect_id, fill=palette["normal"], outline=palette["outline"])
canvas.itemconfigure(text_id, fill=palette["text"])
def _refresh_navigation_buttons_theme(self) -> None:
if not getattr(self, "_nav_buttons", None):
return
palette = self._navigation_palette()
for btn in self._nav_buttons:
btn.configure(
background=palette["bg"],
activebackground=palette["bg"],
fg=palette["fg"],
activeforeground=palette["fg"],
)
def _refresh_status_palette(self, fg: str) -> None:
self.status.configure(foreground=fg)
self._status_palette["fg"] = fg

View File

@ -1,4 +1,4 @@
"""Launcher ensuring Tcl/Tk resources are available before starting ICRS."""
"""Launcher ensuring Tcl/Tk resources are available before starting ICRA."""
from __future__ import annotations

View File

@ -1,9 +1,9 @@
[project]
name = "icrs"
name = "icra"
version = "0.1.0"
description = "Interactive Color Range Analyzer (ICRS) for Tkinter"
description = "Interactive Color Range Analyzer (ICRA) for Tkinter"
readme = "README.md"
authors = [{ name = "ICRS contributors" }]
authors = [{ name = "ICRA contributors" }]
license = "MIT"
requires-python = ">=3.10"
dependencies = [
@ -11,7 +11,7 @@ dependencies = [
]
[project.scripts]
icrs = "app.launcher:main"
icra = "app.launcher:main"
[tool.uv]
package = true