Files
pqAutomationApp/app/views/panels/main_layout.py
2026-04-21 16:03:11 +08:00

606 lines
20 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 重构)。"""
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(
"<<ComboboxSelected>>", 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("<<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))
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("<<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.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("<<ComboboxSelected>>", 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)
# AI 图片对话按钮
self.ai_image_btn = ttk.Button(
self.sidebar_frame,
text="AI 图片",
style="Sidebar.TButton",
command=self.toggle_ai_image_panel,
takefocus=False,
)
self.ai_image_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
if "ai_image" in self.panels:
self.panels["ai_image"]["button"] = self.ai_image_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}", 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")
# 更新配置
self.config.set_current_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 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()