继续优化UI加载速度
This commit is contained in:
@@ -25,7 +25,138 @@ def _result_bg_color() -> str:
|
||||
return "#FFFFFF"
|
||||
|
||||
|
||||
def apply_result_chart_theme(self: "PQAutomationApp"):
|
||||
def _refresh_ax_theme_in_place(ax, palette) -> None:
|
||||
"""就地更新已有坐标轴的主题色,无需清空重绘。"""
|
||||
ax.set_facecolor(palette["card_bg"])
|
||||
for spine in ax.spines.values():
|
||||
spine.set_color(palette["border"])
|
||||
ax.tick_params(axis="both", colors=palette["fg"])
|
||||
try:
|
||||
ax.xaxis.label.set_color(palette["fg"])
|
||||
ax.yaxis.label.set_color(palette["fg"])
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
if ax.title.get_text():
|
||||
ax.title.set_color(palette["fg"])
|
||||
except Exception:
|
||||
pass
|
||||
for artist in getattr(ax, "texts", []):
|
||||
try:
|
||||
artist.set_color(palette["fg"])
|
||||
except Exception:
|
||||
pass
|
||||
for line in ax.get_lines():
|
||||
# 保留曲线本身颜色,仅刷新网格等辅助线
|
||||
pass
|
||||
for gridline in ax.get_xgridlines() + ax.get_ygridlines():
|
||||
try:
|
||||
gridline.set_color(palette["border"])
|
||||
gridline.set_alpha(0.35)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
_CHART_THEME_SPECS = {
|
||||
"gamut": ("gamut_chart_frame", "gamut_fig", "gamut_canvas"),
|
||||
"gamma": ("gamma_chart_frame", "gamma_fig", "gamma_canvas"),
|
||||
"eotf": ("eotf_chart_frame", "eotf_fig", "eotf_canvas"),
|
||||
"cct": ("cct_chart_frame", "cct_fig", "cct_canvas"),
|
||||
"contrast": ("contrast_chart_frame", "contrast_fig", "contrast_canvas"),
|
||||
"accuracy": ("accuracy_chart_frame", "accuracy_fig", "accuracy_canvas"),
|
||||
}
|
||||
|
||||
|
||||
def _get_active_chart_key(self: "PQAutomationApp") -> str | None:
|
||||
"""返回当前选中的结果图 Tab 对应的 chart key。"""
|
||||
notebook = getattr(self, "chart_notebook", None)
|
||||
if notebook is None:
|
||||
return None
|
||||
try:
|
||||
selected = notebook.select()
|
||||
if not selected:
|
||||
return "gamut"
|
||||
for key, (frame_attr, _, _) in _CHART_THEME_SPECS.items():
|
||||
frame = getattr(self, frame_attr, None)
|
||||
if frame is not None and str(frame) == selected:
|
||||
return key
|
||||
except Exception:
|
||||
pass
|
||||
return "gamut"
|
||||
|
||||
|
||||
def _apply_fig_theme_colors(fig, canvas, palette, bg) -> None:
|
||||
"""更新 Figure/Axes 主题色,不触发绘制。"""
|
||||
fig.patch.set_facecolor(bg)
|
||||
suptitle = getattr(fig, "_suptitle", None)
|
||||
if suptitle is not None:
|
||||
try:
|
||||
suptitle.set_color(palette["fg"])
|
||||
except Exception:
|
||||
pass
|
||||
for ax in fig.axes:
|
||||
_refresh_ax_theme_in_place(ax, palette)
|
||||
if canvas is not None:
|
||||
try:
|
||||
widget = canvas.get_tk_widget()
|
||||
widget.configure(bg=bg, highlightthickness=0)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def flush_chart_theme_if_dirty(self: "PQAutomationApp", chart_key: str | None = None) -> None:
|
||||
"""绘制主题已更新但尚未 draw 的图表(通常在切换到该 Tab 时调用)。"""
|
||||
dirty = getattr(self, "_charts_theme_dirty", None)
|
||||
if not dirty:
|
||||
return
|
||||
keys = [chart_key] if chart_key and chart_key in dirty else list(dirty)
|
||||
for key in keys:
|
||||
spec = _CHART_THEME_SPECS.get(key)
|
||||
if spec is None:
|
||||
continue
|
||||
canvas = getattr(self, spec[2], None)
|
||||
if canvas is None:
|
||||
continue
|
||||
try:
|
||||
canvas.draw()
|
||||
except Exception:
|
||||
pass
|
||||
dirty.discard(key)
|
||||
|
||||
|
||||
def refresh_result_charts_theme(
|
||||
self: "PQAutomationApp", *, sync_draw: bool = True, visible_only: bool = True
|
||||
) -> None:
|
||||
"""就地刷新结果区 Matplotlib 图表主题色;默认仅绘制当前可见 Tab。"""
|
||||
palette = get_theme_palette()
|
||||
bg = _result_bg_color()
|
||||
active_key = _get_active_chart_key(self) if visible_only else None
|
||||
|
||||
dirty = getattr(self, "_charts_theme_dirty", None)
|
||||
if dirty is None:
|
||||
dirty = set()
|
||||
self._charts_theme_dirty = dirty
|
||||
|
||||
for key, (_, fig_attr, canvas_attr) in _CHART_THEME_SPECS.items():
|
||||
fig = getattr(self, fig_attr, None)
|
||||
canvas = getattr(self, canvas_attr, None)
|
||||
if fig is None or canvas is None:
|
||||
continue
|
||||
|
||||
_apply_fig_theme_colors(fig, canvas, palette, bg)
|
||||
|
||||
should_draw = sync_draw and ((not visible_only) or key == active_key)
|
||||
if should_draw:
|
||||
try:
|
||||
canvas.draw()
|
||||
except Exception:
|
||||
pass
|
||||
dirty.discard(key)
|
||||
else:
|
||||
dirty.add(key)
|
||||
|
||||
|
||||
def apply_result_chart_theme(self: "PQAutomationApp", *, sync_draw: bool = False):
|
||||
"""统一刷新结果图画布背景,使其跟随浅/深色主题。"""
|
||||
bg = _result_bg_color()
|
||||
|
||||
@@ -50,7 +181,10 @@ def apply_result_chart_theme(self: "PQAutomationApp"):
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
canvas.draw_idle()
|
||||
if sync_draw:
|
||||
canvas.draw()
|
||||
else:
|
||||
canvas.draw_idle()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -1111,6 +1245,9 @@ def on_chart_tab_changed(self: "PQAutomationApp", event):
|
||||
if not selected_tab:
|
||||
return
|
||||
self._last_tab_index = self.chart_notebook.index(selected_tab)
|
||||
active_key = _get_active_chart_key(self)
|
||||
if active_key:
|
||||
flush_chart_theme_if_dirty(self, active_key)
|
||||
except Exception as e:
|
||||
self.log_gui.log(f"Tab切换事件处理失败: {str(e)}", level="error")
|
||||
|
||||
@@ -1129,6 +1266,8 @@ class ChartFrameMixin:
|
||||
init_contrast_chart = init_contrast_chart
|
||||
init_accuracy_chart = init_accuracy_chart
|
||||
apply_result_chart_theme = apply_result_chart_theme
|
||||
refresh_result_charts_theme = refresh_result_charts_theme
|
||||
flush_chart_theme_if_dirty = flush_chart_theme_if_dirty
|
||||
_init_accuracy_result_table = _init_accuracy_result_table
|
||||
clear_accuracy_result_table = clear_accuracy_result_table
|
||||
update_accuracy_result_table = update_accuracy_result_table
|
||||
|
||||
@@ -245,10 +245,20 @@ def _show_main_content(self: "PQAutomationApp") -> None:
|
||||
|
||||
def _notify_panel_shown(self: "PQAutomationApp", panel_name: str) -> None:
|
||||
"""面板显示后的轻量刷新(延迟执行,不阻塞切换)。"""
|
||||
if getattr(self, "_theme_transition", False):
|
||||
return
|
||||
if panel_name == "calman":
|
||||
refresh = getattr(self, "_on_calman_panel_shown", None)
|
||||
if refresh is not None:
|
||||
self.root.after_idle(refresh)
|
||||
return
|
||||
if panel_name == "ai_image" and getattr(self, "_ai_image_theme_dirty", False):
|
||||
self.root.after_idle(lambda: self.refresh_ai_image_theme())
|
||||
return
|
||||
if panel_name == "log" and hasattr(self, "log_gui") and hasattr(
|
||||
self.log_gui, "refresh_log_theme"
|
||||
):
|
||||
self.root.after_idle(self.log_gui.refresh_log_theme)
|
||||
|
||||
|
||||
def _show_stack_panel(self: "PQAutomationApp", panel_name: str) -> None:
|
||||
|
||||
@@ -1220,6 +1220,10 @@ def _build_ucd_resized_image(image_path: str, target_w: int, target_h: int) -> s
|
||||
|
||||
def refresh_ai_image_theme(self: "PQAutomationApp"):
|
||||
"""刷新 AI 图片面板中的主题相关控件。"""
|
||||
if getattr(self, "current_panel", None) != "ai_image":
|
||||
self._ai_image_theme_dirty = True
|
||||
return
|
||||
self._ai_image_theme_dirty = False
|
||||
if hasattr(self, "_apply_ai_image_list_style"):
|
||||
self._apply_ai_image_list_style()
|
||||
tip = getattr(self, "_ai_image_tooltip", None)
|
||||
|
||||
@@ -699,9 +699,14 @@ def _on_calman_panel_shown(self: "PQAutomationApp") -> None:
|
||||
"""面板切到前台时只做轻量刷新,不重绘 Matplotlib。"""
|
||||
_refresh_calman_config_summary(self)
|
||||
_schedule_adaptive_matrix_columns(self)
|
||||
if getattr(self, "_calman_theme_dirty", False):
|
||||
_redraw_calman_charts(self)
|
||||
self._calman_theme_dirty = False
|
||||
|
||||
|
||||
def _schedule_adaptive_matrix_columns(self: "PQAutomationApp") -> None:
|
||||
if getattr(self, "_theme_transition", False):
|
||||
return
|
||||
job = getattr(self, "_calman_matrix_col_job", None)
|
||||
if job is not None:
|
||||
try:
|
||||
@@ -1116,7 +1121,7 @@ def _adaptive_matrix_columns(self: "PQAutomationApp") -> None:
|
||||
self.calman_data_tree.column(str(p), width=width, minwidth=min_w, stretch=False)
|
||||
|
||||
|
||||
def _redraw_calman_charts(self: "PQAutomationApp") -> None:
|
||||
def _redraw_calman_charts(self: "PQAutomationApp", *, sync_draw: bool = False) -> None:
|
||||
"""根据 calman_results 重绘四张图和 xy 散点。"""
|
||||
palette = _get_calman_palette()
|
||||
if hasattr(self, "calman_fig"):
|
||||
@@ -1236,12 +1241,15 @@ def _redraw_calman_charts(self: "PQAutomationApp") -> None:
|
||||
a4.set_ylim(1.8, 2.8)
|
||||
a4.set_xlabel("", fontsize=8)
|
||||
|
||||
self.calman_canvas.draw_idle()
|
||||
if sync_draw or getattr(self, "_theme_transition", False):
|
||||
self.calman_canvas.draw()
|
||||
else:
|
||||
self.calman_canvas.draw_idle()
|
||||
|
||||
_redraw_xy_chart(self)
|
||||
_redraw_xy_chart(self, sync_draw=sync_draw)
|
||||
|
||||
|
||||
def _redraw_xy_chart(self: "PQAutomationApp") -> None:
|
||||
def _redraw_xy_chart(self: "PQAutomationApp", *, sync_draw: bool = False) -> None:
|
||||
palette = _get_calman_palette()
|
||||
if hasattr(self, "calman_xy_fig"):
|
||||
self.calman_xy_fig.patch.set_facecolor(palette["figure_bg"])
|
||||
@@ -1268,7 +1276,10 @@ def _redraw_xy_chart(self: "PQAutomationApp") -> None:
|
||||
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=_mix(palette["fg"], palette["axes_bg"], 0.4), linewidth=0.8)
|
||||
self.calman_xy_canvas.draw_idle()
|
||||
if sync_draw or getattr(self, "_theme_transition", False):
|
||||
self.calman_xy_canvas.draw()
|
||||
else:
|
||||
self.calman_xy_canvas.draw_idle()
|
||||
|
||||
|
||||
def _update_actual_strip(self: "PQAutomationApp") -> None:
|
||||
@@ -1360,11 +1371,18 @@ def _refresh_metric_table(self: "PQAutomationApp") -> None:
|
||||
self.calman_table_ysb.set(first, last)
|
||||
|
||||
|
||||
def refresh_calman_theme(self: "PQAutomationApp") -> None:
|
||||
def refresh_calman_theme(self: "PQAutomationApp", *, redraw_charts: bool | None = None) -> None:
|
||||
"""主题切换后刷新 Calman 灰阶调试面板的表格与图表颜色。"""
|
||||
if not hasattr(self, "calman_frame"):
|
||||
return
|
||||
|
||||
if getattr(self, "current_panel", None) != "calman":
|
||||
self._calman_theme_dirty = True
|
||||
return
|
||||
|
||||
if redraw_charts is None:
|
||||
redraw_charts = True
|
||||
|
||||
palette = _get_calman_palette()
|
||||
|
||||
if hasattr(self, "calman_elapsed_label"):
|
||||
@@ -1388,7 +1406,11 @@ def refresh_calman_theme(self: "PQAutomationApp") -> None:
|
||||
|
||||
_refresh_metric_table(self)
|
||||
_refresh_calman_config_summary(self)
|
||||
_redraw_calman_charts(self)
|
||||
if redraw_charts:
|
||||
_redraw_calman_charts(self, sync_draw=getattr(self, "_theme_transition", False))
|
||||
self._calman_theme_dirty = False
|
||||
else:
|
||||
self._calman_theme_dirty = True
|
||||
|
||||
|
||||
class CalmanPanelMixin:
|
||||
|
||||
@@ -172,6 +172,14 @@ def _apply_custom_result_theme(self: "PQAutomationApp"):
|
||||
|
||||
def refresh_custom_template_theme(self: "PQAutomationApp"):
|
||||
"""刷新客户模板结果表的主题色。"""
|
||||
notebook = getattr(self, "chart_notebook", None)
|
||||
tab_frame = getattr(self, "custom_template_tab_frame", None)
|
||||
if notebook is not None and tab_frame is not None:
|
||||
try:
|
||||
if str(tab_frame) not in notebook.tabs():
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
_apply_custom_result_theme(self)
|
||||
|
||||
|
||||
|
||||
@@ -869,9 +869,113 @@ def _refresh_theme_toggle_label(self: "PQAutomationApp") -> None:
|
||||
self.theme_toggle_btn.configure(text="切换深色模式")
|
||||
|
||||
|
||||
def _replot_charts_for_current_theme(self: "PQAutomationApp") -> None:
|
||||
"""刷新结果区图表配色(仅同步绘制当前可见 Tab)。"""
|
||||
if hasattr(self, "refresh_result_charts_theme"):
|
||||
try:
|
||||
self.refresh_result_charts_theme(sync_draw=True, visible_only=True)
|
||||
return
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
test_type = None
|
||||
if hasattr(self, "config"):
|
||||
test_type = getattr(self.config, "current_test_type", None)
|
||||
|
||||
snapshots = {}
|
||||
if test_type and hasattr(self, "_chart_snapshots"):
|
||||
snapshots = self._chart_snapshots.get(test_type) or {}
|
||||
|
||||
if not snapshots:
|
||||
if hasattr(self, "apply_result_chart_theme"):
|
||||
self.apply_result_chart_theme(sync_draw=True)
|
||||
return
|
||||
|
||||
for chart_name, args in snapshots.items():
|
||||
plot_fn = getattr(self, f"plot_{chart_name}", None)
|
||||
if plot_fn:
|
||||
try:
|
||||
plot_fn(*args)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _cancel_pending_ui_jobs(self: "PQAutomationApp") -> None:
|
||||
"""取消可能在主题切换后继续触发的延迟 UI 任务。"""
|
||||
for attr in ("_calman_matrix_col_job", "_chart_restore_job"):
|
||||
job = getattr(self, attr, None)
|
||||
if job is None:
|
||||
continue
|
||||
try:
|
||||
self.root.after_cancel(job)
|
||||
except Exception:
|
||||
pass
|
||||
setattr(self, attr, None)
|
||||
|
||||
|
||||
def _refresh_all_theme_widgets(self: "PQAutomationApp") -> None:
|
||||
"""同步刷新主题相关控件:优先处理可见区域,隐藏面板延后。"""
|
||||
_refresh_theme_toggle_label(self)
|
||||
|
||||
# 侧栏 / 连接指示 / 选中态 —— 始终可见,必须立即更新
|
||||
for method_name in (
|
||||
"refresh_tool_panel_sidebar_theme",
|
||||
"refresh_connection_indicators",
|
||||
):
|
||||
method = getattr(self, method_name, None)
|
||||
if method is not None:
|
||||
try:
|
||||
method()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
current_panel = getattr(self, "current_panel", None)
|
||||
|
||||
if current_panel == "ai_image" and hasattr(self, "refresh_ai_image_theme"):
|
||||
try:
|
||||
self.refresh_ai_image_theme()
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
self._ai_image_theme_dirty = True
|
||||
|
||||
if current_panel == "single_step" and hasattr(self, "refresh_single_step_theme"):
|
||||
try:
|
||||
self.refresh_single_step_theme()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if current_panel == "log" and 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_custom_template_theme"):
|
||||
try:
|
||||
self.refresh_custom_template_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:
|
||||
self.update_sidebar_selection()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
_replot_charts_for_current_theme(self)
|
||||
|
||||
|
||||
def _on_toggle_theme(self: "PQAutomationApp") -> None:
|
||||
"""切换主题:重新应用 ttk 样式并刷新所有自定义样式相关的标签。"""
|
||||
# 在测试进行时禁止切换主题,避免影响测量稳定性
|
||||
"""切换主题:保持窗口可见,同步批量刷新避免卡顿与分批变色。"""
|
||||
if getattr(self, "testing", False):
|
||||
try:
|
||||
if hasattr(self, "log_gui"):
|
||||
@@ -881,67 +985,14 @@ def _on_toggle_theme(self: "PQAutomationApp") -> None:
|
||||
return
|
||||
|
||||
from app.views.theme_manager import toggle_theme
|
||||
toggle_theme()
|
||||
# apply_modern_styles()
|
||||
_refresh_theme_toggle_label(self)
|
||||
if hasattr(self, "refresh_tool_panel_sidebar_theme"):
|
||||
try:
|
||||
self.refresh_tool_panel_sidebar_theme()
|
||||
except Exception:
|
||||
pass
|
||||
if hasattr(self, "refresh_connection_indicators"):
|
||||
try:
|
||||
self.refresh_connection_indicators()
|
||||
except Exception:
|
||||
pass
|
||||
if hasattr(self, "apply_result_chart_theme"):
|
||||
try:
|
||||
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_ai_image_theme"):
|
||||
try:
|
||||
self.refresh_ai_image_theme()
|
||||
except Exception:
|
||||
pass
|
||||
if hasattr(self, "refresh_single_step_theme"):
|
||||
try:
|
||||
self.refresh_single_step_theme()
|
||||
except Exception:
|
||||
pass
|
||||
if hasattr(self, "refresh_custom_template_theme"):
|
||||
try:
|
||||
self.refresh_custom_template_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:
|
||||
self.update_sidebar_selection()
|
||||
except Exception:
|
||||
pass
|
||||
# 以新的 dark_mode 值重绘当前测试类型的所有图表
|
||||
if hasattr(self, "_chart_snapshots") and hasattr(self, "config"):
|
||||
test_type = getattr(self.config, "current_test_type", None)
|
||||
if test_type:
|
||||
snapshots = self._chart_snapshots.get(test_type, {})
|
||||
for chart_name, args in snapshots.items():
|
||||
plot_fn = getattr(self, f"plot_{chart_name}", None)
|
||||
if plot_fn:
|
||||
try:
|
||||
plot_fn(*args)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
_cancel_pending_ui_jobs(self)
|
||||
self._theme_transition = True
|
||||
try:
|
||||
toggle_theme()
|
||||
_refresh_all_theme_widgets(self)
|
||||
finally:
|
||||
self._theme_transition = False
|
||||
|
||||
|
||||
def update_config_info_display(self: "PQAutomationApp"):
|
||||
|
||||
@@ -156,6 +156,7 @@ class PQAutomationApp(
|
||||
self.current_panel = None # 当前显示的面板名称
|
||||
self.panels = {} # 存储所有面板的信息
|
||||
self.log_visible = False
|
||||
self._theme_transition = False
|
||||
|
||||
# 创建左侧面板
|
||||
self.left_frame = ttk.Frame(self.main_frame, width=208)
|
||||
|
||||
Reference in New Issue
Block a user