屏模组添加colorinfo设置、修改配置项UI样式

This commit is contained in:
xinzhu.yin
2026-05-28 10:20:17 +08:00
parent c63b9ef615
commit f8f2d471e5
8 changed files with 752 additions and 155 deletions

View File

@@ -14,50 +14,129 @@ if TYPE_CHECKING:
from pqAutomationApp import PQAutomationApp
# ---------------------------------------------------------------------------
# 内部工具:现代化卡片容器
# ---------------------------------------------------------------------------
def _make_card(parent, icon: str, title: str) -> ttk.Frame:
"""构造一个"卡片"容器(圆角描边 + 顶部图标/标题)。
返回值是 outer card frame通过 ``outer._body`` 拿到内部 body Frame
旧代码里习惯把控件挂在 ``self.connection_frame`` 等"卡片本身"上,所以
保留 outer 作为对外别名,但实际控件父级换成 body 以避开边框 padding。
"""
outer = ttk.Frame(parent, style="Card.TFrame", padding=12)
header = ttk.Frame(outer, style="CardInner.TFrame")
header.pack(fill="x", pady=(0, 8))
ttk.Label(header, text=icon, style="CardIcon.TLabel").pack(side="left", padx=(0, 8))
ttk.Label(header, text=title, style="CardTitle.TLabel").pack(side="left")
body = ttk.Frame(outer, style="CardInner.TFrame")
body.pack(fill="both", expand=True)
outer._body = body # type: ignore[attr-defined]
return outer
def create_floating_config_panel(self: "PQAutomationApp"):
"""创建右上角悬浮配置"""
"""创建顶部"配置"现代化折叠面板。
布局变化vs 旧版):
- 用 Unicode chevron + 整条 header 可点击折叠/展开;
- header 上额外显示折叠状态预览(``config_preview_var``
- header 右侧承载常驻操作工具条(开始/停止/保存 等),不再放中部;
- 内部三个区段从 LabelFrame 改为统一的 Card 样式。
"""
cf = CollapsingFrame(self.control_frame_top)
cf.pack(fill="both")
# 创建悬浮框主容器
self._config_collapsing = cf
# 配置项主体容器(卡片宿主)
self.config_panel_frame = ttk.Frame(cf)
cf.add(self.config_panel_frame, title="配置项")
# 创建一个统一的frame来替代选项卡控件
# 折叠预览:呈现"测试类型 · 已选测试项"
self.config_preview_var = tk.StringVar(value="")
# header 右侧工具条占位 —— create_operation_frame 之后向这里挂按钮
self.toolbar_actions_frame: ttk.Frame | None = None
def _header_actions(parent: ttk.Frame):
# 暴露给 create_operation_frame 使用
self.toolbar_actions_frame = parent
cf.add(
self.config_panel_frame,
title="配置项",
preview_textvariable=self.config_preview_var,
header_actions=_header_actions,
)
# 卡片三栏
self.config_content_frame = ttk.Frame(self.config_panel_frame)
self.config_content_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self.config_content_frame.pack(fill=tk.BOTH, expand=True, padx=8, pady=8)
# 创建一个横向排列的Frame
config_row_frame = ttk.Frame(self.config_content_frame)
config_row_frame.pack(fill=tk.X, expand=False, padx=5, pady=5)
config_row_frame.pack(fill=tk.X, expand=False)
# 创建连接内容区域
self.connection_frame = ttk.LabelFrame(config_row_frame, text="设备连接")
self.connection_frame.pack(
side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5
)
# 设备连接 卡片
connection_card = _make_card(config_row_frame, icon="\U0001F4E1", title="设备连接") # 📡
connection_card.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 6))
self.connection_frame = connection_card._body # type: ignore[attr-defined]
# 创建测试项目区域
self.test_items_frame = ttk.LabelFrame(config_row_frame, text="测试项目")
self.test_items_frame.pack(
side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5
)
# 测试项目 卡片
test_items_card = _make_card(config_row_frame, icon="\u2714", title="测试项目") # ✔
test_items_card.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=6)
self.test_items_frame = test_items_card._body # type: ignore[attr-defined]
# 创建信号格式区域
self.signal_format_frame = ttk.LabelFrame(config_row_frame, text="信号格式")
self.signal_format_frame.pack(
side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5
)
# 信号格式 卡片
signal_card = _make_card(config_row_frame, icon="\u2699", title="信号格式") # ⚙
signal_card.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(6, 0))
self.signal_format_frame = signal_card._body # type: ignore[attr-defined]
# 创建连接内容
# 创建卡片内部内容(沿用旧函数,父级已是 body Frame
self.create_connection_content()
# 创建测试项目内容
self.create_test_items_content()
# 创建信号格式内容
self.create_signal_format_content()
# 默认收起 —— 与旧版行为保持一致
self.config_panel_frame.grid_remove()
self.config_panel_frame.btn.configure(image="closed")
# 初始化预览文本
refresh_config_preview(self)
def refresh_config_preview(self: "PQAutomationApp") -> None:
"""根据当前测试类型 + 已选测试项刷新折叠预览。"""
if not hasattr(self, "config_preview_var"):
return
type_labels = {
"screen_module": "屏模组",
"sdr_movie": "SDR Movie",
"hdr_movie": "HDR Movie",
}
current_type = getattr(self.config, "current_test_type", "")
type_label = type_labels.get(current_type, "")
item_labels = []
if current_type and hasattr(self, "test_items"):
info = self.test_items.get(current_type, {})
label_map = {code: name for name, code in info.get("items", [])}
if hasattr(self, "test_vars"):
for code, var in self.test_vars.items():
try:
if var.get():
item_labels.append(label_map.get(code, code))
except Exception:
pass
parts = []
if type_label:
parts.append(f"[{type_label}]")
if item_labels:
# 限制宽度,避免顶部条过挤
shown = item_labels[:5]
suffix = " \u2026" if len(item_labels) > 5 else ""
parts.append(" \u00b7 ".join(shown) + suffix)
self.config_preview_var.set(" ".join(parts))
def create_test_items_content(self: "PQAutomationApp"):
"""创建测试项目选项卡内容"""
@@ -109,13 +188,17 @@ def create_signal_format_content(self: "PQAutomationApp"):
# ==================== 屏模组格式设置 ====================
self.screen_module_signal_frame = ttk.Frame(self.signal_tabs)
self.screen_module_signal_frame.grid_columnconfigure(0, weight=1)
self.screen_module_signal_frame.grid_columnconfigure(0, weight=0)
self.screen_module_signal_frame.grid_columnconfigure(1, weight=1)
self.signal_tabs.add(self.screen_module_signal_frame, text="屏模组测试")
screen_cfg = self.config.current_test_types.get("screen_module", {})
self.screen_module_timing_var = tk.StringVar(
value=self.config.current_test_types[self.config.current_test_type][
"timing"
]
value=screen_cfg.get("timing", "DMT 1920x 1080 @ 60Hz")
)
ttk.Label(self.screen_module_signal_frame, text="分辨率:").grid(
row=0, column=0, sticky=tk.W, padx=5, pady=2
)
screen_module_timing_combo = ttk.Combobox(
self.screen_module_signal_frame,
@@ -126,7 +209,83 @@ def create_signal_format_content(self: "PQAutomationApp"):
screen_module_timing_combo.bind(
"<<ComboboxSelected>>", self.on_screen_module_timing_changed
)
screen_module_timing_combo.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
screen_module_timing_combo.grid(row=0, column=1, sticky=tk.W, padx=5, pady=2)
ttk.Label(self.screen_module_signal_frame, text="色彩空间:").grid(
row=1, column=0, sticky=tk.W, padx=5, pady=2
)
self.screen_module_color_space_var = tk.StringVar(
value=screen_cfg.get("colorimetry", "sRGB")
)
screen_module_color_space_combo = ttk.Combobox(
self.screen_module_signal_frame,
textvariable=self.screen_module_color_space_var,
values=["sRGB", "BT.709", "BT.601", "BT.2020", "DCI-P3"],
width=10,
state="readonly",
)
screen_module_color_space_combo.bind(
"<<ComboboxSelected>>", self.on_screen_module_signal_format_changed
)
screen_module_color_space_combo.grid(row=1, column=1, sticky=tk.W, padx=5, pady=2)
ttk.Label(self.screen_module_signal_frame, text="数据范围:").grid(
row=2, column=0, sticky=tk.W, padx=5, pady=2
)
self.screen_module_data_range_var = tk.StringVar(
value=screen_cfg.get("data_range", UCDEnum.SignalFormat.DataRange.FULL)
)
screen_module_data_range_combo = ttk.Combobox(
self.screen_module_signal_frame,
textvariable=self.screen_module_data_range_var,
values=UCDEnum.SignalFormat.DataRange.get_list(),
width=10,
state="readonly",
)
screen_module_data_range_combo.bind(
"<<ComboboxSelected>>", self.on_screen_module_signal_format_changed
)
screen_module_data_range_combo.grid(row=2, column=1, sticky=tk.W, padx=5, pady=2)
default_screen_bpc = int(screen_cfg.get("bpc", 8))
default_screen_bit_depth = (
f"{default_screen_bpc}bit"
if f"{default_screen_bpc}bit" in UCDEnum.SignalFormat.BitDepth.get_list()
else UCDEnum.SignalFormat.BitDepth.BIT_8
)
ttk.Label(self.screen_module_signal_frame, text="编码位深:").grid(
row=3, column=0, sticky=tk.W, padx=5, pady=2
)
self.screen_module_bit_depth_var = tk.StringVar(value=default_screen_bit_depth)
screen_module_bit_depth_combo = ttk.Combobox(
self.screen_module_signal_frame,
textvariable=self.screen_module_bit_depth_var,
values=UCDEnum.SignalFormat.BitDepth.get_list(),
width=10,
state="readonly",
)
screen_module_bit_depth_combo.bind(
"<<ComboboxSelected>>", self.on_screen_module_signal_format_changed
)
screen_module_bit_depth_combo.grid(row=3, column=1, sticky=tk.W, padx=5, pady=2)
ttk.Label(self.screen_module_signal_frame, text="色彩格式:").grid(
row=4, column=0, sticky=tk.W, padx=5, pady=2
)
self.screen_module_output_format_var = tk.StringVar(
value=screen_cfg.get("color_format", UCDEnum.SignalFormat.OutputFormat.RGB)
)
screen_module_output_format_combo = ttk.Combobox(
self.screen_module_signal_frame,
textvariable=self.screen_module_output_format_var,
values=UCDEnum.SignalFormat.OutputFormat.get_list(),
width=10,
state="readonly",
)
screen_module_output_format_combo.bind(
"<<ComboboxSelected>>", self.on_screen_module_signal_format_changed
)
screen_module_output_format_combo.grid(row=4, column=1, sticky=tk.W, padx=5, pady=2)
# ==================== SDR信号格式设置 ====================
self.sdr_signal_frame = ttk.Frame(self.signal_tabs)
@@ -578,57 +737,87 @@ def update_config_info_display(self: "PQAutomationApp"):
def create_operation_frame(self: "PQAutomationApp"):
"""创建操作按钮区域"""
operation_frame = ttk.Frame(self.control_frame_top)
operation_frame.pack(fill=tk.X, padx=5, pady=10)
"""创建操作按钮区域
新布局:按钮挂到配置项 header 右侧常驻工具条 ``self.toolbar_actions_frame``。
若该容器尚未创建(极端情况下顺序异常),回退到原 control_frame_top 位置,
确保按钮始终可见、调用方代码不破。
"""
parent = getattr(self, "toolbar_actions_frame", None)
fallback = parent is None
if fallback:
parent = ttk.Frame(self.control_frame_top)
parent.pack(fill=tk.X, padx=5, pady=8)
# 用 bootstyle 取代旧 style="xxx.TButton",跟随主题;
# 给按钮统一加 padding触摸友好。
btn_pad = dict(padx=4, pady=0)
self.start_btn = ttk.Button(
operation_frame,
text="开始测试",
parent,
text="\u25b6 开始测试",
command=self.start_test,
style="success.TButton",
bootstyle="success",
padding=(12, 6),
takefocus=False,
)
self.start_btn.pack(side=tk.LEFT, padx=5)
self.start_btn.pack(side=tk.LEFT, **btn_pad)
self.simulate_btn = ttk.Button(
operation_frame,
parent,
text="模拟测试",
command=self.run_simulation_test,
style="warning.TButton",
bootstyle="warning-outline",
padding=(12, 6),
takefocus=False,
)
self.simulate_btn.pack(side=tk.LEFT, padx=5)
self.simulate_btn.pack(side=tk.LEFT, **btn_pad)
self.stop_btn = ttk.Button(
operation_frame,
text="停止测试",
parent,
text="\u25a0 停止",
command=self.stop_test,
style="danger.TButton",
bootstyle="danger",
padding=(12, 6),
state=tk.DISABLED,
takefocus=False,
)
self.stop_btn.pack(side=tk.LEFT, padx=5)
self.stop_btn.pack(side=tk.LEFT, **btn_pad)
# 分隔
ttk.Separator(parent, orient="vertical").pack(side=tk.LEFT, fill="y", padx=8, pady=4)
self.save_btn = ttk.Button(
operation_frame,
parent,
text="保存结果",
command=self.save_results,
bootstyle="info-outline",
padding=(12, 6),
state=tk.DISABLED,
takefocus=False,
)
self.save_btn.pack(side=tk.LEFT, padx=5)
self.clear_config_btn = ttk.Button(
operation_frame,
text="清理配置",
command=self.clear_config_file,
)
self.clear_config_btn.pack(side=tk.LEFT, padx=5)
self.save_btn.pack(side=tk.LEFT, **btn_pad)
self.custom_btn = ttk.Button(
operation_frame,
parent,
text="客户模版",
command=self.start_custom_template_test,
style="info.TButton",
bootstyle="info",
padding=(12, 6),
takefocus=False,
)
self.custom_btn.pack(side=tk.LEFT, padx=5)
self.custom_btn.pack(side=tk.LEFT, **btn_pad)
self.clear_config_btn = ttk.Button(
parent,
text="清理配置",
command=self.clear_config_file,
bootstyle="secondary-outline",
padding=(10, 6),
takefocus=False,
)
self.clear_config_btn.pack(side=tk.LEFT, **btn_pad)
self.update_custom_button_visibility()
@@ -656,8 +845,10 @@ def on_screen_module_timing_changed(self: "PQAutomationApp", event=None):
if refresh_rate >= 120:
self.log_gui.log(" 检测到高刷新率", level="info")
# 更新配置
self.config.set_current_timing(selected_timing)
# 更新屏模组配置(独立于 current_test_type
self.config.current_test_types.setdefault("screen_module", {})[
"timing"
] = selected_timing
# 如果正在测试,提示用户
if self.testing:
@@ -670,6 +861,50 @@ def on_screen_module_timing_changed(self: "PQAutomationApp", event=None):
self.log_gui.log(f"屏模组信号格式更改失败: {str(e)}", level="error")
def on_screen_module_signal_format_changed(self: "PQAutomationApp", event=None):
"""屏模组 ColorInfo 相关选项变更回调。"""
try:
color_space = self.screen_module_color_space_var.get()
data_range = self.screen_module_data_range_var.get()
bit_depth = self.screen_module_bit_depth_var.get()
output_format = self.screen_module_output_format_var.get()
screen_cfg = self.config.current_test_types.setdefault("screen_module", {})
screen_cfg["colorimetry"] = color_space
screen_cfg["color_format"] = output_format
screen_cfg["bpc"] = UCDEnum.SignalFormat.BitDepth.get_bit_value(bit_depth)
screen_cfg["data_range"] = data_range
self.log_gui.log(
(
"屏模组信号格式已更新: "
f"色彩空间={color_space}, 数据范围={data_range}, "
f"位深={bit_depth}, 色彩格式={output_format}"
),
level="info",
)
if self.testing:
self.log_gui.log("警告: 测试进行中,格式更改将在下次测试时生效", level="error")
self.save_pq_config()
return
if getattr(self.ucd, "status", False):
ok = self.signal_service.update_signal_format(
color_space=color_space,
data_range=data_range,
bit_depth=bit_depth,
output_format=output_format,
)
if not ok:
self.log_gui.log("屏模组信号格式应用到UCD失败", level="error")
self.save_pq_config()
except Exception as e:
self.log_gui.log(f"屏模组信号格式更改失败: {str(e)}", level="error")
def on_sdr_timing_changed(self: "PQAutomationApp", event=None):
"""SDR测试分辨率改变时的回调"""
try:
@@ -784,6 +1019,9 @@ def update_test_items(self: "PQAutomationApp"):
if hasattr(self, "cct_params_frame"):
self.toggle_cct_params_frame()
# 同步刷新 header 折叠预览
refresh_config_preview(self)
def on_test_type_change(self: "PQAutomationApp"):
"""根据测试类型更新内容区域"""
@@ -801,6 +1039,7 @@ class MainLayoutMixin:
把本模块的自由函数挂到 PQAutomationApp 上,便于 F12 跳转与类型推断。
"""
create_floating_config_panel = create_floating_config_panel
refresh_config_preview = refresh_config_preview
create_test_items_content = create_test_items_content
create_signal_format_content = create_signal_format_content
create_connection_content = create_connection_content
@@ -808,6 +1047,7 @@ class MainLayoutMixin:
update_config_info_display = update_config_info_display
create_operation_frame = create_operation_frame
on_screen_module_timing_changed = on_screen_module_timing_changed
on_screen_module_signal_format_changed = on_screen_module_signal_format_changed
on_sdr_timing_changed = on_sdr_timing_changed
on_sdr_output_format_changed = on_sdr_output_format_changed
on_hdr_output_format_changed = on_hdr_output_format_changed