Files
pqAutomationApp/app/views/panel_manager.py
2026-06-22 10:31:19 +08:00

271 lines
8.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""面板管理器Step 6 重构)。
register_panel / show_panel / hide_all_panels —— 在右侧栏不同浮动面板间切换。
"""
from __future__ import annotations
import tkinter as tk
import tkinter.font as tkfont
from typing import TYPE_CHECKING, Any, Callable
if TYPE_CHECKING:
from pqAutomationApp import PQAutomationApp
def create_tool_panel_sidebar_item(
self: "PQAutomationApp", parent: tk.Misc, label: str, command: Callable[[], None]
) -> dict[str, Any]:
"""创建工具面板侧栏项:名称左对齐,选中时 × 贴右端,整体可点击。"""
from app.views.modern_styles import _contrast_text, _is_dark, get_theme_palette
row = tk.Frame(parent, bd=0, highlightthickness=0, cursor="hand2")
name_lbl = tk.Label(row, text=label, anchor=tk.W, bd=0, padx=18, pady=9)
close_lbl = tk.Label(
row,
text="×",
anchor=tk.CENTER,
bd=0,
padx=0,
pady=9,
width=2,
font=("Segoe UI", 9),
)
name_lbl.pack(side=tk.LEFT, fill=tk.X, expand=True)
item: dict[str, Any] = {
"row": row,
"name_label": name_lbl,
"close_label": close_lbl,
"label": label,
"selected": False,
"hover": False,
}
def _apply_style() -> None:
palette = get_theme_palette()
sidebar_bg = palette["sidebar_bg"]
sidebar_hover = palette["sidebar_hover"]
sidebar_selected = palette["sidebar_selected"]
sidebar_fg = palette["sidebar_fg"]
selected_fg = _contrast_text(
sidebar_selected,
dark_text=palette["badge_fg"],
light_text=sidebar_fg,
)
if item["selected"]:
bg = sidebar_selected
fg = selected_fg
name_font = ("Segoe UI Semibold", 10)
elif item["hover"]:
bg = sidebar_hover
fg = "#ffffff" if _is_dark(sidebar_hover) else sidebar_fg
name_font = ("Segoe UI", 10)
else:
bg = sidebar_bg
fg = sidebar_fg
name_font = ("Segoe UI", 10)
row.configure(bg=bg)
for widget in (name_lbl, close_lbl):
widget.configure(
bg=bg,
fg=fg,
activebackground=bg,
activeforeground=fg,
)
name_lbl.configure(font=name_font)
def _truncate_name() -> None:
full = item["label"]
if not item["selected"]:
name_lbl.configure(text=full)
return
row.update_idletasks()
close_w = close_lbl.winfo_width() if close_lbl.winfo_ismapped() else 20
avail = max(24, row.winfo_width() - close_w - 18 - 14)
font = tkfont.Font(font=name_lbl.cget("font"))
if font.measure(full) <= avail:
name_lbl.configure(text=full)
return
for i in range(len(full), 0, -1):
candidate = full[:i].rstrip() + ""
if font.measure(candidate) <= avail:
name_lbl.configure(text=candidate)
return
name_lbl.configure(text="")
def set_selected(selected: bool) -> None:
item["selected"] = selected
if selected:
close_lbl.pack(side=tk.RIGHT, padx=(0, 14))
else:
close_lbl.pack_forget()
_apply_style()
row.after_idle(_truncate_name)
def _on_click(_event=None) -> None:
command()
def _on_enter(_event=None) -> None:
if not item["selected"]:
item["hover"] = True
_apply_style()
def _on_leave(_event=None) -> None:
px, py = row.winfo_pointerxy()
rx, ry = row.winfo_rootx(), row.winfo_rooty()
rw, rh = row.winfo_width(), row.winfo_height()
if rx <= px < rx + rw and ry <= py < ry + rh:
return
item["hover"] = False
_apply_style()
def _on_configure(_event=None) -> None:
if item["selected"]:
_truncate_name()
for widget in (row, name_lbl, close_lbl):
widget.bind("<Button-1>", _on_click)
widget.bind("<Enter>", _on_enter)
widget.bind("<Leave>", _on_leave)
row.bind("<Configure>", _on_configure)
item["set_selected"] = set_selected
item["apply_theme"] = _apply_style
item["pack"] = lambda **kw: row.pack(fill=tk.X, padx=0, pady=1, **kw)
_apply_style()
return item
def _set_tool_panel_sidebar_selected(
panel_info: dict[str, Any], selected: bool
) -> None:
sidebar_item = panel_info.get("sidebar_item")
if sidebar_item is not None:
sidebar_item["set_selected"](selected)
return
button = panel_info.get("button")
if button is not None:
button.configure(
style="SidebarSelected.TButton" if selected else "Sidebar.TButton"
)
def refresh_tool_panel_sidebar_theme(self: "PQAutomationApp") -> None:
"""主题切换后刷新工具面板侧栏项配色。"""
for panel_info in self.panels.values():
sidebar_item = panel_info.get("sidebar_item")
if sidebar_item is not None:
sidebar_item["apply_theme"]()
def register_panel(self: "PQAutomationApp", panel_name, frame, button, visible_attr):
"""注册一个面板到管理系统"""
self.panels[panel_name] = {
"frame": frame,
"button": button,
"button_label": None,
"sidebar_item": None,
"visible_attr": visible_attr,
}
def show_panel(self: "PQAutomationApp", panel_name):
"""显示指定面板,隐藏其他所有面板"""
if panel_name not in self.panels:
return
# 如果当前面板就是要显示的面板,则隐藏它
if self.current_panel == panel_name:
self.hide_all_panels()
# 如果当前测试类型是 Local Dimming则在关闭日志等面板后自动恢复 Local Dimming 面板
try:
if (
getattr(self, "config", None)
and getattr(self.config, "current_test_type", None) == "local_dimming"
and panel_name != "local_dimming"
):
self.show_panel("local_dimming")
except Exception:
pass
return
# 隐藏所有面板
self.hide_all_panels()
# 显示指定面板
panel_info = self.panels[panel_name]
# 隐藏主内容区域。
# Local Dimming 作为并列测试类型时,需要保留顶部配置区,
# 让用户在面板上方直接看到并修改配置项。
if panel_name == "local_dimming":
# 重新按“自适应高度”布局顶部配置区,避免其占用可扩展空间把
# Local Dimming 主面板整体向下挤出大块空白。
self.control_frame_top.pack_forget()
self.control_frame_top.pack(
side=tk.TOP, fill=tk.X, expand=False, padx=0, pady=5
)
self.control_frame_middle.pack_forget()
self.control_frame_bottom.pack_forget()
else:
self.control_frame_top.pack_forget()
self.control_frame_middle.pack_forget()
self.control_frame_bottom.pack_forget()
# 显示目标面板
panel_info["frame"].pack(side=tk.TOP, fill=tk.BOTH, expand=True, padx=5, pady=5)
# 更新侧栏选中样式
_set_tool_panel_sidebar_selected(panel_info, True)
# 更新状态
setattr(self, panel_info["visible_attr"], True)
self.current_panel = panel_name
def hide_all_panels(self: "PQAutomationApp"):
"""隐藏所有面板,显示主内容区域"""
# 隐藏所有注册的面板
for panel_name, panel_info in self.panels.items():
panel_info["frame"].pack_forget()
_set_tool_panel_sidebar_selected(panel_info, False)
setattr(self, panel_info["visible_attr"], False)
# 显示主内容区域
self.control_frame_top.pack(
side=tk.TOP, fill=tk.BOTH, expand=True, padx=0, pady=5
)
self.control_frame_middle.pack(
side=tk.TOP, fill=tk.BOTH, expand=True, padx=0, pady=5
)
self.control_frame_bottom.pack(
side=tk.TOP, fill=tk.BOTH, expand=True, padx=0, pady=5
)
self.current_panel = None
if hasattr(self, "_sync_save_button_state"):
try:
self._sync_save_button_state()
except Exception:
pass
class PanelManagerMixin:
"""由 tools/refactor_to_mixins.py 自动生成。
把本模块的自由函数挂到 PQAutomationApp 上,便于 F12 跳转与类型推断。
"""
create_tool_panel_sidebar_item = create_tool_panel_sidebar_item
register_panel = register_panel
show_panel = show_panel
hide_all_panels = hide_all_panels
refresh_tool_panel_sidebar_theme = refresh_tool_panel_sidebar_theme