"""主布局面板创建函数(Step 6 重构)。""" import re import tkinter as tk import ttkbootstrap as ttk from drivers.UCD323_Enum import UCDEnum from app.views.collapsing_frame import CollapsingFrame from app.resources import load_icon def create_floating_config_panel(self): """创建右上角悬浮配置框""" cf = CollapsingFrame(self.control_frame_top) cf.pack(fill="both") # 创建悬浮框主容器 self.config_panel_frame = ttk.Frame(cf) cf.add(self.config_panel_frame, title="配置项") # 创建一个统一的frame来替代选项卡控件 self.config_content_frame = ttk.Frame(self.config_panel_frame) self.config_content_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) # 创建一个横向排列的Frame config_row_frame = ttk.Frame(self.config_content_frame) config_row_frame.pack(fill=tk.X, expand=False, padx=5, pady=5) # 创建连接内容区域 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 ) # 创建测试项目区域 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 ) # 创建信号格式区域 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 ) # 创建连接内容 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") def create_test_items_content(self): """创建测试项目选项卡内容""" # 创建测试项目字典,用于管理不同测试类型的选项 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"), ], }, } # 根据当前测试类型创建复选框 self.test_vars = {} self.update_test_items() # 创建色度参数设置框架 self.create_cct_params_frame() def create_signal_format_content(self): """创建信号格式选项卡内容""" 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=1) self.signal_tabs.add(self.screen_module_signal_frame, text="屏模组测试") self.screen_module_timing_var = tk.StringVar( value=self.config.current_test_types[self.config.current_test_type][ "timing" ] ) 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( "<>", self.on_screen_module_timing_changed ) screen_module_timing_combo.grid(row=0, column=0, sticky="ew", padx=5, pady=5) # ==================== 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_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=["BT.709", "BT.601", "BT.2020"], width=10, state="readonly", ) sdr_color_space_combo.grid(row=0, column=1, sticky=tk.W, padx=5, pady=2) # Gamma ttk.Label(self.sdr_signal_frame, text="Gamma:").grid( row=1, column=0, sticky=tk.W, padx=5, pady=2 ) self.sdr_gamma_type_var = tk.StringVar(value="2.2") sdr_gamma_combo = ttk.Combobox( self.sdr_signal_frame, textvariable=self.sdr_gamma_type_var, values=["2.2", "2.4", "2.6"], width=10, state="readonly", ) sdr_gamma_combo.grid(row=1, column=1, sticky=tk.W, padx=5, pady=2) # 数据范围 ttk.Label(self.sdr_signal_frame, text="数据范围:").grid( row=2, column=0, sticky=tk.W, padx=5, pady=2 ) self.sdr_data_range_var = tk.StringVar(value="Full") sdr_range_combo = ttk.Combobox( self.sdr_signal_frame, textvariable=self.sdr_data_range_var, values=["Full", "Limited"], width=10, state="readonly", ) sdr_range_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_bit_depth_var = tk.StringVar(value="8bit") sdr_bit_depth_combo = ttk.Combobox( self.sdr_signal_frame, textvariable=self.sdr_bit_depth_var, values=["8bit", "10bit", "12bit"], width=10, state="readonly", ) sdr_bit_depth_combo.grid(row=3, 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="Full") hdr_range_combo = ttk.Combobox( self.hdr_signal_frame, textvariable=self.hdr_data_range_var, values=["Full", "Limited"], 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="8bit") hdr_bit_depth_combo = ttk.Combobox( self.hdr_signal_frame, textvariable=self.hdr_bit_depth_var, values=["8bit", "10bit", "12bit"], width=10, state="readonly", ) hdr_bit_depth_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 def create_connection_content(self): """创建设备连接区域""" # 创建设备连接区域的主框架 com_frame = ttk.Frame(self.connection_frame) com_frame.pack(fill=tk.X, pady=5) # 获取可用的COM端口列表 available_ports = self.get_available_com_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_ports, 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("<>", 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)) self.ucd_status_indicator.config(bg="gray") # 添加按钮框架 button_frame = ttk.Frame(com_frame) button_frame.grid(row=3, column=0, columnspan=3, pady=3, sticky="w") connect_icon = load_icon("assets/connect-svgrepo-com.png") self.check_button = ttk.Button( button_frame, image=connect_icon, bootstyle="link", takefocus=False, command=self.check_com_connections, ) self.check_button.image = connect_icon self.check_button.pack(side="left", padx=0, pady=3) disconnect_icon = load_icon("assets/disconnect-svgrepo-com.png") # 断开连接按钮 self.disconnect_button = ttk.Button( button_frame, image=disconnect_icon, bootstyle="link", takefocus=False, command=self.disconnect_com_connections, ) self.disconnect_button.image = disconnect_icon # 防止图标被垃圾回收 self.disconnect_button.pack(side="left", padx=0, pady=3) refresh_icon = load_icon("assets/refresh-svgrepo-com.png") self.refresh_button = ttk.Button( button_frame, image=refresh_icon, bootstyle="link", takefocus=False, command=self.refresh_com_ports, ) self.refresh_button.image = refresh_icon # 防止图标被垃圾回收 self.refresh_button.pack(side="left", padx=0, pady=3) # 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("<>", 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.ca_status_indicator.config(bg="gray") # 添加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("<>", self.update_config) def create_test_type_frame(self): """创建测试类型选择区域(侧边栏形式)""" # 设置测试类型变量 self.test_type_var = tk.StringVar(value="screen_module") # 创建测试类型按钮并放置在侧边栏 test_types = [ ("屏模组性能测试", "screen_module"), ("SDR Movie测试", "sdr_movie"), ("HDR Movie测试", "hdr_movie"), ] for text, type_value in test_types: btn = ttk.Button( master=self.sidebar_frame, text=text, style="Sidebar.TButton", padding=10, 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.Separator(self.sidebar_frame, orient="horizontal").pack( fill=tk.X, padx=10, pady=10 ) # ✅ 只保留日志按钮 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=1) # Local Dimming 测试按钮 self.local_dimming_btn = ttk.Button( self.sidebar_frame, text="Local Dimming", style="Sidebar.TButton", command=self.toggle_local_dimming_panel, takefocus=False, ) self.local_dimming_btn.pack(fill=tk.X, padx=0, pady=1) # 注册面板按钮(只保留日志) if hasattr(self, "panels"): if "log" in self.panels: self.panels["log"]["button"] = self.log_btn if "local_dimming" in self.panels: self.panels["local_dimming"]["button"] = self.local_dimming_btn def update_config_info_display(self): """更新配置信息显示""" 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): """创建操作按钮区域""" operation_frame = ttk.Frame(self.control_frame_top) operation_frame.pack(fill=tk.X, padx=5, pady=10) self.start_btn = ttk.Button( operation_frame, text="开始测试", command=self.start_test, style="success.TButton", ) self.start_btn.pack(side=tk.LEFT, padx=5) self.stop_btn = ttk.Button( operation_frame, text="停止测试", command=self.stop_test, style="danger.TButton", state=tk.DISABLED, ) self.stop_btn.pack(side=tk.LEFT, padx=5) self.save_btn = ttk.Button( operation_frame, text="保存结果", command=self.save_results, state=tk.DISABLED, ) 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.custom_btn = ttk.Button( operation_frame, text="客户模版", command=self.start_custom_template_test, style="info.TButton", ) self.custom_btn.pack(side=tk.LEFT, padx=5) self.update_custom_button_visibility() def on_screen_module_timing_changed(self, event=None): """屏模组信号格式改变时的回调""" try: selected_timing = self.screen_module_timing_var.get() # 记录日志 self.log_gui.log(f"屏模组信号格式已更改为: {selected_timing}") 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}") self.log_gui.log(f" └─ 刷新率: {refresh_rate}Hz") # 根据分辨率给出提示 if width >= 3840: # 4K及以上 self.log_gui.log(" ℹ️ 检测到4K分辨率") if refresh_rate >= 120: self.log_gui.log(" ℹ️ 检测到高刷新率") # 更新配置 self.config.set_current_timing(selected_timing) # 如果正在测试,提示用户 if self.testing: self.log_gui.log("⚠️ 警告: 测试进行中,信号格式更改将在下次测试时生效") # 保存配置 self.save_pq_config() except Exception as e: self.log_gui.log(f"❌ 屏模组信号格式更改失败: {str(e)}") def update_test_items(self): """根据当前测试类型更新测试项目复选框""" # 先隐藏所有测试项目框架 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) # 添加测试类型标签 type_label = ttk.Label( frame, text=self.get_test_type_name(current_test_type), style="primary.TLabel", ) type_label.grid(row=0, column=0, columnspan=2, sticky=tk.W, padx=5, pady=3) # 从配置中读取保存的选择状态 saved_test_items = self.config.current_test_types[current_test_type].get( "test_items", [] ) # 添加复选框 for i, (text, var_name) in enumerate(config["items"]): is_checked = var_name in saved_test_items var = tk.BooleanVar(value=is_checked) self.test_vars[f"{current_test_type}_{var_name}"] = var ttk.Checkbutton( frame, text=text, variable=var, bootstyle="round-toggle", command=self.update_config_and_tabs, ).grid(row=i // 2 + 1, column=i % 2, sticky=tk.W, padx=10, pady=5) if hasattr(self, "chart_notebook"): self.update_chart_tabs_state() if hasattr(self, "cct_params_frame"): self.toggle_cct_params_frame() def on_test_type_change(self): """根据测试类型更新内容区域""" # 更新配置信息显示 if hasattr(self, "config") and hasattr(self.config, "get_current_config"): self.update_config_info_display() # SDR 选中时显示客户模版按钮 self.update_custom_button_visibility()