修复日志模块深色显示不正确
This commit is contained in:
@@ -64,8 +64,9 @@ def apply_modern_styles() -> None:
|
|||||||
sidebar_bg = _mix(dark, bg, 0.18) if dark_theme else _mix(primary, "#000000", 0.10)
|
sidebar_bg = _mix(dark, bg, 0.18) if dark_theme else _mix(primary, "#000000", 0.10)
|
||||||
sidebar_hover = _mix(sidebar_bg, "#ffffff", 0.07) if dark_theme else _mix(sidebar_bg, "#000000", 0.06)
|
sidebar_hover = _mix(sidebar_bg, "#ffffff", 0.07) if dark_theme else _mix(sidebar_bg, "#000000", 0.06)
|
||||||
sidebar_selected = _mix(sidebar_bg, "#ffffff", 0.14) if dark_theme else _mix(sidebar_bg, "#000000", 0.10)
|
sidebar_selected = _mix(sidebar_bg, "#ffffff", 0.14) if dark_theme else _mix(sidebar_bg, "#000000", 0.10)
|
||||||
sidebar_fg = _mix(fg, bg, 0.05)
|
# 侧栏背景在浅色主题下也偏深,文字颜色需按侧栏亮度自适应,避免“黑字不明显”。
|
||||||
sidebar_muted = _mix(fg, sidebar_bg, 0.45)
|
sidebar_fg = "#F4F8FD" if _is_dark(sidebar_bg) else _mix(fg, bg, 0.05)
|
||||||
|
sidebar_muted = _mix(sidebar_fg, sidebar_bg, 0.45)
|
||||||
|
|
||||||
# ---------------- 卡片 ----------------
|
# ---------------- 卡片 ----------------
|
||||||
style.configure(
|
style.configure(
|
||||||
@@ -219,7 +220,7 @@ def apply_modern_styles() -> None:
|
|||||||
("active", sidebar_hover),
|
("active", sidebar_hover),
|
||||||
("pressed", sidebar_selected),
|
("pressed", sidebar_selected),
|
||||||
],
|
],
|
||||||
foreground=[("active", "#ffffff" if dark_theme else sidebar_fg)],
|
foreground=[("active", "#ffffff" if _is_dark(sidebar_hover) else sidebar_fg)],
|
||||||
)
|
)
|
||||||
style.configure(
|
style.configure(
|
||||||
"SidebarSelected.TButton",
|
"SidebarSelected.TButton",
|
||||||
|
|||||||
@@ -92,6 +92,85 @@ def _xy_to_upvp(x: float, y: float) -> tuple[float, float]:
|
|||||||
return up, vp
|
return up, vp
|
||||||
|
|
||||||
|
|
||||||
|
def _hex_to_rgb(hex_color: str) -> tuple[int, int, int]:
|
||||||
|
c = hex_color.lstrip("#")
|
||||||
|
return int(c[0:2], 16), int(c[2:4], 16), int(c[4:6], 16)
|
||||||
|
|
||||||
|
|
||||||
|
def _mix(hex_a: str, hex_b: str, ratio: float) -> str:
|
||||||
|
r1, g1, b1 = _hex_to_rgb(hex_a)
|
||||||
|
r2, g2, b2 = _hex_to_rgb(hex_b)
|
||||||
|
r = int(r1 * (1 - ratio) + r2 * ratio)
|
||||||
|
g = int(g1 * (1 - ratio) + g2 * ratio)
|
||||||
|
b = int(b1 * (1 - ratio) + b2 * ratio)
|
||||||
|
return f"#{r:02x}{g:02x}{b:02x}"
|
||||||
|
|
||||||
|
|
||||||
|
def _is_dark_hex(hex_color: str) -> bool:
|
||||||
|
r, g, b = _hex_to_rgb(hex_color)
|
||||||
|
return (r * 299 + g * 587 + b * 114) / 1000 < 128
|
||||||
|
|
||||||
|
|
||||||
|
def _get_calman_palette() -> dict[str, str]:
|
||||||
|
"""根据当前主题生成 Calman 调试面板色板。"""
|
||||||
|
style = ttk.Style()
|
||||||
|
colors = style.colors
|
||||||
|
bg = colors.bg
|
||||||
|
fg = colors.fg
|
||||||
|
dark_mode = _is_dark_hex(bg)
|
||||||
|
|
||||||
|
if dark_mode:
|
||||||
|
figure_bg = _mix(bg, "#000000", 0.18)
|
||||||
|
axes_bg = _mix(bg, "#000000", 0.25)
|
||||||
|
grid = _mix(fg, axes_bg, 0.55)
|
||||||
|
tree_bg = _mix(bg, "#000000", 0.10)
|
||||||
|
tree_even = _mix(bg, "#ffffff", 0.03)
|
||||||
|
tree_odd = _mix(bg, "#ffffff", 0.07)
|
||||||
|
heading_bg = _mix(bg, "#ffffff", 0.12)
|
||||||
|
reading_bg = _mix(bg, "#ffffff", 0.06)
|
||||||
|
reading_fg = _mix(fg, "#ffffff", 0.06)
|
||||||
|
status_fg = _mix(fg, bg, 0.35)
|
||||||
|
reading_accent = colors.info
|
||||||
|
xy_series = "#d7dce4"
|
||||||
|
d65_mark = "#ffffff"
|
||||||
|
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_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"
|
||||||
|
|
||||||
|
return {
|
||||||
|
"figure_bg": figure_bg,
|
||||||
|
"axes_bg": axes_bg,
|
||||||
|
"fg": fg,
|
||||||
|
"grid": grid,
|
||||||
|
"metric_tile_bg": _mix(figure_bg, "#ffffff", 0.08 if dark_mode else 0.25),
|
||||||
|
"metric_tile_fg": reading_fg,
|
||||||
|
"status_fg": status_fg,
|
||||||
|
"reading_accent": reading_accent,
|
||||||
|
"reading_bg": reading_bg,
|
||||||
|
"reading_fg": reading_fg,
|
||||||
|
"tree_bg": tree_bg,
|
||||||
|
"tree_fg": reading_fg,
|
||||||
|
"tree_even": tree_even,
|
||||||
|
"tree_odd": tree_odd,
|
||||||
|
"tree_heading_bg": heading_bg,
|
||||||
|
"tree_heading_fg": reading_fg,
|
||||||
|
"tree_select": _mix(colors.info, figure_bg, 0.35),
|
||||||
|
"xy_series": xy_series,
|
||||||
|
"d65_mark": d65_mark,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _xyY_to_rgb_balance(x: float, y: float, big_y: float) -> tuple[float, float, float]:
|
def _xyY_to_rgb_balance(x: float, y: float, big_y: float) -> tuple[float, float, float]:
|
||||||
"""把 xyY 近似映射到 RGB 比例,并归一到平均值 100。"""
|
"""把 xyY 近似映射到 RGB 比例,并归一到平均值 100。"""
|
||||||
if y <= 0 or big_y <= 0:
|
if y <= 0 or big_y <= 0:
|
||||||
@@ -114,17 +193,48 @@ def _xyY_to_rgb_balance(x: float, y: float, big_y: float) -> tuple[float, float,
|
|||||||
return (r / avg) * 100.0, (g / avg) * 100.0, (b / avg) * 100.0
|
return (r / avg) * 100.0, (g / avg) * 100.0, (b / avg) * 100.0
|
||||||
|
|
||||||
|
|
||||||
def _style_axes(ax, title: str) -> None:
|
def _style_axes(self: "PQAutomationApp", ax, title: str) -> None:
|
||||||
ax.set_title(title, color=_FG, fontsize=9, pad=4)
|
palette = _get_calman_palette()
|
||||||
ax.set_facecolor(_AX_BG)
|
ax.set_title(title, color=palette["fg"], fontsize=9, pad=4)
|
||||||
ax.grid(True, color=_GRID, alpha=0.35, linewidth=0.6)
|
ax.set_facecolor(palette["axes_bg"])
|
||||||
ax.tick_params(colors=_FG, labelsize=8)
|
ax.grid(True, color=palette["grid"], alpha=0.35, linewidth=0.6)
|
||||||
|
ax.tick_params(colors=palette["fg"], labelsize=8)
|
||||||
for spine in ax.spines.values():
|
for spine in ax.spines.values():
|
||||||
spine.set_color("#8a8a8a")
|
spine.set_color(_mix(palette["fg"], palette["axes_bg"], 0.55))
|
||||||
|
|
||||||
|
|
||||||
|
def _apply_calman_tree_style(self: "PQAutomationApp") -> None:
|
||||||
|
"""应用矩阵 Treeview 的深浅色样式。"""
|
||||||
|
palette = _get_calman_palette()
|
||||||
|
style = ttk.Style()
|
||||||
|
style.configure(
|
||||||
|
"Calman.Treeview",
|
||||||
|
rowheight=22,
|
||||||
|
font=("Consolas", 9),
|
||||||
|
background=palette["tree_bg"],
|
||||||
|
fieldbackground=palette["tree_bg"],
|
||||||
|
foreground=palette["tree_fg"],
|
||||||
|
)
|
||||||
|
style.map(
|
||||||
|
"Calman.Treeview",
|
||||||
|
background=[("selected", palette["tree_select"])],
|
||||||
|
foreground=[("selected", palette["tree_fg"])],
|
||||||
|
)
|
||||||
|
style.configure(
|
||||||
|
"Calman.Treeview.Heading",
|
||||||
|
font=("微软雅黑", 9, "bold"),
|
||||||
|
background=palette["tree_heading_bg"],
|
||||||
|
foreground=palette["tree_heading_fg"],
|
||||||
|
)
|
||||||
|
if hasattr(self, "calman_metric_tree"):
|
||||||
|
self.calman_metric_tree.configure(style="Calman.Treeview")
|
||||||
|
if hasattr(self, "calman_data_tree"):
|
||||||
|
self.calman_data_tree.configure(style="Calman.Treeview")
|
||||||
|
|
||||||
|
|
||||||
def create_calman_panel(self: "PQAutomationApp") -> None:
|
def create_calman_panel(self: "PQAutomationApp") -> None:
|
||||||
"""创建 CALMAN 风格灰阶测试面板,注册到 panel_manager。"""
|
"""创建 CALMAN 风格灰阶测试面板,注册到 panel_manager。"""
|
||||||
|
palette = _get_calman_palette()
|
||||||
self.calman_frame = ttk.Frame(self.content_frame)
|
self.calman_frame = ttk.Frame(self.content_frame)
|
||||||
self.calman_visible = False
|
self.calman_visible = False
|
||||||
self.calman_levels = list(DEFAULT_LEVELS_PCT)
|
self.calman_levels = list(DEFAULT_LEVELS_PCT)
|
||||||
@@ -152,7 +262,7 @@ def create_calman_panel(self: "PQAutomationApp") -> None:
|
|||||||
chart_frame.rowconfigure(2, weight=0)
|
chart_frame.rowconfigure(2, weight=0)
|
||||||
chart_frame.columnconfigure(0, weight=1)
|
chart_frame.columnconfigure(0, weight=1)
|
||||||
|
|
||||||
fig = Figure(figsize=(10.5, 3.4), dpi=90, facecolor=_DARK_BG)
|
fig = Figure(figsize=(10.5, 3.4), dpi=90, facecolor=palette["figure_bg"])
|
||||||
self.calman_fig = fig
|
self.calman_fig = fig
|
||||||
self.calman_ax_de = fig.add_subplot(141)
|
self.calman_ax_de = fig.add_subplot(141)
|
||||||
self.calman_ax_rgb_line = fig.add_subplot(142)
|
self.calman_ax_rgb_line = fig.add_subplot(142)
|
||||||
@@ -163,7 +273,7 @@ def create_calman_panel(self: "PQAutomationApp") -> None:
|
|||||||
)
|
)
|
||||||
canvas = FigureCanvasTkAgg(fig, master=chart_frame)
|
canvas = FigureCanvasTkAgg(fig, master=chart_frame)
|
||||||
canvas_widget = canvas.get_tk_widget()
|
canvas_widget = canvas.get_tk_widget()
|
||||||
canvas_widget.configure(bg=_DARK_BG, highlightthickness=0)
|
canvas_widget.configure(bg=palette["figure_bg"], highlightthickness=0)
|
||||||
canvas_widget.grid(row=0, column=0, sticky=tk.NSEW)
|
canvas_widget.grid(row=0, column=0, sticky=tk.NSEW)
|
||||||
self.calman_canvas = canvas
|
self.calman_canvas = canvas
|
||||||
|
|
||||||
@@ -181,11 +291,12 @@ def create_calman_panel(self: "PQAutomationApp") -> None:
|
|||||||
de_combo.pack(side=tk.LEFT, padx=(4, 10))
|
de_combo.pack(side=tk.LEFT, padx=(4, 10))
|
||||||
|
|
||||||
self.calman_elapsed_var = tk.StringVar(value="Step: -- s | Total: -- s")
|
self.calman_elapsed_var = tk.StringVar(value="Step: -- s | Total: -- s")
|
||||||
ttk.Label(
|
self.calman_elapsed_label = ttk.Label(
|
||||||
control_row,
|
control_row,
|
||||||
textvariable=self.calman_elapsed_var,
|
textvariable=self.calman_elapsed_var,
|
||||||
foreground="#d0d0d0",
|
foreground=palette["status_fg"],
|
||||||
).pack(side=tk.LEFT)
|
)
|
||||||
|
self.calman_elapsed_label.pack(side=tk.LEFT)
|
||||||
|
|
||||||
metrics_row = ttk.Frame(chart_frame)
|
metrics_row = ttk.Frame(chart_frame)
|
||||||
metrics_row.grid(row=2, column=0, sticky=tk.EW, pady=(2, 0))
|
metrics_row.grid(row=2, column=0, sticky=tk.EW, pady=(2, 0))
|
||||||
@@ -194,6 +305,7 @@ def create_calman_panel(self: "PQAutomationApp") -> None:
|
|||||||
self.calman_avg_cct_var = tk.StringVar(value="Avg CCT: --")
|
self.calman_avg_cct_var = tk.StringVar(value="Avg CCT: --")
|
||||||
self.calman_contrast_var = tk.StringVar(value="Contrast Ratio: --")
|
self.calman_contrast_var = tk.StringVar(value="Contrast Ratio: --")
|
||||||
self.calman_avg_gamma_var = tk.StringVar(value="Average Gamma: --")
|
self.calman_avg_gamma_var = tk.StringVar(value="Average Gamma: --")
|
||||||
|
self.calman_metric_labels = []
|
||||||
for idx, v in enumerate(
|
for idx, v in enumerate(
|
||||||
(
|
(
|
||||||
self.calman_avg_de_var,
|
self.calman_avg_de_var,
|
||||||
@@ -202,14 +314,16 @@ def create_calman_panel(self: "PQAutomationApp") -> None:
|
|||||||
self.calman_avg_gamma_var,
|
self.calman_avg_gamma_var,
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
tk.Label(
|
lbl = tk.Label(
|
||||||
metrics_row,
|
metrics_row,
|
||||||
textvariable=v,
|
textvariable=v,
|
||||||
anchor=tk.CENTER,
|
anchor=tk.CENTER,
|
||||||
fg="#f2f2f2",
|
fg=palette["metric_tile_fg"],
|
||||||
bg="#373737",
|
bg=palette["metric_tile_bg"],
|
||||||
font=("微软雅黑", 10, "bold"),
|
font=("微软雅黑", 10, "bold"),
|
||||||
).grid(row=0, column=idx, sticky=tk.EW, padx=2)
|
)
|
||||||
|
lbl.grid(row=0, column=idx, sticky=tk.EW, padx=2)
|
||||||
|
self.calman_metric_labels.append(lbl)
|
||||||
|
|
||||||
# ---------------------------- 顶部右:按钮列 ----------------------------
|
# ---------------------------- 顶部右:按钮列 ----------------------------
|
||||||
btn_col = ttk.LabelFrame(root, text="操作", padding=6)
|
btn_col = ttk.LabelFrame(root, text="操作", padding=6)
|
||||||
@@ -246,13 +360,14 @@ def create_calman_panel(self: "PQAutomationApp") -> None:
|
|||||||
).pack(fill=tk.X, pady=2)
|
).pack(fill=tk.X, pady=2)
|
||||||
|
|
||||||
self.calman_status_var = tk.StringVar(value="待机")
|
self.calman_status_var = tk.StringVar(value="待机")
|
||||||
ttk.Label(
|
self.calman_status_label = ttk.Label(
|
||||||
btn_col,
|
btn_col,
|
||||||
textvariable=self.calman_status_var,
|
textvariable=self.calman_status_var,
|
||||||
foreground="#555",
|
foreground=palette["status_fg"],
|
||||||
wraplength=150,
|
wraplength=150,
|
||||||
justify=tk.LEFT,
|
justify=tk.LEFT,
|
||||||
).pack(fill=tk.X, pady=(8, 0))
|
)
|
||||||
|
self.calman_status_label.pack(fill=tk.X, pady=(8, 0))
|
||||||
|
|
||||||
self.calman_progress_var = tk.StringVar(value="0 / 0")
|
self.calman_progress_var = tk.StringVar(value="0 / 0")
|
||||||
self.calman_progress = ttk.Progressbar(
|
self.calman_progress = ttk.Progressbar(
|
||||||
@@ -269,14 +384,15 @@ def create_calman_panel(self: "PQAutomationApp") -> None:
|
|||||||
self.calman_reading_var = tk.StringVar(
|
self.calman_reading_var = tk.StringVar(
|
||||||
value="x: -- y: -- Y: --\nCCT: -- ΔE: --"
|
value="x: -- y: -- Y: --\nCCT: -- ΔE: --"
|
||||||
)
|
)
|
||||||
ttk.Label(
|
self.calman_reading_summary_label = ttk.Label(
|
||||||
btn_col,
|
btn_col,
|
||||||
textvariable=self.calman_reading_var,
|
textvariable=self.calman_reading_var,
|
||||||
font=("Consolas", 9),
|
font=("Consolas", 9),
|
||||||
foreground="#1f6fb2",
|
foreground=palette["reading_accent"],
|
||||||
wraplength=160,
|
wraplength=160,
|
||||||
justify=tk.LEFT,
|
justify=tk.LEFT,
|
||||||
).pack(fill=tk.X, pady=(8, 0))
|
)
|
||||||
|
self.calman_reading_summary_label.pack(fill=tk.X, pady=(8, 0))
|
||||||
|
|
||||||
# ---------------------------- 中部:灰阶色块(Target / Actual) ----------------------------
|
# ---------------------------- 中部:灰阶色块(Target / Actual) ----------------------------
|
||||||
patch_outer = ttk.LabelFrame(root, text="灰阶色块(点击可直接输出 Pattern)", padding=6)
|
patch_outer = ttk.LabelFrame(root, text="灰阶色块(点击可直接输出 Pattern)", padding=6)
|
||||||
@@ -390,24 +506,26 @@ def create_calman_panel(self: "PQAutomationApp") -> None:
|
|||||||
"cd/m²: --\n"
|
"cd/m²: --\n"
|
||||||
"ΔE2000: --"
|
"ΔE2000: --"
|
||||||
)
|
)
|
||||||
tk.Label(
|
self.calman_reading_detail_label = tk.Label(
|
||||||
left,
|
left,
|
||||||
textvariable=self.calman_reading_var,
|
textvariable=self.calman_reading_var,
|
||||||
justify=tk.LEFT,
|
justify=tk.LEFT,
|
||||||
font=("Consolas", 10),
|
font=("Consolas", 10),
|
||||||
fg="#e5e5e5",
|
fg=palette["reading_fg"],
|
||||||
bg="#323232",
|
bg=palette["reading_bg"],
|
||||||
width=22,
|
width=22,
|
||||||
padx=4,
|
padx=4,
|
||||||
pady=4,
|
pady=4,
|
||||||
).pack(fill=tk.X)
|
)
|
||||||
|
self.calman_reading_detail_label.pack(fill=tk.X)
|
||||||
|
|
||||||
xy_fig = Figure(figsize=(2.6, 2.2), dpi=90, facecolor=_DARK_BG)
|
xy_fig = Figure(figsize=(2.6, 2.2), dpi=90, facecolor=palette["figure_bg"])
|
||||||
|
self.calman_xy_fig = xy_fig
|
||||||
self.calman_xy_ax = xy_fig.add_subplot(111)
|
self.calman_xy_ax = xy_fig.add_subplot(111)
|
||||||
xy_fig.subplots_adjust(left=0.20, right=0.96, top=0.90, bottom=0.18)
|
xy_fig.subplots_adjust(left=0.20, right=0.96, top=0.90, bottom=0.18)
|
||||||
xy_canvas = FigureCanvasTkAgg(xy_fig, master=left)
|
xy_canvas = FigureCanvasTkAgg(xy_fig, master=left)
|
||||||
xy_widget = xy_canvas.get_tk_widget()
|
xy_widget = xy_canvas.get_tk_widget()
|
||||||
xy_widget.configure(bg=_DARK_BG, highlightthickness=0)
|
xy_widget.configure(bg=palette["figure_bg"], highlightthickness=0)
|
||||||
xy_widget.pack(fill=tk.BOTH, expand=True, pady=(6, 0))
|
xy_widget.pack(fill=tk.BOTH, expand=True, pady=(6, 0))
|
||||||
self.calman_xy_canvas = xy_canvas
|
self.calman_xy_canvas = xy_canvas
|
||||||
|
|
||||||
@@ -458,11 +576,7 @@ def create_calman_panel(self: "PQAutomationApp") -> None:
|
|||||||
for widget in (metric_tree, data_tree):
|
for widget in (metric_tree, data_tree):
|
||||||
widget.bind("<MouseWheel>", lambda e: _matrix_mousewheel(self, e))
|
widget.bind("<MouseWheel>", lambda e: _matrix_mousewheel(self, e))
|
||||||
|
|
||||||
style = ttk.Style()
|
_apply_calman_tree_style(self)
|
||||||
style.configure("Calman.Treeview", rowheight=22, font=("Consolas", 9))
|
|
||||||
style.configure("Calman.Treeview.Heading", font=("微软雅黑", 9, "bold"))
|
|
||||||
self.calman_metric_tree.configure(style="Calman.Treeview")
|
|
||||||
self.calman_data_tree.configure(style="Calman.Treeview")
|
|
||||||
|
|
||||||
right.bind("<Configure>", lambda _e: _adaptive_matrix_columns(self))
|
right.bind("<Configure>", lambda _e: _adaptive_matrix_columns(self))
|
||||||
|
|
||||||
@@ -796,6 +910,17 @@ def _adaptive_matrix_columns(self: "PQAutomationApp") -> None:
|
|||||||
|
|
||||||
def _redraw_calman_charts(self: "PQAutomationApp") -> None:
|
def _redraw_calman_charts(self: "PQAutomationApp") -> None:
|
||||||
"""根据 calman_results 重绘四张图和 xy 散点。"""
|
"""根据 calman_results 重绘四张图和 xy 散点。"""
|
||||||
|
palette = _get_calman_palette()
|
||||||
|
if hasattr(self, "calman_fig"):
|
||||||
|
self.calman_fig.patch.set_facecolor(palette["figure_bg"])
|
||||||
|
if hasattr(self, "calman_canvas"):
|
||||||
|
try:
|
||||||
|
self.calman_canvas.get_tk_widget().configure(
|
||||||
|
bg=palette["figure_bg"], highlightthickness=0
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
recs = sorted(self.calman_results.values(), key=lambda r: r["pct"])
|
recs = sorted(self.calman_results.values(), key=lambda r: r["pct"])
|
||||||
pcts = [r["pct"] for r in recs]
|
pcts = [r["pct"] for r in recs]
|
||||||
de_vals = [r["de2000"] if r["de2000"] == r["de2000"] else 0 for r in recs]
|
de_vals = [r["de2000"] if r["de2000"] == r["de2000"] else 0 for r in recs]
|
||||||
@@ -834,7 +959,7 @@ def _redraw_calman_charts(self: "PQAutomationApp") -> None:
|
|||||||
# ΔE2000
|
# ΔE2000
|
||||||
a1 = self.calman_ax_de
|
a1 = self.calman_ax_de
|
||||||
a1.clear()
|
a1.clear()
|
||||||
_style_axes(a1, "DeltaE 2000")
|
_style_axes(self, a1, "DeltaE 2000")
|
||||||
if pcts:
|
if pcts:
|
||||||
a1.bar(pcts, de_vals, color="#ffcf57", width=3.5)
|
a1.bar(pcts, de_vals, color="#ffcf57", width=3.5)
|
||||||
a1.set_xlim(-2, 102)
|
a1.set_xlim(-2, 102)
|
||||||
@@ -844,7 +969,7 @@ def _redraw_calman_charts(self: "PQAutomationApp") -> None:
|
|||||||
# RGB Balance 线图
|
# RGB Balance 线图
|
||||||
a2 = self.calman_ax_rgb_line
|
a2 = self.calman_ax_rgb_line
|
||||||
a2.clear()
|
a2.clear()
|
||||||
_style_axes(a2, "RGB Balance")
|
_style_axes(self, a2, "RGB Balance")
|
||||||
if rgb_pcts:
|
if rgb_pcts:
|
||||||
a2.plot(rgb_pcts, rgb_r, "-", color="#ff4d4d", linewidth=1.2)
|
a2.plot(rgb_pcts, rgb_r, "-", color="#ff4d4d", linewidth=1.2)
|
||||||
a2.plot(rgb_pcts, rgb_g, "-", color="#4caf50", linewidth=1.2)
|
a2.plot(rgb_pcts, rgb_g, "-", color="#4caf50", linewidth=1.2)
|
||||||
@@ -857,7 +982,7 @@ def _redraw_calman_charts(self: "PQAutomationApp") -> None:
|
|||||||
# RGB Balance 条图(用最后一个点)
|
# RGB Balance 条图(用最后一个点)
|
||||||
a3 = self.calman_ax_rgb_bar
|
a3 = self.calman_ax_rgb_bar
|
||||||
a3.clear()
|
a3.clear()
|
||||||
_style_axes(a3, "RGB Balance")
|
_style_axes(self, a3, "RGB Balance")
|
||||||
if recs:
|
if recs:
|
||||||
last = recs[-1]
|
last = recs[-1]
|
||||||
bars = [
|
bars = [
|
||||||
@@ -875,7 +1000,7 @@ def _redraw_calman_charts(self: "PQAutomationApp") -> None:
|
|||||||
# Gamma
|
# Gamma
|
||||||
a4 = self.calman_ax_gamma
|
a4 = self.calman_ax_gamma
|
||||||
a4.clear()
|
a4.clear()
|
||||||
_style_axes(a4, "Gamma Log/Log")
|
_style_axes(self, a4, "Gamma Log/Log")
|
||||||
if gamma_pcts:
|
if gamma_pcts:
|
||||||
a4.plot(gamma_pcts, gamma_vals, "-", color="#ffe24d", linewidth=1.3)
|
a4.plot(gamma_pcts, gamma_vals, "-", color="#ffe24d", linewidth=1.3)
|
||||||
a4.axhline(TARGET_GAMMA, color="#9e9e9e", lw=0.8, ls="--")
|
a4.axhline(TARGET_GAMMA, color="#9e9e9e", lw=0.8, ls="--")
|
||||||
@@ -889,21 +1014,32 @@ def _redraw_calman_charts(self: "PQAutomationApp") -> None:
|
|||||||
|
|
||||||
|
|
||||||
def _redraw_xy_chart(self: "PQAutomationApp") -> None:
|
def _redraw_xy_chart(self: "PQAutomationApp") -> None:
|
||||||
|
palette = _get_calman_palette()
|
||||||
|
if hasattr(self, "calman_xy_fig"):
|
||||||
|
self.calman_xy_fig.patch.set_facecolor(palette["figure_bg"])
|
||||||
|
if hasattr(self, "calman_xy_canvas"):
|
||||||
|
try:
|
||||||
|
self.calman_xy_canvas.get_tk_widget().configure(
|
||||||
|
bg=palette["figure_bg"], highlightthickness=0
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
ax = self.calman_xy_ax
|
ax = self.calman_xy_ax
|
||||||
ax.clear()
|
ax.clear()
|
||||||
_style_axes(ax, "CIE 1931 xy")
|
_style_axes(self, ax, "CIE 1931 xy")
|
||||||
ax.set_xlim(0.29, 0.34)
|
ax.set_xlim(0.29, 0.34)
|
||||||
ax.set_ylim(0.31, 0.35)
|
ax.set_ylim(0.31, 0.35)
|
||||||
ax.plot([D65_X], [D65_Y], marker="x", color="#ffffff", markersize=7)
|
ax.plot([D65_X], [D65_Y], marker="x", color=palette["d65_mark"], markersize=7)
|
||||||
|
|
||||||
recs = sorted(self.calman_results.values(), key=lambda r: r["pct"])
|
recs = sorted(self.calman_results.values(), key=lambda r: r["pct"])
|
||||||
if recs:
|
if recs:
|
||||||
xs = [r["x"] for r in recs]
|
xs = [r["x"] for r in recs]
|
||||||
ys = [r["y"] for r in recs]
|
ys = [r["y"] for r in recs]
|
||||||
ax.plot(xs, ys, "o-", color="#000000", linewidth=1.0, markersize=3)
|
ax.plot(xs, ys, "o-", color=palette["xy_series"], linewidth=1.0, markersize=3)
|
||||||
last = recs[-1]
|
last = recs[-1]
|
||||||
ax.plot([last["x"]], [last["y"]], marker="o", color="#ffcc00", markersize=5)
|
ax.plot([last["x"]], [last["y"]], marker="o", color="#ffcc00", markersize=5)
|
||||||
ax.plot([last["x"], D65_X], [last["y"], D65_Y], color="#c7c7c7", linewidth=0.8)
|
ax.plot([last["x"], D65_X], [last["y"], D65_Y], color=_mix(palette["fg"], palette["axes_bg"], 0.4), linewidth=0.8)
|
||||||
self.calman_xy_canvas.draw_idle()
|
self.calman_xy_canvas.draw_idle()
|
||||||
|
|
||||||
|
|
||||||
@@ -937,6 +1073,8 @@ def _update_target_strip(self: "PQAutomationApp") -> None:
|
|||||||
|
|
||||||
def _refresh_metric_table(self: "PQAutomationApp") -> None:
|
def _refresh_metric_table(self: "PQAutomationApp") -> None:
|
||||||
"""重绘下方矩阵表。"""
|
"""重绘下方矩阵表。"""
|
||||||
|
_apply_calman_tree_style(self)
|
||||||
|
palette = _get_calman_palette()
|
||||||
metrics = [
|
metrics = [
|
||||||
("x CIE31", lambda r: _safe_float(r.get("x")) if r else "-"),
|
("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 CIE31", lambda r: _safe_float(r.get("y")) if r else "-"),
|
||||||
@@ -971,17 +1109,54 @@ def _refresh_metric_table(self: "PQAutomationApp") -> None:
|
|||||||
self.calman_metric_tree.insert("", tk.END, iid=iid, values=(name,), tags=tags)
|
self.calman_metric_tree.insert("", tk.END, iid=iid, values=(name,), tags=tags)
|
||||||
self.calman_data_tree.insert("", tk.END, iid=iid, values=values, tags=tags)
|
self.calman_data_tree.insert("", tk.END, iid=iid, values=values, tags=tags)
|
||||||
|
|
||||||
self.calman_metric_tree.tag_configure("odd", background="#f5f7fa")
|
self.calman_metric_tree.tag_configure(
|
||||||
self.calman_metric_tree.tag_configure("even", background="#ffffff")
|
"odd", background=palette["tree_odd"], foreground=palette["tree_fg"]
|
||||||
self.calman_data_tree.tag_configure("odd", background="#f5f7fa")
|
)
|
||||||
self.calman_data_tree.tag_configure("even", background="#ffffff")
|
self.calman_metric_tree.tag_configure(
|
||||||
|
"even", background=palette["tree_even"], foreground=palette["tree_fg"]
|
||||||
|
)
|
||||||
|
self.calman_data_tree.tag_configure(
|
||||||
|
"odd", background=palette["tree_odd"], foreground=palette["tree_fg"]
|
||||||
|
)
|
||||||
|
self.calman_data_tree.tag_configure(
|
||||||
|
"even", background=palette["tree_even"], foreground=palette["tree_fg"]
|
||||||
|
)
|
||||||
|
|
||||||
first, last = self.calman_data_tree.yview()
|
first, last = self.calman_data_tree.yview()
|
||||||
self.calman_table_ysb.set(first, last)
|
self.calman_table_ysb.set(first, last)
|
||||||
|
|
||||||
|
|
||||||
|
def refresh_calman_theme(self: "PQAutomationApp") -> None:
|
||||||
|
"""主题切换后刷新 Calman 灰阶调试面板的表格与图表颜色。"""
|
||||||
|
if not hasattr(self, "calman_frame"):
|
||||||
|
return
|
||||||
|
|
||||||
|
palette = _get_calman_palette()
|
||||||
|
|
||||||
|
if hasattr(self, "calman_elapsed_label"):
|
||||||
|
self.calman_elapsed_label.configure(foreground=palette["status_fg"])
|
||||||
|
if hasattr(self, "calman_status_label"):
|
||||||
|
self.calman_status_label.configure(foreground=palette["status_fg"])
|
||||||
|
if hasattr(self, "calman_reading_summary_label"):
|
||||||
|
self.calman_reading_summary_label.configure(foreground=palette["reading_accent"])
|
||||||
|
if hasattr(self, "calman_reading_detail_label"):
|
||||||
|
self.calman_reading_detail_label.configure(
|
||||||
|
fg=palette["reading_fg"],
|
||||||
|
bg=palette["reading_bg"],
|
||||||
|
)
|
||||||
|
for lbl in getattr(self, "calman_metric_labels", []):
|
||||||
|
lbl.configure(
|
||||||
|
fg=palette["metric_tile_fg"],
|
||||||
|
bg=palette["metric_tile_bg"],
|
||||||
|
)
|
||||||
|
|
||||||
|
_refresh_metric_table(self)
|
||||||
|
_redraw_calman_charts(self)
|
||||||
|
|
||||||
|
|
||||||
class CalmanPanelMixin:
|
class CalmanPanelMixin:
|
||||||
"""挂载本模块的自由函数到 PQAutomationApp。"""
|
"""挂载本模块的自由函数到 PQAutomationApp。"""
|
||||||
|
|
||||||
create_calman_panel = create_calman_panel
|
create_calman_panel = create_calman_panel
|
||||||
toggle_calman_panel = toggle_calman_panel
|
toggle_calman_panel = toggle_calman_panel
|
||||||
|
refresh_calman_theme = refresh_calman_theme
|
||||||
|
|||||||
@@ -723,6 +723,16 @@ def _on_toggle_theme(self: "PQAutomationApp") -> None:
|
|||||||
self.apply_result_chart_theme()
|
self.apply_result_chart_theme()
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
if hasattr(self, "log_gui") and hasattr(self.log_gui, "refresh_log_theme"):
|
||||||
|
try:
|
||||||
|
self.log_gui.refresh_log_theme()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if hasattr(self, "refresh_calman_theme"):
|
||||||
|
try:
|
||||||
|
self.refresh_calman_theme()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
# 同步刷新侧栏选中态(高亮样式跟随新色板)
|
# 同步刷新侧栏选中态(高亮样式跟随新色板)
|
||||||
if hasattr(self, "update_sidebar_selection"):
|
if hasattr(self, "update_sidebar_selection"):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -33,6 +33,55 @@ def _theme_colors():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_hex(hex_color: str, fallback: str = "#808080") -> str:
|
||||||
|
"""把颜色字符串规范化为 #RRGGBB;非法输入回退到 fallback。"""
|
||||||
|
if not isinstance(hex_color, str):
|
||||||
|
return fallback
|
||||||
|
c = hex_color.strip()
|
||||||
|
if not c:
|
||||||
|
return fallback
|
||||||
|
if c.startswith("#"):
|
||||||
|
c = c[1:]
|
||||||
|
if len(c) == 3:
|
||||||
|
c = "".join(ch * 2 for ch in c)
|
||||||
|
if len(c) != 6:
|
||||||
|
return fallback
|
||||||
|
try:
|
||||||
|
int(c, 16)
|
||||||
|
except ValueError:
|
||||||
|
return fallback
|
||||||
|
return f"#{c}"
|
||||||
|
|
||||||
|
|
||||||
|
def _hex_to_rgb(hex_color: str):
|
||||||
|
c = _normalize_hex(hex_color, fallback="#808080").lstrip("#")
|
||||||
|
return int(c[0:2], 16), int(c[2:4], 16), int(c[4:6], 16)
|
||||||
|
|
||||||
|
|
||||||
|
def _mix(hex_a: str, hex_b: str, ratio: float) -> str:
|
||||||
|
ratio = max(0.0, min(1.0, ratio))
|
||||||
|
r1, g1, b1 = _hex_to_rgb(hex_a)
|
||||||
|
r2, g2, b2 = _hex_to_rgb(hex_b)
|
||||||
|
r = int(r1 * (1 - ratio) + r2 * ratio)
|
||||||
|
g = int(g1 * (1 - ratio) + g2 * ratio)
|
||||||
|
b = int(b1 * (1 - ratio) + b2 * ratio)
|
||||||
|
return f"#{r:02x}{g:02x}{b:02x}"
|
||||||
|
|
||||||
|
|
||||||
|
def _is_dark(hex_color: str) -> bool:
|
||||||
|
r, g, b = _hex_to_rgb(hex_color)
|
||||||
|
return (r * 299 + g * 587 + b * 114) / 1000 < 128
|
||||||
|
|
||||||
|
|
||||||
|
def _auto_text_color(bg_hex: str, fg_hint: str) -> str:
|
||||||
|
"""根据背景亮度给出稳定可读的文本颜色。"""
|
||||||
|
bg_hex = _normalize_hex(bg_hex, fallback="#f5f5f5")
|
||||||
|
fg_hint = _normalize_hex(fg_hint, fallback="#202020")
|
||||||
|
if _is_dark(bg_hex):
|
||||||
|
return _mix("#ffffff", fg_hint, 0.25)
|
||||||
|
return _mix("#000000", fg_hint, 0.25)
|
||||||
|
|
||||||
|
|
||||||
class PQLogGUI(ttk.Frame):
|
class PQLogGUI(ttk.Frame):
|
||||||
VALID_LEVELS = {"info", "success", "warning", "error", "debug", "separator", "blank"}
|
VALID_LEVELS = {"info", "success", "warning", "error", "debug", "separator", "blank"}
|
||||||
|
|
||||||
@@ -132,21 +181,44 @@ class PQLogGUI(ttk.Frame):
|
|||||||
|
|
||||||
def _configure_tags(self):
|
def _configure_tags(self):
|
||||||
palette = _theme_colors()
|
palette = _theme_colors()
|
||||||
|
bg = self.log_text.cget("bg") or palette["text_bg"] or palette["bg"]
|
||||||
|
base_fg = _auto_text_color(bg, palette["fg"])
|
||||||
|
muted_fg = _mix(base_fg, bg, 0.45)
|
||||||
|
debug_level_color = _mix(palette["accent"], base_fg, 0.35)
|
||||||
|
debug_msg_color = _mix(palette["accent"], base_fg, 0.50)
|
||||||
self.log_text.tag_configure("timestamp", foreground=palette["muted"])
|
self.log_text.tag_configure("timestamp", foreground=palette["muted"])
|
||||||
self.log_text.tag_configure("level_info", foreground=palette["accent"])
|
self.log_text.tag_configure("level_info", foreground=palette["accent"])
|
||||||
self.log_text.tag_configure("level_success", foreground=palette["success"])
|
self.log_text.tag_configure("level_success", foreground=palette["success"])
|
||||||
self.log_text.tag_configure("level_warning", foreground=palette["warning"])
|
self.log_text.tag_configure("level_warning", foreground=palette["warning"])
|
||||||
self.log_text.tag_configure("level_error", foreground=palette["error"])
|
self.log_text.tag_configure("level_error", foreground=palette["error"])
|
||||||
self.log_text.tag_configure("level_debug", foreground="#7c3aed")
|
self.log_text.tag_configure("level_debug", foreground=debug_level_color)
|
||||||
self.log_text.tag_configure("message", foreground=palette["fg"])
|
self.log_text.tag_configure("message", foreground=base_fg)
|
||||||
self.log_text.tag_configure("message_success", foreground=palette["success"])
|
self.log_text.tag_configure("message_success", foreground=palette["success"])
|
||||||
self.log_text.tag_configure("message_warning", foreground=palette["warning"])
|
self.log_text.tag_configure("message_warning", foreground=palette["warning"])
|
||||||
self.log_text.tag_configure("message_error", foreground=palette["error"])
|
self.log_text.tag_configure("message_error", foreground=palette["error"])
|
||||||
self.log_text.tag_configure("message_debug", foreground="#6d28d9")
|
self.log_text.tag_configure("message_debug", foreground=debug_msg_color)
|
||||||
self.log_text.tag_configure("separator", foreground=palette["muted"])
|
self.log_text.tag_configure("separator", foreground=muted_fg)
|
||||||
self.log_text.tag_configure("traceback", foreground=palette["error"])
|
self.log_text.tag_configure("traceback", foreground=palette["error"])
|
||||||
self.log_text.tag_configure("blank", spacing1=4, spacing3=4)
|
self.log_text.tag_configure("blank", spacing1=4, spacing3=4)
|
||||||
|
|
||||||
|
def refresh_log_theme(self):
|
||||||
|
"""主题切换后刷新日志控件的背景和字体颜色。"""
|
||||||
|
if threading.current_thread() is not threading.main_thread():
|
||||||
|
self.after(0, self.refresh_log_theme)
|
||||||
|
return
|
||||||
|
|
||||||
|
palette = _theme_colors()
|
||||||
|
bg = palette["text_bg"]
|
||||||
|
fg_hint = palette["text_fg"] if palette["text_fg"] else palette["fg"]
|
||||||
|
fg = _auto_text_color(bg, fg_hint)
|
||||||
|
|
||||||
|
self.log_text.configure(
|
||||||
|
bg=bg,
|
||||||
|
fg=fg,
|
||||||
|
insertbackground=fg,
|
||||||
|
)
|
||||||
|
self._configure_tags()
|
||||||
|
|
||||||
def _append_message(self, message, level):
|
def _append_message(self, message, level):
|
||||||
lines = message.splitlines() or [""]
|
lines = message.splitlines() or [""]
|
||||||
for line in lines:
|
for line in lines:
|
||||||
|
|||||||
Reference in New Issue
Block a user