diff --git a/app/views/modern_styles.py b/app/views/modern_styles.py index 0b578ba..bd6bf1e 100644 --- a/app/views/modern_styles.py +++ b/app/views/modern_styles.py @@ -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_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_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( @@ -219,7 +220,7 @@ def apply_modern_styles() -> None: ("active", sidebar_hover), ("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( "SidebarSelected.TButton", diff --git a/app/views/panels/calman_panel.py b/app/views/panels/calman_panel.py index 3b1d1ba..060e630 100644 --- a/app/views/panels/calman_panel.py +++ b/app/views/panels/calman_panel.py @@ -92,6 +92,85 @@ def _xy_to_upvp(x: float, y: float) -> tuple[float, float]: 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]: """把 xyY 近似映射到 RGB 比例,并归一到平均值 100。""" 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 -def _style_axes(ax, title: str) -> None: - ax.set_title(title, color=_FG, fontsize=9, pad=4) - ax.set_facecolor(_AX_BG) - ax.grid(True, color=_GRID, alpha=0.35, linewidth=0.6) - ax.tick_params(colors=_FG, labelsize=8) +def _style_axes(self: "PQAutomationApp", ax, title: str) -> None: + palette = _get_calman_palette() + ax.set_title(title, color=palette["fg"], fontsize=9, pad=4) + ax.set_facecolor(palette["axes_bg"]) + 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(): - 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: """创建 CALMAN 风格灰阶测试面板,注册到 panel_manager。""" + palette = _get_calman_palette() self.calman_frame = ttk.Frame(self.content_frame) self.calman_visible = False 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.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_ax_de = fig.add_subplot(141) 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_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) self.calman_canvas = canvas @@ -181,11 +291,12 @@ def create_calman_panel(self: "PQAutomationApp") -> None: de_combo.pack(side=tk.LEFT, padx=(4, 10)) self.calman_elapsed_var = tk.StringVar(value="Step: -- s | Total: -- s") - ttk.Label( + self.calman_elapsed_label = ttk.Label( control_row, textvariable=self.calman_elapsed_var, - foreground="#d0d0d0", - ).pack(side=tk.LEFT) + foreground=palette["status_fg"], + ) + self.calman_elapsed_label.pack(side=tk.LEFT) metrics_row = ttk.Frame(chart_frame) 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_contrast_var = tk.StringVar(value="Contrast Ratio: --") self.calman_avg_gamma_var = tk.StringVar(value="Average Gamma: --") + self.calman_metric_labels = [] for idx, v in enumerate( ( self.calman_avg_de_var, @@ -202,14 +314,16 @@ def create_calman_panel(self: "PQAutomationApp") -> None: self.calman_avg_gamma_var, ) ): - tk.Label( + lbl = tk.Label( metrics_row, textvariable=v, anchor=tk.CENTER, - fg="#f2f2f2", - bg="#373737", + fg=palette["metric_tile_fg"], + bg=palette["metric_tile_bg"], 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) @@ -246,13 +360,14 @@ def create_calman_panel(self: "PQAutomationApp") -> None: ).pack(fill=tk.X, pady=2) self.calman_status_var = tk.StringVar(value="待机") - ttk.Label( + self.calman_status_label = ttk.Label( btn_col, textvariable=self.calman_status_var, - foreground="#555", + foreground=palette["status_fg"], wraplength=150, 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 = ttk.Progressbar( @@ -269,14 +384,15 @@ def create_calman_panel(self: "PQAutomationApp") -> None: self.calman_reading_var = tk.StringVar( value="x: -- y: -- Y: --\nCCT: -- ΔE: --" ) - ttk.Label( + self.calman_reading_summary_label = ttk.Label( btn_col, textvariable=self.calman_reading_var, font=("Consolas", 9), - foreground="#1f6fb2", + foreground=palette["reading_accent"], wraplength=160, justify=tk.LEFT, - ).pack(fill=tk.X, pady=(8, 0)) + ) + self.calman_reading_summary_label.pack(fill=tk.X, pady=(8, 0)) # ---------------------------- 中部:灰阶色块(Target / Actual) ---------------------------- patch_outer = ttk.LabelFrame(root, text="灰阶色块(点击可直接输出 Pattern)", padding=6) @@ -390,24 +506,26 @@ def create_calman_panel(self: "PQAutomationApp") -> None: "cd/m²: --\n" "ΔE2000: --" ) - tk.Label( + self.calman_reading_detail_label = tk.Label( left, textvariable=self.calman_reading_var, justify=tk.LEFT, font=("Consolas", 10), - fg="#e5e5e5", - bg="#323232", + fg=palette["reading_fg"], + bg=palette["reading_bg"], width=22, padx=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) xy_fig.subplots_adjust(left=0.20, right=0.96, top=0.90, bottom=0.18) xy_canvas = FigureCanvasTkAgg(xy_fig, master=left) 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)) self.calman_xy_canvas = xy_canvas @@ -458,11 +576,7 @@ def create_calman_panel(self: "PQAutomationApp") -> None: for widget in (metric_tree, data_tree): widget.bind("", lambda e: _matrix_mousewheel(self, e)) - style = ttk.Style() - 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") + _apply_calman_tree_style(self) right.bind("", lambda _e: _adaptive_matrix_columns(self)) @@ -796,6 +910,17 @@ def _adaptive_matrix_columns(self: "PQAutomationApp") -> None: def _redraw_calman_charts(self: "PQAutomationApp") -> None: """根据 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"]) pcts = [r["pct"] 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 a1 = self.calman_ax_de a1.clear() - _style_axes(a1, "DeltaE 2000") + _style_axes(self, a1, "DeltaE 2000") if pcts: a1.bar(pcts, de_vals, color="#ffcf57", width=3.5) a1.set_xlim(-2, 102) @@ -844,7 +969,7 @@ def _redraw_calman_charts(self: "PQAutomationApp") -> None: # RGB Balance 线图 a2 = self.calman_ax_rgb_line a2.clear() - _style_axes(a2, "RGB Balance") + _style_axes(self, a2, "RGB Balance") if rgb_pcts: a2.plot(rgb_pcts, rgb_r, "-", color="#ff4d4d", 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 条图(用最后一个点) a3 = self.calman_ax_rgb_bar a3.clear() - _style_axes(a3, "RGB Balance") + _style_axes(self, a3, "RGB Balance") if recs: last = recs[-1] bars = [ @@ -875,7 +1000,7 @@ def _redraw_calman_charts(self: "PQAutomationApp") -> None: # Gamma a4 = self.calman_ax_gamma a4.clear() - _style_axes(a4, "Gamma Log/Log") + _style_axes(self, a4, "Gamma Log/Log") if gamma_pcts: a4.plot(gamma_pcts, gamma_vals, "-", color="#ffe24d", linewidth=1.3) 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: + 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.clear() - _style_axes(ax, "CIE 1931 xy") + _style_axes(self, ax, "CIE 1931 xy") ax.set_xlim(0.29, 0.34) 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"]) if recs: xs = [r["x"] 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] 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() @@ -937,6 +1073,8 @@ def _update_target_strip(self: "PQAutomationApp") -> None: def _refresh_metric_table(self: "PQAutomationApp") -> None: """重绘下方矩阵表。""" + _apply_calman_tree_style(self) + palette = _get_calman_palette() 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 "-"), @@ -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_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("even", background="#ffffff") - self.calman_data_tree.tag_configure("odd", background="#f5f7fa") - self.calman_data_tree.tag_configure("even", background="#ffffff") + self.calman_metric_tree.tag_configure( + "odd", background=palette["tree_odd"], foreground=palette["tree_fg"] + ) + 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() 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: """挂载本模块的自由函数到 PQAutomationApp。""" create_calman_panel = create_calman_panel toggle_calman_panel = toggle_calman_panel + refresh_calman_theme = refresh_calman_theme diff --git a/app/views/panels/main_layout.py b/app/views/panels/main_layout.py index 3e22630..1219697 100644 --- a/app/views/panels/main_layout.py +++ b/app/views/panels/main_layout.py @@ -723,6 +723,16 @@ def _on_toggle_theme(self: "PQAutomationApp") -> None: self.apply_result_chart_theme() except Exception: 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"): try: diff --git a/app/views/pq_log_gui.py b/app/views/pq_log_gui.py index 2ff97b4..ec05083 100644 --- a/app/views/pq_log_gui.py +++ b/app/views/pq_log_gui.py @@ -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): VALID_LEVELS = {"info", "success", "warning", "error", "debug", "separator", "blank"} @@ -132,21 +181,44 @@ class PQLogGUI(ttk.Frame): def _configure_tags(self): 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("level_info", foreground=palette["accent"]) 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_error", foreground=palette["error"]) - self.log_text.tag_configure("level_debug", foreground="#7c3aed") - self.log_text.tag_configure("message", foreground=palette["fg"]) + self.log_text.tag_configure("level_debug", foreground=debug_level_color) + 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_warning", foreground=palette["warning"]) self.log_text.tag_configure("message_error", foreground=palette["error"]) - self.log_text.tag_configure("message_debug", foreground="#6d28d9") - self.log_text.tag_configure("separator", foreground=palette["muted"]) + self.log_text.tag_configure("message_debug", foreground=debug_msg_color) + self.log_text.tag_configure("separator", foreground=muted_fg) self.log_text.tag_configure("traceback", foreground=palette["error"]) 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): lines = message.splitlines() or [""] for line in lines: