"""主布局面板创建函数（Step 6 重构）。"""

import re
import tkinter as tk
import ttkbootstrap as ttk

from app.ucd import UCDEnum
from app.views.collapsing_frame import CollapsingFrame
from app.resources import load_icon

from typing import TYPE_CHECKING

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"):
    """创建顶部"配置项"现代化折叠面板
    """
    cf = CollapsingFrame(self.control_frame_top)
    cf.pack(fill="both")
    self._config_collapsing = cf

    # 配置项主体容器（卡片宿主）
    self.config_panel_frame = ttk.Frame(cf)

    # 折叠预览：呈现"测试类型 · 已选测试项"
    self.config_preview_var = tk.StringVar(value="")

    self.toolbar_actions_frame = 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=8, pady=8)

    config_row_frame = ttk.Frame(self.config_content_frame)
    config_row_frame.pack(fill=tk.X, expand=False)

    # 设备连接 卡片
    connection_card = _make_card(config_row_frame, icon="01", 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]

    # 测试项目 卡片
    test_items_card = _make_card(config_row_frame, icon="02", 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]

    # 信号格式 卡片
    signal_card = _make_card(config_row_frame, icon="03", 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]

    # 创建卡片内部内容
    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",
        "local_dimming": "Local Dimming",
    }
    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"):
    """创建测试项目选项卡内容"""
    # 创建测试项目字典,用于管理不同测试类型的选项
    self.test_items = {
        "screen_module": {
            "frame": ttk.Frame(self.test_items_frame),
            "items": [
                ("色域", "gamut"),
                ("Gamma", "gamma"),
                ("色度", "cct"),
                ("对比度", "contrast"),
            ],
        },
        "sdr_movie": {
            "frame": ttk.Frame(self.test_items_frame),
            "items": [
                ("色域", "gamut"),
                ("Gamma", "gamma"),
                ("色度", "cct"),
                ("对比度", "contrast"),
                ("色准", "accuracy"),
            ],
        },
        "hdr_movie": {
            "frame": ttk.Frame(self.test_items_frame),
            "items": [
                ("色域", "gamut"),
                ("EOTF", "eotf"),
                ("色度", "cct"),
                ("对比度", "contrast"),
                ("色准", "accuracy"),
            ],
        },
        "local_dimming": {
            "frame": ttk.Frame(self.test_items_frame),
            "items": [],
        },
    }

    # 预创建各测试类型的复选框，切换时只改显隐与勾选状态，避免重复构建控件。
    self.test_vars = {}
    toggle_bootstyle = "success-round-toggle"
    for type_key, config in self.test_items.items():
        frame = config["frame"]
        type_label = ttk.Label(
            frame,
            text=self.get_test_type_name(type_key),
            style="primary.TLabel",
        )
        type_label.grid(row=0, column=0, columnspan=2, sticky=tk.W, padx=5, pady=3)
        config["type_label"] = type_label

        checkbox_vars: dict[str, tk.BooleanVar] = {}
        for i, (text, var_name) in enumerate(config["items"]):
            var = tk.BooleanVar(value=False)
            ttk.Checkbutton(
                frame,
                text=text,
                variable=var,
                bootstyle=toggle_bootstyle,
                command=self.update_config_and_tabs,
            ).grid(row=i // 2 + 1, column=i % 2, sticky=tk.W, padx=10, pady=5)
            checkbox_vars[var_name] = var
        config["checkbox_vars"] = checkbox_vars

    self.update_test_items()

    # 创建色度参数设置框架
    self.create_cct_params_frame()


def create_signal_format_content(self: "PQAutomationApp"):
    """创建信号格式选项卡内容"""
    self.signal_tabs = ttk.Notebook(self.signal_format_frame)
    self.signal_tabs.pack(fill=tk.BOTH, expand=True)

    # ==================== 屏模组格式设置 ====================
    self.screen_module_signal_frame = ttk.Frame(self.signal_tabs)
    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=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,
        textvariable=self.screen_module_timing_var,
        values=UCDEnum.TimingInfo.get_formatted_resolution_list(),
        state="readonly",
    )
    screen_module_timing_combo.bind(
        "<<ComboboxSelected>>", self.on_screen_module_timing_changed
    )
    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)
    # 配置列权重
    self.sdr_signal_frame.grid_columnconfigure(0, weight=0)
    self.sdr_signal_frame.grid_columnconfigure(1, weight=1)
    self.signal_tabs.add(self.sdr_signal_frame, text="SDR测试")

    # 分辨率
    ttk.Label(self.sdr_signal_frame, text="分辨率:").grid(
        row=0, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.sdr_timing_var = tk.StringVar(
        value=self.config.current_test_types.get("sdr_movie", {}).get(
            "timing", "DMT 1920x1080@60Hz"
        )
    )
    sdr_timing_combo = ttk.Combobox(
        self.sdr_signal_frame,
        textvariable=self.sdr_timing_var,
        values=UCDEnum.TimingInfo.get_formatted_resolution_list(),
        width=20,
        state="readonly",
    )
    sdr_timing_combo.bind("<<ComboboxSelected>>", self.on_sdr_timing_changed)
    sdr_timing_combo.grid(row=0, column=1, sticky=tk.W, padx=5, pady=2)

    # 色彩空间
    ttk.Label(self.sdr_signal_frame, text="色彩空间:").grid(
        row=1, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.sdr_color_space_var = tk.StringVar(value="BT.709")
    sdr_color_space_combo = ttk.Combobox(
        self.sdr_signal_frame,
        textvariable=self.sdr_color_space_var,
        values=["sRGB", "BT.709", "BT.601", "BT.2020", "DCI-P3"],
        width=10,
        state="readonly",
    )
    sdr_color_space_combo.grid(row=1, column=1, sticky=tk.W, padx=5, pady=2)

    # Gamma（测试参考值，用于Gamma曲线绘制和色准计算）
    ttk.Label(self.sdr_signal_frame, text="Gamma:").grid(
        row=2, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.sdr_gamma_type_var = tk.StringVar(value=UCDEnum.SignalFormat.GammaType.GAMMA_22)
    sdr_gamma_combo = ttk.Combobox(
        self.sdr_signal_frame,
        textvariable=self.sdr_gamma_type_var,
        values=UCDEnum.SignalFormat.GammaType.get_list(),
        width=10,
        state="readonly",
    )
    sdr_gamma_combo.grid(row=2, column=1, sticky=tk.W, padx=5, pady=2)

    # 数据范围
    ttk.Label(self.sdr_signal_frame, text="数据范围:").grid(
        row=3, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.sdr_data_range_var = tk.StringVar(value=UCDEnum.SignalFormat.DataRange.FULL)
    sdr_range_combo = ttk.Combobox(
        self.sdr_signal_frame,
        textvariable=self.sdr_data_range_var,
        values=UCDEnum.SignalFormat.DataRange.get_list(),
        width=10,
        state="readonly",
    )
    sdr_range_combo.grid(row=3, column=1, sticky=tk.W, padx=5, pady=2)

    # 编码位深
    ttk.Label(self.sdr_signal_frame, text="编码位深:").grid(
        row=4, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.sdr_bit_depth_var = tk.StringVar(value=UCDEnum.SignalFormat.BitDepth.BIT_8)
    sdr_bit_depth_combo = ttk.Combobox(
        self.sdr_signal_frame,
        textvariable=self.sdr_bit_depth_var,
        values=UCDEnum.SignalFormat.BitDepth.get_list(),
        width=10,
        state="readonly",
    )
    sdr_bit_depth_combo.grid(row=4, column=1, sticky=tk.W, padx=5, pady=2)

    # 色彩格式
    ttk.Label(self.sdr_signal_frame, text="色彩格式:").grid(
        row=5, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.sdr_output_format_var = tk.StringVar(value=UCDEnum.SignalFormat.OutputFormat.RGB)
    sdr_output_format_combo = ttk.Combobox(
        self.sdr_signal_frame,
        textvariable=self.sdr_output_format_var,
        values=UCDEnum.SignalFormat.OutputFormat.get_list(),
        width=10,
        state="readonly",
    )
    sdr_output_format_combo.bind("<<ComboboxSelected>>", self.on_sdr_output_format_changed)
    sdr_output_format_combo.grid(row=5, column=1, sticky=tk.W, padx=5, pady=2)

    # ==================== HDR信号格式设置 ====================
    self.hdr_signal_frame = ttk.Frame(self.signal_tabs)
    # 配置列权重
    self.hdr_signal_frame.grid_columnconfigure(0, weight=0)
    self.hdr_signal_frame.grid_columnconfigure(1, weight=1)
    self.signal_tabs.add(self.hdr_signal_frame, text="HDR")

    # 色彩空间
    ttk.Label(self.hdr_signal_frame, text="色彩空间:").grid(
        row=0, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.hdr_color_space_var = tk.StringVar(value="BT.2020")
    hdr_color_space_combo = ttk.Combobox(
        self.hdr_signal_frame,
        textvariable=self.hdr_color_space_var,
        values=["BT.2020", "DCI-P3"],
        width=10,
        state="readonly",
    )
    hdr_color_space_combo.grid(row=0, column=1, sticky=tk.W, padx=5, pady=2)

    # Metadata设置
    ttk.Label(self.hdr_signal_frame, text="Metadata:").grid(
        row=1, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.hdr_metadata_frame = ttk.Frame(self.hdr_signal_frame)
    self.hdr_metadata_frame.grid(
        row=1, column=1, rowspan=2, sticky=tk.W, padx=5, pady=2
    )

    ttk.Label(self.hdr_metadata_frame, text="MaxCLL:").grid(
        row=0, column=0, sticky=tk.W
    )
    self.hdr_maxcll_var = tk.StringVar(value="1000")
    ttk.Entry(
        self.hdr_metadata_frame, textvariable=self.hdr_maxcll_var, width=6
    ).grid(row=0, column=1, padx=2)

    ttk.Label(self.hdr_metadata_frame, text="MaxFALL:").grid(
        row=1, column=0, sticky=tk.W
    )
    self.hdr_maxfall_var = tk.StringVar(value="400")
    ttk.Entry(
        self.hdr_metadata_frame, textvariable=self.hdr_maxfall_var, width=6
    ).grid(row=1, column=1, padx=2)

    # 数据范围
    ttk.Label(self.hdr_signal_frame, text="数据范围:").grid(
        row=3, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.hdr_data_range_var = tk.StringVar(value=UCDEnum.SignalFormat.DataRange.FULL)
    hdr_range_combo = ttk.Combobox(
        self.hdr_signal_frame,
        textvariable=self.hdr_data_range_var,
        values=UCDEnum.SignalFormat.DataRange.get_list(),
        width=10,
        state="readonly",
    )
    hdr_range_combo.grid(row=3, column=1, sticky=tk.W, padx=5, pady=2)

    # 编码位深
    ttk.Label(self.hdr_signal_frame, text="编码位深:").grid(
        row=4, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.hdr_bit_depth_var = tk.StringVar(value=UCDEnum.SignalFormat.BitDepth.BIT_8)
    hdr_bit_depth_combo = ttk.Combobox(
        self.hdr_signal_frame,
        textvariable=self.hdr_bit_depth_var,
        values=UCDEnum.SignalFormat.BitDepth.get_list(),
        width=10,
        state="readonly",
    )
    hdr_bit_depth_combo.grid(row=4, column=1, sticky=tk.W, padx=5, pady=2)

    # 色彩格式
    ttk.Label(self.hdr_signal_frame, text="色彩格式:").grid(
        row=5, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.hdr_output_format_var = tk.StringVar(value=UCDEnum.SignalFormat.OutputFormat.RGB)
    hdr_output_format_combo = ttk.Combobox(
        self.hdr_signal_frame,
        textvariable=self.hdr_output_format_var,
        values=UCDEnum.SignalFormat.OutputFormat.get_list(),
        width=10,
        state="readonly",
    )
    hdr_output_format_combo.bind("<<ComboboxSelected>>", self.on_hdr_output_format_changed)
    hdr_output_format_combo.grid(row=5, column=1, sticky=tk.W, padx=5, pady=2)

    # ==================== Local Dimming 信号格式设置 ====================
    self.local_dimming_signal_frame = ttk.Frame(self.signal_tabs)
    self.local_dimming_signal_frame.grid_columnconfigure(0, weight=0)
    self.local_dimming_signal_frame.grid_columnconfigure(1, weight=1)
    self.signal_tabs.add(self.local_dimming_signal_frame, text="Local Dimming")

    ld_cfg = self.config.current_test_types.get("local_dimming", {})

    ttk.Label(self.local_dimming_signal_frame, text="分辨率:").grid(
        row=0, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.local_dimming_timing_var = tk.StringVar(
        value=ld_cfg.get("timing", "DMT 1920x 1080 @ 60Hz")
    )
    ld_timing_combo = ttk.Combobox(
        self.local_dimming_signal_frame,
        textvariable=self.local_dimming_timing_var,
        values=UCDEnum.TimingInfo.get_formatted_resolution_list(),
        width=20,
        state="readonly",
    )
    ld_timing_combo.bind("<<ComboboxSelected>>", self.on_local_dimming_timing_changed)
    ld_timing_combo.grid(row=0, column=1, sticky=tk.W, padx=5, pady=2)

    ttk.Label(self.local_dimming_signal_frame, text="色彩空间:").grid(
        row=1, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.local_dimming_color_space_var = tk.StringVar(
        value=ld_cfg.get("colorimetry", "sRGB")
    )
    ld_color_space_combo = ttk.Combobox(
        self.local_dimming_signal_frame,
        textvariable=self.local_dimming_color_space_var,
        values=["sRGB", "BT.709", "BT.601", "BT.2020", "DCI-P3"],
        width=10,
        state="readonly",
    )
    ld_color_space_combo.bind("<<ComboboxSelected>>", self.on_local_dimming_signal_format_changed)
    ld_color_space_combo.grid(row=1, column=1, sticky=tk.W, padx=5, pady=2)

    ttk.Label(self.local_dimming_signal_frame, text="数据范围:").grid(
        row=2, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.local_dimming_data_range_var = tk.StringVar(
        value=ld_cfg.get("data_range", UCDEnum.SignalFormat.DataRange.FULL)
    )
    ld_data_range_combo = ttk.Combobox(
        self.local_dimming_signal_frame,
        textvariable=self.local_dimming_data_range_var,
        values=UCDEnum.SignalFormat.DataRange.get_list(),
        width=10,
        state="readonly",
    )
    ld_data_range_combo.bind("<<ComboboxSelected>>", self.on_local_dimming_signal_format_changed)
    ld_data_range_combo.grid(row=2, column=1, sticky=tk.W, padx=5, pady=2)

    default_ld_bpc = int(ld_cfg.get("bpc", 8))
    default_ld_bit_depth = (
        f"{default_ld_bpc}bit"
        if f"{default_ld_bpc}bit" in UCDEnum.SignalFormat.BitDepth.get_list()
        else UCDEnum.SignalFormat.BitDepth.BIT_8
    )
    ttk.Label(self.local_dimming_signal_frame, text="编码位深:").grid(
        row=3, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.local_dimming_bit_depth_var = tk.StringVar(value=default_ld_bit_depth)
    ld_bit_depth_combo = ttk.Combobox(
        self.local_dimming_signal_frame,
        textvariable=self.local_dimming_bit_depth_var,
        values=UCDEnum.SignalFormat.BitDepth.get_list(),
        width=10,
        state="readonly",
    )
    ld_bit_depth_combo.bind("<<ComboboxSelected>>", self.on_local_dimming_signal_format_changed)
    ld_bit_depth_combo.grid(row=3, column=1, sticky=tk.W, padx=5, pady=2)

    ttk.Label(self.local_dimming_signal_frame, text="色彩格式:").grid(
        row=4, column=0, sticky=tk.W, padx=5, pady=2
    )
    self.local_dimming_output_format_var = tk.StringVar(
        value=ld_cfg.get("color_format", UCDEnum.SignalFormat.OutputFormat.RGB)
    )
    ld_output_format_combo = ttk.Combobox(
        self.local_dimming_signal_frame,
        textvariable=self.local_dimming_output_format_var,
        values=UCDEnum.SignalFormat.OutputFormat.get_list(),
        width=10,
        state="readonly",
    )
    ld_output_format_combo.bind("<<ComboboxSelected>>", self.on_local_dimming_signal_format_changed)
    ld_output_format_combo.grid(row=4, column=1, sticky=tk.W, padx=5, pady=2)

    # ==================== 初始化：默认只启用屏模组 Tab ====================
    self.signal_tabs.select(0)  # 选中屏模组
    self.signal_tabs.tab(1, state="disabled")  # 禁用 SDR
    self.signal_tabs.tab(2, state="disabled")  # 禁用 HDR
    self.signal_tabs.tab(3, state="disabled")  # 禁用 Local Dimming


def create_connection_content(self: "PQAutomationApp"):
    """创建设备连接区域"""
    # 创建设备连接区域的主框架
    com_frame = ttk.Frame(self.connection_frame)
    com_frame.pack(fill=tk.X, pady=5)

    # 获取可用的COM端口列表和UCD设备列表
    available_ports = self.get_available_com_ports()
    available_ucd_list = self.get_available_ucd_ports()

    # 使用网格布局，更整齐
    ttk.Label(com_frame, text="UCD列表:").grid(
        row=0, column=0, sticky=ttk.W, padx=5, pady=3
    )
    self.ucd_list_var = tk.StringVar(value=self.config.device_config["ucd_list"])
    self.ucd_list_combo = ttk.Combobox(
        com_frame,
        textvariable=self.ucd_list_var,
        values=available_ucd_list,
        width=10,
        state="readonly",
    )
    self.ucd_list_combo.grid(row=0, column=1, sticky=ttk.W, padx=5, pady=3)
    self.ucd_list_combo.bind("<<ComboboxSelected>>", self.update_config)

    # 添加UCD连接状态指示器
    self.ucd_status_indicator = tk.Canvas(
        com_frame, width=15, height=15, bg="gray", highlightthickness=0
    )
    self.ucd_status_indicator.grid(row=0, column=2, padx=(10, 20))

    # 添加按钮框架
    button_frame = ttk.Frame(com_frame)
    button_frame.grid(row=3, column=0, columnspan=3, pady=3, sticky="ew")
    button_frame.grid_columnconfigure(0, weight=1)
    button_frame.grid_columnconfigure(1, weight=1)
    button_frame.grid_columnconfigure(2, weight=1)
    button_frame.grid_columnconfigure(3, weight=1)

    # connect_icon = load_icon("assets/connect-svgrepo-com.png")
    self.check_button = ttk.Button(
        button_frame,
        # image=connect_icon,
        # bootstyle="link",
        text="全部连接",
        bootstyle="success",
        takefocus=False,
        command=self.check_com_connections,
    )
    # self.check_button.image = connect_icon
    self.check_button.grid(row=0, column=0, padx=(0, 4), pady=3, sticky="ew")

    self.ucd_connect_button = ttk.Button(
        button_frame,
        text="连接UCD",
        bootstyle="success-outline",
        takefocus=False,
        command=self.check_ucd_connection,
    )
    self.ucd_connect_button.grid(row=0, column=1, padx=4, pady=3, sticky="ew")

    self.ca_connect_button = ttk.Button(
        button_frame,
        text="连接CA410",
        bootstyle="success-outline",
        takefocus=False,
        command=self.check_ca_connection,
    )
    self.ca_connect_button.grid(row=0, column=2, padx=4, pady=3, sticky="ew")

    self.ucd_disconnect_button = ttk.Button(
        button_frame,
        text="断开UCD",
        bootstyle="danger-outline",
        takefocus=False,
        command=self.disconnect_ucd_connection,
    )
    self.ucd_disconnect_button.grid(row=1, column=1, padx=4, pady=3, sticky="ew")

    self.ca_disconnect_button = ttk.Button(
        button_frame,
        text="断开CA410",
        bootstyle="danger-outline",
        takefocus=False,
        command=self.disconnect_ca_connection,
    )
    self.ca_disconnect_button.grid(row=1, column=2, padx=4, pady=3, sticky="ew")

    # disconnect_icon = load_icon("assets/disconnect-svgrepo-com.png")
    # 断开连接按钮
    self.disconnect_button = ttk.Button(
        button_frame,
        # image=disconnect_icon,
        # bootstyle="link",
        text="断开",
        bootstyle="danger",
        takefocus=False,
        command=self.disconnect_com_connections,
    )
    # self.disconnect_button.image = disconnect_icon  # 防止图标被垃圾回收
    self.disconnect_button.grid(row=1, column=0, padx=(0, 4), pady=3, sticky="ew")

    # refresh_icon = load_icon("assets/refresh-svgrepo-com.png")
    self.refresh_button = ttk.Button(
        button_frame,
        # image=refresh_icon,
        # bootstyle="link",
        text="刷新",
        bootstyle="info",
        takefocus=False,
        command=self.refresh_com_ports,
    )
    # self.refresh_button.image = refresh_icon  # 防止图标被垃圾回收
    self.refresh_button.grid(row=1, column=3, padx=(4, 0), pady=3, sticky="ew")

    # CA端口
    ttk.Label(com_frame, text="CA端口:").grid(
        row=1, column=0, sticky=ttk.W, padx=5, pady=3
    )
    self.ca_com_var = tk.StringVar(value=self.config.device_config["ca_com"])
    self.ca_com_combo = ttk.Combobox(
        com_frame,
        textvariable=self.ca_com_var,
        values=available_ports,
        width=10,
        state="readonly",
    )
    self.ca_com_combo.grid(row=1, column=1, sticky=ttk.W, padx=5, pady=3)
    self.ca_com_combo.bind("<<ComboboxSelected>>", self.update_config)

    # 添加CA连接状态指示器
    self.ca_status_indicator = tk.Canvas(
        com_frame, width=15, height=15, bg="gray", highlightthickness=0
    )
    self.ca_status_indicator.grid(row=1, column=2, padx=(10, 20))

    self.refresh_connection_indicators()

    # 添加CA通道设置
    ttk.Label(com_frame, text="CA通道:").grid(
        row=2, column=0, sticky=tk.W, padx=5, pady=3
    )
    self.ca_channel_var = tk.StringVar(
        value=self.config.device_config["ca_channel"]
    )
    ca_channel_combo = ttk.Combobox(
        com_frame,
        textvariable=self.ca_channel_var,
        values=[str(i) for i in range(11)],
        width=10,
        state="readonly",
    )
    ca_channel_combo.grid(row=2, column=1, sticky=ttk.W, padx=5, pady=3)
    ca_channel_combo.bind("<<ComboboxSelected>>", self.update_config)


def create_test_type_frame(self: "PQAutomationApp"):
    """创建测试类型选择区域（侧边栏形式）。
    """
    # 设置测试类型变量
    self.test_type_var = tk.StringVar(value="screen_module")

    # ---------- 顶部品牌区 ----------
    brand = ttk.Frame(self.sidebar_frame, style="SidebarBrand.TFrame")
    brand.pack(fill=tk.X, pady=(2, 10))
    ttk.Label(
        brand,
        text="PQ AUTOMATION",
        style="SidebarBrand.TLabel",
    ).pack(side=tk.LEFT, padx=16, pady=11)

    # ---------- 分组：测试类型 ----------
    ttk.Label(
        self.sidebar_frame,
        text="测试类型",
        style="SidebarSection.TLabel",
    ).pack(fill=tk.X, padx=16, pady=(2, 6), anchor="w")

    test_types = [
        ("屏模组性能测试", "screen_module"),
        ("SDR Movie", "sdr_movie"),
        ("HDR Movie", "hdr_movie"),
        ("Local Dimming", "local_dimming"),
    ]

    for text, type_value in test_types:
        btn = ttk.Button(
            master=self.sidebar_frame,
            text=text,
            style="Sidebar.TButton",
            command=lambda v=type_value: self.change_test_type(v),
            takefocus=False,
        )
        btn.pack(fill=tk.X, padx=0, pady=1)
        setattr(self, f"{type_value}_btn", btn)

    # ---------- 分组：工具面板 ----------
    ttk.Label(
        self.sidebar_frame,
        text="工具面板",
        style="SidebarSection.TLabel",
    ).pack(fill=tk.X, padx=16, pady=(16, 6), anchor="w")

    panel_buttons = [
        ("ai_image_btn", "ai_image", "AI 图片", self.toggle_ai_image_panel),
        ("pantone_baseline_btn", "pantone_baseline", "Pantone 摸底", self.toggle_pantone_baseline_panel),
        ("gamma_pattern_btn", "gamma_pattern", "Gamma Pattern编辑", self.toggle_gamma_pattern_panel),
        ("calman_btn", "calman", "CALMAN 灰阶", self.toggle_calman_panel),
    ]
    for attr, panel_name, text, cmd in panel_buttons:
        item = self.create_tool_panel_sidebar_item(self.sidebar_frame, text, cmd)
        item["pack"]()
        setattr(self, attr, item["row"])
        if hasattr(self, "panels") and panel_name in self.panels:
            self.panels[panel_name]["button_label"] = text
            self.panels[panel_name]["sidebar_item"] = item
            self.panels[panel_name]["button"] = item["row"]

    # 测试版水印标签（版本 x.x.0.0 时显示）
    from app_version import is_beta_version, APP_VERSION
    if is_beta_version():
        beta_lbl = ttk.Label(
            self.sidebar_frame,
            text=f"[测试版] v{APP_VERSION}",
            style="SidebarBadge.TLabel",
        )
        beta_lbl.pack(fill=tk.X, side=tk.BOTTOM, padx=4, pady=(6, 4))


    # ---------- 测试日志（底部固定） ----------
    self.log_btn = ttk.Button(
        self.sidebar_frame,
        text="测试日志",
        style="Sidebar.TButton",
        command=self.toggle_log_panel,
        takefocus=False,
    )
    self.log_btn.pack(fill=tk.X, padx=0, pady=(0, 2), side=tk.BOTTOM)

    # ---------- 主题切换（底部固定） ----------
    self.theme_toggle_btn = ttk.Button(
        self.sidebar_frame,
        text="切换深色模式",
        style="Sidebar.TButton",
        command=self._on_toggle_theme,
        takefocus=False,
    )
    self.theme_toggle_btn.pack(fill=tk.X, padx=0, pady=(0, 2), side=tk.BOTTOM)
    _refresh_theme_toggle_label(self)

    # 注册面板侧栏按钮
    if hasattr(self, "panels"):
        if "ai_image" in self.panels:
            self.panels["ai_image"]["button"] = self.ai_image_btn
        if "single_step" in self.panels:
            self.panels["single_step"]["button"] = getattr(self, "single_step_btn", None)
        if "pantone_baseline" in self.panels:
            self.panels["pantone_baseline"]["button"] = self.pantone_baseline_btn
        if "gamma_pattern" in self.panels:
            self.panels["gamma_pattern"]["button"] = self.gamma_pattern_btn
        if "calman" in self.panels:
            self.panels["calman"]["button"] = self.calman_btn


def _refresh_theme_toggle_label(self: "PQAutomationApp") -> None:
    """根据当前主题刷新切换按钮的文字。"""
    from app.views.theme_manager import is_dark
    if not hasattr(self, "theme_toggle_btn"):
        return
    if is_dark():
        self.theme_toggle_btn.configure(text="切换浅色模式")
    else:
        self.theme_toggle_btn.configure(text="切换深色模式")


def _on_toggle_theme(self: "PQAutomationApp") -> None:
    """切换主题：重新应用 ttk 样式并刷新所有自定义样式相关的标签。"""
    # 在测试进行时禁止切换主题，避免影响测量稳定性
    if getattr(self, "testing", False):
        try:
            if hasattr(self, "log_gui"):
                self.log_gui.log("警告: 测试进行中，禁止切换主题", level="error")
        except Exception:
            pass
        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


def update_config_info_display(self: "PQAutomationApp"):
    """更新配置信息显示"""
    if hasattr(self, "config") and hasattr(self.config, "get_current_config"):
        current_config = self.config.get_current_config()

        info_text = f"测试类型: {current_config.get('name', '未知')}\n"
        info_text += (
            f"测试项目: {', '.join(current_config.get('test_items', []))}\n"
        )
        info_text += f"信号格式: {current_config.get('signal_format', 'none')}\n"
        info_text += f"色彩空间: {current_config.get('color_space', 'unknown')}\n"
        info_text += f"位深度: {current_config.get('bit_depth', 'unknown')}"

    # 高亮当前选中的测试类型
    self.update_sidebar_selection()


def create_operation_frame(self: "PQAutomationApp"):
    """创建操作按钮区域。

    新布局：按钮挂到配置项 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(
        parent,
        text="\u25b6 开始测试",
        command=self.start_test,
        bootstyle="success",
        padding=(12, 6),
        takefocus=False,
    )
    self.start_btn.pack(side=tk.LEFT, **btn_pad)

    self.stop_btn = ttk.Button(
        parent,
        text="\u25a0 停止",
        command=self.stop_test,
        bootstyle="danger",
        padding=(12, 6),
        state=tk.DISABLED,
        takefocus=False,
    )
    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(
        parent,
        text="保存结果",
        command=self.save_results,
        bootstyle="info-outline",
        padding=(12, 6),
        state=tk.DISABLED,
        takefocus=False,
    )
    self.save_btn.pack(side=tk.LEFT, **btn_pad)

    self.custom_btn = ttk.Button(
        parent,
        text="客户模版",
        command=self.start_custom_template_test,
        bootstyle="info",
        padding=(12, 6),
        takefocus=False,
    )
    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()


def on_screen_module_timing_changed(self: "PQAutomationApp", event=None):
    """屏模组信号格式改变时的回调"""
    try:
        selected_timing = self.screen_module_timing_var.get()

        # 记录日志
        self.log_gui.log(f"屏模组信号格式已更改为: {selected_timing}", level="info")

        match = re.search(r"(\d+)x(\d+)\s*@\s*(\d+)", selected_timing)
        if match:
            width = int(match.group(1))
            height = int(match.group(2))
            refresh_rate = int(match.group(3))

            self.log_gui.log(f"  ├─ 分辨率: {width}x{height}", level="info")
            self.log_gui.log(f"  └─ 刷新率: {refresh_rate}Hz", level="info")

            # 根据分辨率给出提示
            if width >= 3840:  # 4K及以上
                self.log_gui.log("检测到4K分辨率", level="info")

            if refresh_rate >= 120:
                self.log_gui.log("检测到高刷新率", level="info")

        # 更新屏模组配置（独立于 current_test_type）
        self.config.current_test_types.setdefault("screen_module", {})[
            "timing"
        ] = selected_timing

        # 如果正在测试，提示用户
        if self.testing:
            self.log_gui.log("警告: 测试进行中，信号格式更改将在下次测试时生效", level="error")

        # 保存配置
        self.save_pq_config()

    except Exception as e:
        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 self.signal_service.is_connected:
            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:
        selected_timing = self.sdr_timing_var.get()
        self.log_gui.log(f"SDR测试分辨率已更改为: {selected_timing}", level="info")

        # 直接更新 sdr_movie 的 timing 配置
        self.config.current_test_types["sdr_movie"]["timing"] = selected_timing

        if self.testing:
            self.log_gui.log("警告: 测试进行中，分辨率更改将在下次测试时生效", level="error")

        self.save_pq_config()

    except Exception as e:
        self.log_gui.log(f"SDR测试分辨率更改失败: {str(e)}", level="error")


def on_sdr_output_format_changed(self: "PQAutomationApp", event=None):
    """SDR 色彩格式改变时的回调"""
    try:
        fmt = self.sdr_output_format_var.get()
        self.log_gui.log(f"SDR色彩格式已更改为: {fmt}", level="info")

        if self.testing:
            self.log_gui.log("警告: 测试进行中，格式更改将在下次测试时生效", level="error")
            return

        if self.signal_service.is_connected:
            ok = self.signal_service.update_signal_format(
                color_space=self.sdr_color_space_var.get(),
                data_range=self.sdr_data_range_var.get(),
                bit_depth=self.sdr_bit_depth_var.get(),
                output_format=fmt,
            )
            if not ok:
                self.log_gui.log("SDR色彩格式应用到UCD失败", level="error")

    except Exception as e:
        self.log_gui.log(f"SDR色彩格式更改失败: {str(e)}", level="error")


def on_hdr_output_format_changed(self: "PQAutomationApp", event=None):
    """HDR 色彩格式改变时的回调"""
    try:
        fmt = self.hdr_output_format_var.get()
        self.log_gui.log(f"HDR色彩格式已更改为: {fmt}", level="info")

        if self.testing:
            self.log_gui.log("警告: 测试进行中，格式更改将在下次测试时生效", level="error")
            return

        if self.signal_service.is_connected:
            ok = self.signal_service.update_signal_format(
                color_space=self.hdr_color_space_var.get(),
                data_range=self.hdr_data_range_var.get(),
                bit_depth=self.hdr_bit_depth_var.get(),
                max_cll=self.hdr_maxcll_var.get(),
                max_fall=self.hdr_maxfall_var.get(),
                output_format=fmt,
            )
            if not ok:
                self.log_gui.log("HDR色彩格式应用到UCD失败", level="error")

    except Exception as e:
        self.log_gui.log(f"HDR色彩格式更改失败: {str(e)}", level="error")


def on_local_dimming_timing_changed(self: "PQAutomationApp", event=None):
    """Local Dimming 分辨率改变时的回调。"""
    try:
        selected_timing = self.local_dimming_timing_var.get()
        self.log_gui.log(f"Local Dimming 分辨率已更改为: {selected_timing}", level="info")

        self.config.current_test_types.setdefault("local_dimming", {})["timing"] = selected_timing

        if hasattr(self, "invalidate_ld_ucd_params_cache"):
            self.invalidate_ld_ucd_params_cache()

        if self.testing:
            self.log_gui.log("警告: 测试进行中，分辨率更改将在下次测试时生效", level="error")

        self.save_pq_config()
    except Exception as e:
        self.log_gui.log(f"Local Dimming 分辨率更改失败: {str(e)}", level="error")


def on_local_dimming_signal_format_changed(self: "PQAutomationApp", event=None):
    """Local Dimming ColorInfo 相关选项变更回调。"""
    try:
        color_space = self.local_dimming_color_space_var.get()
        data_range = self.local_dimming_data_range_var.get()
        bit_depth = self.local_dimming_bit_depth_var.get()
        output_format = self.local_dimming_output_format_var.get()

        ld_cfg = self.config.current_test_types.setdefault("local_dimming", {})
        ld_cfg["colorimetry"] = color_space
        ld_cfg["color_format"] = output_format
        ld_cfg["bpc"] = UCDEnum.SignalFormat.BitDepth.get_bit_value(bit_depth)
        ld_cfg["data_range"] = data_range

        if hasattr(self, "invalidate_ld_ucd_params_cache"):
            self.invalidate_ld_ucd_params_cache()

        self.log_gui.log(
            (
                "Local Dimming 信号格式已更新: "
                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 self.signal_service.is_connected:
            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("Local Dimming 信号格式应用到UCD失败", level="error")

        self.save_pq_config()
    except Exception as e:
        self.log_gui.log(f"Local Dimming 信号格式更改失败: {str(e)}", level="error")


def update_test_items(self: "PQAutomationApp"):
    """根据当前测试类型更新测试项目复选框"""
    # 先隐藏所有测试项目框架
    for config in self.test_items.values():
        config["frame"].pack_forget()

    current_test_type = self.config.current_test_type
    self.test_vars = {}

    if current_test_type in self.test_items:
        config = self.test_items[current_test_type]
        frame = config["frame"]
        frame.pack(fill=tk.X, padx=5, pady=5)

        config["type_label"].configure(
            text=self.get_test_type_name(current_test_type),
        )

        saved_test_items = self.config.current_test_types[current_test_type].get(
            "test_items", []
        )

        for var_name, var in config["checkbox_vars"].items():
            var.set(var_name in saved_test_items)
            self.test_vars[f"{current_test_type}_{var_name}"] = var

    if hasattr(self, "chart_notebook"):
        self.update_chart_tabs_state()
    if hasattr(self, "cct_params_frame"):
        self.toggle_cct_params_frame()

    # 同步刷新 header 折叠预览
    refresh_config_preview(self)


def on_test_type_change(self: "PQAutomationApp"):
    """根据测试类型更新内容区域"""
    # 更新配置信息显示
    if hasattr(self, "config") and hasattr(self.config, "get_current_config"):
        self.update_config_info_display()

    # SDR 选中时显示客户模版按钮
    self.update_custom_button_visibility()

    # Local Dimming 作为并列测试类型时，自动显示其专用面板。
    if self.config.current_test_type == "local_dimming":
        self.show_panel("local_dimming")
    elif getattr(self, "current_panel", None) == "local_dimming":
        self.hide_all_panels()



class MainLayoutMixin:
    """由 tools/refactor_to_mixins.py 自动生成。
    把本模块的自由函数挂到 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
    create_test_type_frame = create_test_type_frame
    update_config_info_display = update_config_info_display
    create_operation_frame = create_operation_frame
    _on_toggle_theme = _on_toggle_theme
    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
    on_local_dimming_timing_changed = on_local_dimming_timing_changed
    on_local_dimming_signal_format_changed = on_local_dimming_signal_format_changed
    update_test_items = update_test_items
    on_test_type_change = on_test_type_change
