Replace toolbar with categorized menu system, standardize 'color', and improve CSV export
- Migrated toolbar to QMenuBar to fix UI crowding - Categorized actions into File, Edit, Tools, and View - Added dynamic theming to QMenuBar and QMenu - Localized Export CSV delimiter and decimals for German Excel - Padded exported CSV values for clean plain-text alignment - Globally standardized the spelling of 'color' - Removed duplicate code and old Tkinter codebase
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import numpy as np
|
||||
import pytest
|
||||
from PIL import Image
|
||||
|
||||
from app.qt.image_processor import Stats, _rgb_to_hsv_numpy, QtImageProcessor
|
||||
|
||||
|
||||
def test_stats_summary():
|
||||
s = Stats(
|
||||
matches_all=50, total_all=100,
|
||||
matches_keep=40, total_keep=80,
|
||||
matches_excl=10, total_excl=20
|
||||
)
|
||||
|
||||
# Mock translator
|
||||
def mock_t(key, **kwargs):
|
||||
if key == "stats.placeholder":
|
||||
return "Placeholder"
|
||||
return f"{kwargs['with_pct']:.1f} {kwargs['without_pct']:.1f} {kwargs['excluded_pct']:.1f} {kwargs['excluded_match_pct']:.1f}"
|
||||
|
||||
res = s.summary(mock_t)
|
||||
# with_pct: 40/80 = 50.0
|
||||
# without_pct: 50/100 = 50.0
|
||||
# excluded_pct: 20/100 = 20.0
|
||||
# excluded_match_pct: 10/20 = 50.0
|
||||
assert res == "50.0 50.0 20.0 50.0"
|
||||
|
||||
def test_stats_empty():
|
||||
s = Stats()
|
||||
assert s.summary(lambda k, **kw: "Empty") == "Empty"
|
||||
|
||||
|
||||
def test_rgb_to_hsv_numpy():
|
||||
# Test red
|
||||
arr = np.array([[[1.0, 0.0, 0.0]]], dtype=np.float32)
|
||||
hsv = _rgb_to_hsv_numpy(arr)
|
||||
assert np.allclose(hsv[0, 0], [0.0, 100.0, 100.0])
|
||||
|
||||
# Test green
|
||||
arr = np.array([[[0.0, 1.0, 0.0]]], dtype=np.float32)
|
||||
hsv = _rgb_to_hsv_numpy(arr)
|
||||
assert np.allclose(hsv[0, 0], [120.0, 100.0, 100.0])
|
||||
|
||||
# Test blue
|
||||
arr = np.array([[[0.0, 0.0, 1.0]]], dtype=np.float32)
|
||||
hsv = _rgb_to_hsv_numpy(arr)
|
||||
assert np.allclose(hsv[0, 0], [240.0, 100.0, 100.0])
|
||||
|
||||
# Test white
|
||||
arr = np.array([[[1.0, 1.0, 1.0]]], dtype=np.float32)
|
||||
hsv = _rgb_to_hsv_numpy(arr)
|
||||
assert np.allclose(hsv[0, 0], [0.0, 0.0, 100.0])
|
||||
|
||||
# Test black
|
||||
arr = np.array([[[0.0, 0.0, 0.0]]], dtype=np.float32)
|
||||
hsv = _rgb_to_hsv_numpy(arr)
|
||||
assert np.allclose(hsv[0, 0], [0.0, 0.0, 0.0])
|
||||
|
||||
|
||||
def test_qt_processor_matches_legacy():
|
||||
proc = QtImageProcessor()
|
||||
proc.hue_min = 350
|
||||
proc.hue_max = 10
|
||||
proc.sat_min = 50
|
||||
proc.val_min = 50
|
||||
proc.val_max = 100
|
||||
|
||||
# Red wraps around 360, so H=0 -> ok
|
||||
assert proc._matches(255, 0, 0) is True
|
||||
# Green H=120 -> fail
|
||||
assert proc._matches(0, 255, 0) is False
|
||||
# Dark red S=100, V=25 -> fail because val_min=50
|
||||
assert proc._matches(64, 0, 0) is False
|
||||
|
||||
def test_set_overlay_color():
|
||||
proc = QtImageProcessor()
|
||||
# default red
|
||||
assert proc.overlay_r == 255
|
||||
assert proc.overlay_g == 0
|
||||
assert proc.overlay_b == 0
|
||||
|
||||
proc.set_overlay_color("#00ff00")
|
||||
assert proc.overlay_r == 0
|
||||
assert proc.overlay_g == 255
|
||||
assert proc.overlay_b == 0
|
||||
|
||||
# invalid hex does nothing
|
||||
proc.set_overlay_color("blue")
|
||||
assert proc.overlay_r == 0
|
||||
Reference in New Issue
Block a user