修改Calman灰阶中结果图显示、修改UI主题样式应用
This commit is contained in:
@@ -22,6 +22,7 @@ from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
|
||||
from matplotlib.figure import Figure
|
||||
|
||||
from app.tests.color_accuracy import calculate_delta_e_2000
|
||||
from app.views.modern_styles import get_theme_palette
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from pqAutomationApp import PQAutomationApp
|
||||
@@ -35,10 +36,6 @@ D65_X = 0.3127
|
||||
D65_Y = 0.3290
|
||||
TARGET_CCT = 6504
|
||||
TARGET_GAMMA = 2.2
|
||||
_DARK_BG = "#2f2f2f"
|
||||
_AX_BG = "#262626"
|
||||
_FG = "#d8d8d8"
|
||||
_GRID = "#5b5b5b"
|
||||
|
||||
DE_FORMULAS = ["2000", "94", "76"]
|
||||
|
||||
@@ -60,7 +57,7 @@ def _contrast_fg(gray_value: int) -> str:
|
||||
def _set_canvas_patch(canvas: tk.Canvas, color: str, text: str) -> None:
|
||||
"""统一设置色块 Canvas 颜色与文字,避免主题对 Label 背景渲染的干扰。"""
|
||||
gray = int(color[1:3], 16)
|
||||
canvas.configure(bg=color, highlightbackground="#666666")
|
||||
canvas.configure(bg=color, highlightbackground=get_theme_palette()["border"])
|
||||
canvas.itemconfigure("patch_bg", fill=color, outline=color)
|
||||
canvas.itemconfigure("patch_text", text=text, fill=_contrast_fg(gray))
|
||||
|
||||
@@ -115,6 +112,7 @@ def _get_calman_palette() -> dict[str, str]:
|
||||
"""根据当前主题生成 Calman 调试面板色板。"""
|
||||
style = ttk.Style()
|
||||
colors = style.colors
|
||||
theme_palette = get_theme_palette()
|
||||
bg = colors.bg
|
||||
fg = colors.fg
|
||||
dark_mode = _is_dark_hex(bg)
|
||||
@@ -131,22 +129,22 @@ def _get_calman_palette() -> dict[str, str]:
|
||||
reading_fg = _mix(fg, "#ffffff", 0.06)
|
||||
status_fg = _mix(fg, bg, 0.35)
|
||||
reading_accent = colors.info
|
||||
xy_series = "#d7dce4"
|
||||
d65_mark = "#ffffff"
|
||||
xy_series = _mix(fg, "#ffffff", 0.10)
|
||||
d65_mark = _mix(fg, "#ffffff", 0.04)
|
||||
else:
|
||||
figure_bg = _mix(bg, "#dfe7ef", 0.45)
|
||||
axes_bg = _mix(bg, "#eff4f9", 0.72)
|
||||
grid = _mix("#5f6f82", axes_bg, 0.55)
|
||||
tree_bg = "#ffffff"
|
||||
tree_even = "#ffffff"
|
||||
tree_bg = theme_palette["input_bg"]
|
||||
tree_even = theme_palette["input_bg"]
|
||||
tree_odd = "#f3f7fb"
|
||||
heading_bg = _mix(colors.primary, "#ffffff", 0.82)
|
||||
reading_bg = _mix(bg, "#e7eef5", 0.58)
|
||||
reading_fg = fg
|
||||
status_fg = _mix(fg, bg, 0.25)
|
||||
reading_accent = _mix(colors.info, "#000000", 0.25)
|
||||
xy_series = "#1f2a36"
|
||||
d65_mark = "#253142"
|
||||
xy_series = _mix(fg, bg, 0.18)
|
||||
d65_mark = _mix(fg, bg, 0.28)
|
||||
|
||||
return {
|
||||
"figure_bg": figure_bg,
|
||||
@@ -166,31 +164,59 @@ def _get_calman_palette() -> dict[str, str]:
|
||||
"tree_heading_bg": heading_bg,
|
||||
"tree_heading_fg": reading_fg,
|
||||
"tree_select": _mix(colors.info, figure_bg, 0.35),
|
||||
"patch_border": theme_palette["border"],
|
||||
"patch_border_alt": _mix(theme_palette["border"], theme_palette["fg"], 0.12),
|
||||
"patch_focus": theme_palette["focus"],
|
||||
"xy_series": xy_series,
|
||||
"d65_mark": d65_mark,
|
||||
}
|
||||
|
||||
|
||||
def _xyY_to_rgb_balance(x: float, y: float, big_y: float) -> tuple[float, float, float]:
|
||||
"""把 xyY 近似映射到 RGB 比例,并归一到平均值 100。"""
|
||||
"""按 D65 同亮度参考计算 RGB Balance(Calman 常见口径)。"""
|
||||
if y <= 0 or big_y <= 0:
|
||||
return float("nan"), float("nan"), float("nan")
|
||||
|
||||
big_x = (x * big_y) / y
|
||||
big_z = ((1.0 - x - y) * big_y) / y
|
||||
def _xyY_to_xyz(cx: float, cy: float, cy_big: float) -> tuple[float, float, float]:
|
||||
if cy <= 0:
|
||||
return float("nan"), float("nan"), float("nan")
|
||||
cx_big = (cx * cy_big) / cy
|
||||
cz_big = ((1.0 - cx - cy) * cy_big) / cy
|
||||
return cx_big, cy_big, cz_big
|
||||
|
||||
r = (3.2406 * big_x) + (-1.5372 * big_y) + (-0.4986 * big_z)
|
||||
g = (-0.9689 * big_x) + (1.8758 * big_y) + (0.0415 * big_z)
|
||||
b = (0.0557 * big_x) + (-0.2040 * big_y) + (1.0570 * big_z)
|
||||
def _xyz_to_linear_rgb(cx_big: float, cy_big: float, cz_big: float) -> tuple[float, float, float]:
|
||||
rr = (3.2406 * cx_big) + (-1.5372 * cy_big) + (-0.4986 * cz_big)
|
||||
gg = (-0.9689 * cx_big) + (1.8758 * cy_big) + (0.0415 * cz_big)
|
||||
bb = (0.0557 * cx_big) + (-0.2040 * cy_big) + (1.0570 * cz_big)
|
||||
return rr, gg, bb
|
||||
|
||||
r = max(r, 0.0)
|
||||
g = max(g, 0.0)
|
||||
b = max(b, 0.0)
|
||||
mx, my, mz = _xyY_to_xyz(x, y, big_y)
|
||||
tx, ty, tz = _xyY_to_xyz(D65_X, D65_Y, big_y)
|
||||
mr, mg, mb = _xyz_to_linear_rgb(mx, my, mz)
|
||||
tr, tg, tb = _xyz_to_linear_rgb(tx, ty, tz)
|
||||
|
||||
avg = (r + g + b) / 3.0
|
||||
if avg <= 0:
|
||||
eps = 1e-9
|
||||
if tr <= eps or tg <= eps or tb <= eps:
|
||||
return float("nan"), float("nan"), float("nan")
|
||||
return (r / avg) * 100.0, (g / avg) * 100.0, (b / avg) * 100.0
|
||||
|
||||
rr = (mr / tr) * 100.0
|
||||
gg = (mg / tg) * 100.0
|
||||
bb = (mb / tb) * 100.0
|
||||
|
||||
# 明显异常值视为无效,避免图表被离群点拉坏。
|
||||
if not (math.isfinite(rr) and math.isfinite(gg) and math.isfinite(bb)):
|
||||
return float("nan"), float("nan"), float("nan")
|
||||
if rr < 0 or gg < 0 or bb < 0:
|
||||
return float("nan"), float("nan"), float("nan")
|
||||
|
||||
return rr, gg, bb
|
||||
|
||||
|
||||
def _target_gamma_loglog_curve(pct: int) -> float:
|
||||
"""Calman风格目标曲线:低灰阶从 1.8 过渡并逐步逼近 2.2。"""
|
||||
if pct <= 0:
|
||||
return 1.8
|
||||
return TARGET_GAMMA - 0.4 * math.exp(-pct / 6.0)
|
||||
|
||||
|
||||
def _style_axes(self: "PQAutomationApp", ax, title: str) -> None:
|
||||
@@ -480,6 +506,7 @@ def create_calman_panel(self: "PQAutomationApp") -> None:
|
||||
self.calman_actual_patch_cells = []
|
||||
self.calman_target_patch_canvases = []
|
||||
self.calman_target_hexes = []
|
||||
patch_palette = _get_calman_palette()
|
||||
for idx, pct in enumerate(self.calman_levels):
|
||||
rgb = _pct_to_gray_rgb(pct)
|
||||
color = _rgb_to_hex(rgb)
|
||||
@@ -493,7 +520,7 @@ def create_calman_panel(self: "PQAutomationApp") -> None:
|
||||
bd=1,
|
||||
relief="solid",
|
||||
highlightthickness=1,
|
||||
highlightbackground="#808080",
|
||||
highlightbackground=patch_palette["patch_border_alt"],
|
||||
cursor="hand2",
|
||||
)
|
||||
actual_cell.grid(row=0, column=idx, padx=1, pady=1, sticky=tk.NSEW)
|
||||
@@ -520,7 +547,7 @@ def create_calman_panel(self: "PQAutomationApp") -> None:
|
||||
bd=1,
|
||||
relief="solid",
|
||||
highlightthickness=1,
|
||||
highlightbackground="#9c9c9c",
|
||||
highlightbackground=patch_palette["patch_border"],
|
||||
cursor="hand2",
|
||||
)
|
||||
cell.grid(row=1, column=idx, padx=1, pady=1, sticky=tk.NSEW)
|
||||
@@ -722,7 +749,7 @@ def send_patch(self: "PQAutomationApp", pct: int) -> None:
|
||||
def _measure_once(self: "PQAutomationApp", pct: int) -> dict | None:
|
||||
"""采集一次 CA410,并组装一条记录(含 CCT/Gamma/ΔE2000)。"""
|
||||
try:
|
||||
x, y, lv, X, Y, Z = self.ca.readAllDisplay()
|
||||
x, y, lv, X, Y, Z = self.read_ca_xyLv()
|
||||
except Exception as exc:
|
||||
self._dispatch_ui(self.log_gui.log, f"CA410 采集异常: {exc}", "error")
|
||||
return None
|
||||
@@ -974,20 +1001,21 @@ def clear_results(self: "PQAutomationApp") -> None:
|
||||
|
||||
def _highlight_patch(self: "PQAutomationApp", pct: int) -> None:
|
||||
"""高亮当前选中色块。"""
|
||||
palette = _get_calman_palette()
|
||||
try:
|
||||
idx = self.calman_levels.index(pct)
|
||||
except ValueError:
|
||||
return
|
||||
for i, cell in enumerate(self.calman_patch_cells):
|
||||
if i == idx:
|
||||
cell.configure(highlightbackground="#1f6fb2", highlightthickness=2)
|
||||
cell.configure(highlightbackground=palette["patch_focus"], highlightthickness=2)
|
||||
else:
|
||||
cell.configure(highlightbackground="#9c9c9c", highlightthickness=1)
|
||||
cell.configure(highlightbackground=palette["patch_border"], highlightthickness=1)
|
||||
for i, cell in enumerate(self.calman_actual_cells):
|
||||
if i == idx:
|
||||
cell.configure(highlightbackground="#1f6fb2", highlightthickness=2)
|
||||
cell.configure(highlightbackground=palette["patch_focus"], highlightthickness=2)
|
||||
else:
|
||||
cell.configure(highlightbackground="#808080", highlightthickness=1)
|
||||
cell.configure(highlightbackground=palette["patch_border_alt"], highlightthickness=1)
|
||||
|
||||
total_cols = len(self.calman_levels) + 1 # 含 metric 列
|
||||
col_index = idx + 1
|
||||
@@ -1082,10 +1110,18 @@ def _redraw_calman_charts(self: "PQAutomationApp") -> None:
|
||||
pcts = [r["pct"] for r in recs]
|
||||
de_vals = [r["de2000"] if r["de2000"] == r["de2000"] else 0 for r in recs]
|
||||
lum_vals = [r["Y"] if r["Y"] == r["Y"] else 0 for r in recs]
|
||||
rgb_r = [r["rgb_r"] for r in recs if r["rgb_r"] == r["rgb_r"]]
|
||||
rgb_g = [r["rgb_g"] for r in recs if r["rgb_g"] == r["rgb_g"]]
|
||||
rgb_b = [r["rgb_b"] for r in recs if r["rgb_b"] == r["rgb_b"]]
|
||||
rgb_pcts = [r["pct"] for r in recs if r["rgb_r"] == r["rgb_r"]]
|
||||
rgb_recs = [
|
||||
r for r in recs
|
||||
if (
|
||||
r.get("rgb_r") == r.get("rgb_r")
|
||||
and r.get("rgb_g") == r.get("rgb_g")
|
||||
and r.get("rgb_b") == r.get("rgb_b")
|
||||
)
|
||||
]
|
||||
rgb_pcts = [r["pct"] for r in rgb_recs]
|
||||
rgb_r = [r["rgb_r"] for r in rgb_recs]
|
||||
rgb_g = [r["rgb_g"] for r in rgb_recs]
|
||||
rgb_b = [r["rgb_b"] for r in rgb_recs]
|
||||
gamma_pcts = [r["pct"] for r in recs if r["gamma"] == r["gamma"]]
|
||||
gamma_vals = [r["gamma"] for r in recs if r["gamma"] == r["gamma"]]
|
||||
cct_vals = [r["cct"] for r in recs if r["cct"] == r["cct"]]
|
||||
@@ -1123,6 +1159,16 @@ def _redraw_calman_charts(self: "PQAutomationApp") -> None:
|
||||
a1.set_ylim(bottom=0)
|
||||
a1.set_xlabel("", fontsize=8)
|
||||
|
||||
rgb_ylim_low = 95.0
|
||||
rgb_ylim_high = 105.0
|
||||
if rgb_recs:
|
||||
rgb_values = rgb_r + rgb_g + rgb_b
|
||||
rgb_min = min(rgb_values + [100.0])
|
||||
rgb_max = max(rgb_values + [100.0])
|
||||
pad = max(0.8, (rgb_max - rgb_min) * 0.15)
|
||||
rgb_ylim_low = min(95.0, rgb_min - pad)
|
||||
rgb_ylim_high = max(105.0, rgb_max + pad)
|
||||
|
||||
# RGB Balance 线图
|
||||
a2 = self.calman_ax_rgb_line
|
||||
a2.clear()
|
||||
@@ -1133,36 +1179,38 @@ def _redraw_calman_charts(self: "PQAutomationApp") -> None:
|
||||
a2.plot(rgb_pcts, rgb_b, "-", color="#4a73ff", linewidth=1.2)
|
||||
a2.axhline(100, color="#9e9e9e", lw=0.8, ls="--")
|
||||
a2.set_xlim(-2, 102)
|
||||
a2.set_ylim(95, 105)
|
||||
a2.set_ylim(rgb_ylim_low, rgb_ylim_high)
|
||||
a2.set_xlabel("", fontsize=8)
|
||||
|
||||
# RGB Balance 条图(用最后一个点)
|
||||
a3 = self.calman_ax_rgb_bar
|
||||
a3.clear()
|
||||
_style_axes(self, a3, "RGB Balance")
|
||||
if recs:
|
||||
last = recs[-1]
|
||||
if rgb_recs:
|
||||
last = rgb_recs[-1]
|
||||
bars = [
|
||||
last["rgb_r"] if last["rgb_r"] == last["rgb_r"] else 100,
|
||||
last["rgb_g"] if last["rgb_g"] == last["rgb_g"] else 100,
|
||||
last["rgb_b"] if last["rgb_b"] == last["rgb_b"] else 100,
|
||||
last["rgb_r"],
|
||||
last["rgb_g"],
|
||||
last["rgb_b"],
|
||||
]
|
||||
a3.bar([0, 1, 2], bars, color=["#ff4d4d", "#4caf50", "#4a73ff"], width=0.7)
|
||||
a3.set_xticks([0, 1, 2], ["R", "G", "B"])
|
||||
else:
|
||||
a3.set_xticks([0, 1, 2], ["R", "G", "B"])
|
||||
a3.set_ylim(95, 105)
|
||||
a3.set_ylim(rgb_ylim_low, rgb_ylim_high)
|
||||
a3.set_xlabel("", fontsize=8)
|
||||
|
||||
# Gamma
|
||||
a4 = self.calman_ax_gamma
|
||||
a4.clear()
|
||||
_style_axes(self, a4, "Gamma Log/Log")
|
||||
target_pcts = list(self.calman_levels)
|
||||
target_vals = [_target_gamma_loglog_curve(p) for p in target_pcts]
|
||||
a4.plot(target_pcts, target_vals, "-", color="#f4ff00", linewidth=1.8)
|
||||
if gamma_pcts:
|
||||
a4.plot(gamma_pcts, gamma_vals, "-", color="#ffe24d", linewidth=1.3)
|
||||
a4.axhline(TARGET_GAMMA, color="#9e9e9e", lw=0.8, ls="--")
|
||||
a4.plot(gamma_pcts, gamma_vals, "-", color="#8f8f8f", linewidth=2.0)
|
||||
a4.set_xlim(-2, 102)
|
||||
a4.set_ylim(1.6, 2.8)
|
||||
a4.set_ylim(1.8, 2.8)
|
||||
a4.set_xlabel("", fontsize=8)
|
||||
|
||||
self.calman_canvas.draw_idle()
|
||||
@@ -1232,14 +1280,20 @@ def _refresh_metric_table(self: "PQAutomationApp") -> None:
|
||||
"""重绘下方矩阵表。"""
|
||||
_apply_calman_tree_style(self)
|
||||
palette = _get_calman_palette()
|
||||
ref_white_y = self.calman_results.get(100, {}).get("Y")
|
||||
|
||||
def _target_y_abs(pctx):
|
||||
if pctx is None:
|
||||
return "-"
|
||||
if ref_white_y is None or ref_white_y != ref_white_y or ref_white_y <= 0:
|
||||
return "-"
|
||||
return _safe_float(ref_white_y * ((pctx / 100.0) ** TARGET_GAMMA), "{:.3f}")
|
||||
|
||||
metrics = [
|
||||
("x CIE31", lambda r: _safe_float(r.get("x")) if r else "-"),
|
||||
("y CIE31", lambda r: _safe_float(r.get("y")) if r else "-"),
|
||||
("Y", lambda r: _safe_float(r.get("Y"), "{:.3f}") if r else "-"),
|
||||
(
|
||||
"Target Y",
|
||||
lambda _r, pctx=None: _safe_float((pctx / 100.0) ** TARGET_GAMMA, "{:.4f}") if pctx and pctx > 0 else ("0.0000" if pctx == 0 else "-"),
|
||||
),
|
||||
("Target Y", lambda _r, pctx=None: _target_y_abs(pctx)),
|
||||
("Gamma Log/Log", lambda r: _safe_float(r.get("gamma"), "{:.3f}") if r else "-"),
|
||||
("CCT", lambda r: _safe_float(r.get("cct"), "{:.0f}") if r else "-"),
|
||||
("dE 2000", lambda r: _safe_float(r.get("de2000"), "{:.3f}") if r else "-"),
|
||||
|
||||
Reference in New Issue
Block a user