修改摸底测试功能、修改pattern控制逻辑
This commit is contained in:
@@ -344,6 +344,8 @@ class PQConfig:
|
|||||||
temp_config.current_pattern = copy.deepcopy(self.default_pattern_gray)
|
temp_config.current_pattern = copy.deepcopy(self.default_pattern_gray)
|
||||||
elif mode == "accuracy":
|
elif mode == "accuracy":
|
||||||
temp_config.current_pattern = copy.deepcopy(self.default_pattern_accuracy)
|
temp_config.current_pattern = copy.deepcopy(self.default_pattern_accuracy)
|
||||||
|
elif mode == "custom":
|
||||||
|
temp_config.current_pattern = copy.deepcopy(self.default_pattern_temp)
|
||||||
|
|
||||||
# 3. 替换为转换后的参数
|
# 3. 替换为转换后的参数
|
||||||
temp_config.current_pattern["pattern_params"] = converted_params
|
temp_config.current_pattern["pattern_params"] = converted_params
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import colour
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
import algorithm.pq_algorithm as pq_algorithm
|
import algorithm.pq_algorithm as pq_algorithm
|
||||||
from app.data_range_converter import convert_pattern_params
|
|
||||||
from app.pq.pq_result import PQResult
|
from app.pq.pq_result import PQResult
|
||||||
|
|
||||||
def new_pq_results(self, test_type, test_name):
|
def new_pq_results(self, test_type, test_name):
|
||||||
@@ -310,205 +309,12 @@ def send_fix_pattern(self, mode):
|
|||||||
results = []
|
results = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 1. 设置图案模式
|
session = self.pattern_service.prepare_session(mode, log_details=True)
|
||||||
if mode == "rgb":
|
|
||||||
self.config.set_current_pattern("rgb")
|
|
||||||
elif mode == "gray":
|
|
||||||
self.config.set_current_pattern("gray")
|
|
||||||
elif mode == "accuracy": # 色准模式(SDR 和 HDR 通用 29色)
|
|
||||||
self.config.set_current_pattern("accuracy")
|
|
||||||
elif mode == "custom":
|
|
||||||
self.config.set_current_pattern("custom")
|
|
||||||
else:
|
|
||||||
self.log_gui.log(f"未知的图案模式: {mode}", level="error")
|
|
||||||
return None
|
|
||||||
|
|
||||||
# 2. 获取当前测试类型
|
|
||||||
test_type = self.config.current_test_type
|
|
||||||
|
|
||||||
# 3. 根据测试类型设置信号格式和图案
|
|
||||||
if test_type == "screen_module":
|
|
||||||
# 屏模组测试:使用 Timing
|
|
||||||
self.log_gui.log("=" * 50, level="separator")
|
|
||||||
self.log_gui.log("设置屏模组信号格式:", level="info")
|
|
||||||
self.log_gui.log("=" * 50, level="separator")
|
|
||||||
|
|
||||||
timing_str = self.config.current_test_types[test_type]["timing"]
|
|
||||||
self.log_gui.log(f" Timing: {timing_str}", level="info")
|
|
||||||
|
|
||||||
# 屏模组测试:直接使用原始配置
|
|
||||||
self.ucd.set_ucd_params(self.config)
|
|
||||||
|
|
||||||
elif test_type == "sdr_movie":
|
|
||||||
# SDR 测试:设置色彩空间、Gamma 等
|
|
||||||
self.log_gui.log("=" * 50, level="separator")
|
|
||||||
self.log_gui.log("设置 SDR 信号格式:", level="info")
|
|
||||||
self.log_gui.log("=" * 50, level="separator")
|
|
||||||
|
|
||||||
color_space = self.sdr_color_space_var.get()
|
|
||||||
gamma = self.sdr_gamma_type_var.get()
|
|
||||||
data_range = self.sdr_data_range_var.get()
|
|
||||||
bit_depth = self.sdr_bit_depth_var.get()
|
|
||||||
|
|
||||||
self.log_gui.log(f" 色彩空间: {color_space}", level="info")
|
|
||||||
self.log_gui.log(f" Gamma: {gamma}", level="info")
|
|
||||||
self.log_gui.log(f" 数据范围: {data_range}", level="info")
|
|
||||||
self.log_gui.log(f" 编码位深: {bit_depth}", level="info")
|
|
||||||
|
|
||||||
success = self.ucd.set_sdr_format(
|
|
||||||
color_space=color_space,
|
|
||||||
gamma=gamma,
|
|
||||||
data_range=data_range,
|
|
||||||
bit_depth=bit_depth,
|
|
||||||
)
|
|
||||||
|
|
||||||
if success:
|
|
||||||
self.log_gui.log("SDR 信号格式设置成功", level="success")
|
|
||||||
else:
|
|
||||||
self.log_gui.log("SDR 信号格式设置失败", level="error")
|
|
||||||
|
|
||||||
# 设置图案参数
|
|
||||||
if mode == "accuracy":
|
|
||||||
self.log_gui.log(f"设置 SDR 29色色准测试图案...", level="info")
|
|
||||||
else:
|
|
||||||
self.log_gui.log(f"设置 SDR 测试图案({mode} 模式)...", level="info")
|
|
||||||
|
|
||||||
# ========== ✅✅修改:使用临时配置对象 ==========
|
|
||||||
import copy
|
|
||||||
|
|
||||||
# 从原始配置获取参数(每次都是干净的)
|
|
||||||
if mode == "rgb":
|
|
||||||
original_params = copy.deepcopy(
|
|
||||||
self.config.default_pattern_rgb["pattern_params"]
|
|
||||||
)
|
|
||||||
elif mode == "gray":
|
|
||||||
original_params = copy.deepcopy(
|
|
||||||
self.config.default_pattern_gray["pattern_params"]
|
|
||||||
)
|
|
||||||
elif mode == "accuracy":
|
|
||||||
original_params = copy.deepcopy(
|
|
||||||
self.config.default_pattern_accuracy["pattern_params"]
|
|
||||||
)
|
|
||||||
elif mode == "custom":
|
|
||||||
original_params = copy.deepcopy(
|
|
||||||
self.config.default_pattern_temp["pattern_params"]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.log_gui.log(f" 使用原始 RGB 参数(前 3 个):", level="info")
|
|
||||||
for i in range(min(3, len(original_params))):
|
|
||||||
self.log_gui.log(f" [{i+1}] {original_params[i]}", level="info")
|
|
||||||
|
|
||||||
# 根据 data_range 转换
|
|
||||||
converted_params = convert_pattern_params(
|
|
||||||
pattern_params=original_params, data_range=data_range, verbose=False
|
|
||||||
)
|
|
||||||
|
|
||||||
if data_range == "Limited":
|
|
||||||
self.log_gui.log(" 转换为 Limited Range (16-235):", level="info")
|
|
||||||
for i in range(min(3, len(converted_params))):
|
|
||||||
self.log_gui.log(
|
|
||||||
f" {original_params[i]} → {converted_params[i]}"
|
|
||||||
, level="info")
|
|
||||||
else:
|
|
||||||
self.log_gui.log("Full Range,RGB 保持不变", level="success")
|
|
||||||
|
|
||||||
# 创建临时配置对象(不修改 self.config)
|
|
||||||
temp_config = self.config.get_temp_config_with_converted_params(
|
|
||||||
mode=mode, converted_params=converted_params
|
|
||||||
)
|
|
||||||
|
|
||||||
# 使用临时配置设置参数
|
|
||||||
self.ucd.set_ucd_params(temp_config)
|
|
||||||
|
|
||||||
self.log_gui.log(f"图案参数已设置,共 {len(converted_params)} 个图案", level="success")
|
|
||||||
# ========== 修改结束 ==========
|
|
||||||
|
|
||||||
elif test_type == "hdr_movie":
|
|
||||||
# HDR 测试:设置色彩空间、Metadata 等
|
|
||||||
self.log_gui.log("=" * 50, level="separator")
|
|
||||||
self.log_gui.log("设置 HDR 信号格式:", level="info")
|
|
||||||
self.log_gui.log("=" * 50, level="separator")
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
self.log_gui.log(f" 色彩空间: {color_space}", level="info")
|
|
||||||
self.log_gui.log(f" 数据范围: {data_range}", level="info")
|
|
||||||
self.log_gui.log(f" 编码位深: {bit_depth}", level="info")
|
|
||||||
self.log_gui.log(f" MaxCLL: {max_cll}", level="info")
|
|
||||||
self.log_gui.log(f" MaxFALL: {max_fall}", level="info")
|
|
||||||
|
|
||||||
success = self.ucd.set_hdr_format(
|
|
||||||
color_space=color_space,
|
|
||||||
data_range=data_range,
|
|
||||||
bit_depth=bit_depth,
|
|
||||||
max_cll=max_cll,
|
|
||||||
max_fall=max_fall,
|
|
||||||
)
|
|
||||||
|
|
||||||
if success:
|
|
||||||
self.log_gui.log("HDR 信号格式设置成功", level="success")
|
|
||||||
else:
|
|
||||||
self.log_gui.log("HDR 信号格式设置失败", level="error")
|
|
||||||
|
|
||||||
# 设置图案参数
|
|
||||||
if mode == "accuracy":
|
|
||||||
self.log_gui.log(f"设置 HDR 29色色准测试图案...", level="info")
|
|
||||||
else:
|
|
||||||
self.log_gui.log(f"设置 HDR 测试图案({mode} 模式)...", level="info")
|
|
||||||
|
|
||||||
# ========== ✅✅修改:使用临时配置对象 ==========
|
|
||||||
import copy
|
|
||||||
|
|
||||||
# 从原始配置获取参数
|
|
||||||
if mode == "rgb":
|
|
||||||
original_params = copy.deepcopy(
|
|
||||||
self.config.default_pattern_rgb["pattern_params"]
|
|
||||||
)
|
|
||||||
elif mode == "gray":
|
|
||||||
original_params = copy.deepcopy(
|
|
||||||
self.config.default_pattern_gray["pattern_params"]
|
|
||||||
)
|
|
||||||
elif mode == "accuracy":
|
|
||||||
original_params = copy.deepcopy(
|
|
||||||
self.config.default_pattern_accuracy["pattern_params"]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.log_gui.log(f" 使用原始 RGB 参数(前 3 个):", level="info")
|
|
||||||
for i in range(min(3, len(original_params))):
|
|
||||||
self.log_gui.log(f" [{i+1}] {original_params[i]}", level="info")
|
|
||||||
|
|
||||||
# 根据 data_range 转换
|
|
||||||
converted_params = convert_pattern_params(
|
|
||||||
pattern_params=original_params, data_range=data_range, verbose=False
|
|
||||||
)
|
|
||||||
|
|
||||||
if data_range == "Limited":
|
|
||||||
self.log_gui.log(" 转换为 Limited Range (16-235):", level="info")
|
|
||||||
for i in range(min(3, len(converted_params))):
|
|
||||||
self.log_gui.log(
|
|
||||||
f" {original_params[i]} → {converted_params[i]}"
|
|
||||||
, level="info")
|
|
||||||
else:
|
|
||||||
self.log_gui.log("Full Range,RGB 保持不变", level="success")
|
|
||||||
|
|
||||||
# 创建临时配置对象
|
|
||||||
temp_config = self.config.get_temp_config_with_converted_params(
|
|
||||||
mode=mode, converted_params=converted_params
|
|
||||||
)
|
|
||||||
|
|
||||||
self.ucd.set_ucd_params(temp_config)
|
|
||||||
|
|
||||||
self.log_gui.log(f"图案参数已设置,共 {len(converted_params)} 个图案", level="success")
|
|
||||||
# ========== 修改结束 ==========
|
|
||||||
|
|
||||||
self.log_gui.log("=" * 50, level="separator")
|
self.log_gui.log("=" * 50, level="separator")
|
||||||
|
|
||||||
# 4. 循环发送图案并采集数据(使用原始配置的数量)
|
# 4. 循环发送图案并采集数据
|
||||||
total_patterns = len(self.config.current_pattern["pattern_params"])
|
total_patterns = session.total_patterns
|
||||||
self.log_gui.log(f"开始采集数据,共 {total_patterns} 个图案", level="info")
|
self.log_gui.log(f"开始采集数据,共 {total_patterns} 个图案", level="info")
|
||||||
settle_time = max(0.2, float(getattr(self, "pattern_settle_time", 1.0)))
|
settle_time = max(0.2, float(getattr(self, "pattern_settle_time", 1.0)))
|
||||||
progress_step = max(
|
progress_step = max(
|
||||||
@@ -518,14 +324,7 @@ def send_fix_pattern(self, mode):
|
|||||||
f"采集等待时间: {settle_time:.2f}s(可通过 pattern_settle_time 调整)"
|
f"采集等待时间: {settle_time:.2f}s(可通过 pattern_settle_time 调整)"
|
||||||
, level="info")
|
, level="info")
|
||||||
|
|
||||||
# 获取颜色名称列表(用于日志显示)
|
display_names = session.display_names
|
||||||
color_names = None
|
|
||||||
if mode == "accuracy":
|
|
||||||
color_names = self.config.get_accuracy_color_names()
|
|
||||||
|
|
||||||
custom_pattern_names = []
|
|
||||||
if mode == "custom" and hasattr(self.config, "get_temp_pattern_names"):
|
|
||||||
custom_pattern_names = self.config.get_temp_pattern_names()
|
|
||||||
|
|
||||||
for i in range(total_patterns):
|
for i in range(total_patterns):
|
||||||
if not self.testing:
|
if not self.testing:
|
||||||
@@ -538,17 +337,15 @@ def send_fix_pattern(self, mode):
|
|||||||
or ((i + 1) % progress_step == 0)
|
or ((i + 1) % progress_step == 0)
|
||||||
)
|
)
|
||||||
|
|
||||||
# 设置下一个图案(显示颜色名称)
|
|
||||||
if should_log_detail:
|
if should_log_detail:
|
||||||
if color_names and i < len(color_names):
|
if display_names and i < len(display_names):
|
||||||
self.log_gui.log(
|
self.log_gui.log(
|
||||||
f"发送第 {i+1}/{total_patterns} 个图案: {color_names[i]}..."
|
f"发送第 {i+1}/{total_patterns} 个图案: {display_names[i]}..."
|
||||||
, level="info")
|
, level="info")
|
||||||
else:
|
else:
|
||||||
self.log_gui.log(f"发送第 {i+1}/{total_patterns} 个图案...", level="info")
|
self.log_gui.log(f"发送第 {i+1}/{total_patterns} 个图案...", level="info")
|
||||||
|
|
||||||
self.ucd.set_next_pattern()
|
self.pattern_service.send_session_pattern(session, i)
|
||||||
self.ucd.run()
|
|
||||||
time.sleep(settle_time)
|
time.sleep(settle_time)
|
||||||
|
|
||||||
# 测量数据
|
# 测量数据
|
||||||
@@ -582,8 +379,8 @@ def send_fix_pattern(self, mode):
|
|||||||
)
|
)
|
||||||
row_data = {
|
row_data = {
|
||||||
"pattern_name": (
|
"pattern_name": (
|
||||||
custom_pattern_names[i]
|
display_names[i]
|
||||||
if i < len(custom_pattern_names)
|
if i < len(display_names)
|
||||||
else f"P {i + 1}"
|
else f"P {i + 1}"
|
||||||
),
|
),
|
||||||
"X": X,
|
"X": X,
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
from app.services.pattern_service import PatternService, PatternSession
|
||||||
|
|||||||
199
app/services/pattern_service.py
Normal file
199
app/services/pattern_service.py
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import copy
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from app.data_range_converter import convert_pattern_params
|
||||||
|
from drivers.ucd_helpers import send_solid_rgb_pattern
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class PatternSession:
|
||||||
|
mode: str
|
||||||
|
test_type: str
|
||||||
|
active_config: object
|
||||||
|
pattern_params: list[list[int]]
|
||||||
|
total_patterns: int
|
||||||
|
display_names: list[str]
|
||||||
|
|
||||||
|
|
||||||
|
class PatternService:
|
||||||
|
def __init__(self, app):
|
||||||
|
self.app = app
|
||||||
|
|
||||||
|
def prepare_session(self, mode, *, test_type=None, log_details=False):
|
||||||
|
test_type = test_type or self.app.config.current_test_type
|
||||||
|
if not self.app.config.set_current_pattern(mode):
|
||||||
|
raise ValueError(f"未知的图案模式: {mode}")
|
||||||
|
|
||||||
|
active_config = self.app.config
|
||||||
|
source_params = self._get_source_pattern_params(mode)
|
||||||
|
|
||||||
|
if test_type == "screen_module":
|
||||||
|
if log_details:
|
||||||
|
self._log("=" * 50, "separator")
|
||||||
|
self._log("设置屏模组信号格式:", "info")
|
||||||
|
self._log("=" * 50, "separator")
|
||||||
|
self._log(
|
||||||
|
f" Timing: {self.app.config.current_test_types[test_type]['timing']}",
|
||||||
|
"info",
|
||||||
|
)
|
||||||
|
self.app.ucd.set_ucd_params(active_config)
|
||||||
|
elif test_type == "sdr_movie":
|
||||||
|
active_config = self._prepare_video_session(
|
||||||
|
mode=mode,
|
||||||
|
test_type=test_type,
|
||||||
|
source_params=source_params,
|
||||||
|
data_range=self.app.sdr_data_range_var.get(),
|
||||||
|
log_title="设置 SDR 信号格式:",
|
||||||
|
setup_message=f"设置 SDR 测试图案({mode} 模式)..."
|
||||||
|
if mode != "accuracy"
|
||||||
|
else "设置 SDR 29色色准测试图案...",
|
||||||
|
setup_format=lambda: self.app.ucd.set_sdr_format(
|
||||||
|
color_space=self.app.sdr_color_space_var.get(),
|
||||||
|
gamma=self.app.sdr_gamma_type_var.get(),
|
||||||
|
data_range=self.app.sdr_data_range_var.get(),
|
||||||
|
bit_depth=self.app.sdr_bit_depth_var.get(),
|
||||||
|
),
|
||||||
|
log_items=[
|
||||||
|
("色彩空间", self.app.sdr_color_space_var.get()),
|
||||||
|
("Gamma", self.app.sdr_gamma_type_var.get()),
|
||||||
|
("数据范围", self.app.sdr_data_range_var.get()),
|
||||||
|
("编码位深", self.app.sdr_bit_depth_var.get()),
|
||||||
|
],
|
||||||
|
log_details=log_details,
|
||||||
|
)
|
||||||
|
elif test_type == "hdr_movie":
|
||||||
|
active_config = self._prepare_video_session(
|
||||||
|
mode=mode,
|
||||||
|
test_type=test_type,
|
||||||
|
source_params=source_params,
|
||||||
|
data_range=self.app.hdr_data_range_var.get(),
|
||||||
|
log_title="设置 HDR 信号格式:",
|
||||||
|
setup_message=f"设置 HDR 测试图案({mode} 模式)..."
|
||||||
|
if mode != "accuracy"
|
||||||
|
else "设置 HDR 29色色准测试图案...",
|
||||||
|
setup_format=lambda: self.app.ucd.set_hdr_format(
|
||||||
|
color_space=self.app.hdr_color_space_var.get(),
|
||||||
|
data_range=self.app.hdr_data_range_var.get(),
|
||||||
|
bit_depth=self.app.hdr_bit_depth_var.get(),
|
||||||
|
max_cll=self.app.hdr_maxcll_var.get(),
|
||||||
|
max_fall=self.app.hdr_maxfall_var.get(),
|
||||||
|
),
|
||||||
|
log_items=[
|
||||||
|
("色彩空间", self.app.hdr_color_space_var.get()),
|
||||||
|
("数据范围", self.app.hdr_data_range_var.get()),
|
||||||
|
("编码位深", self.app.hdr_bit_depth_var.get()),
|
||||||
|
("MaxCLL", self.app.hdr_maxcll_var.get()),
|
||||||
|
("MaxFALL", self.app.hdr_maxfall_var.get()),
|
||||||
|
],
|
||||||
|
log_details=log_details,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"不支持的测试类型: {test_type}")
|
||||||
|
|
||||||
|
pattern_params = copy.deepcopy(active_config.current_pattern["pattern_params"])
|
||||||
|
return PatternSession(
|
||||||
|
mode=mode,
|
||||||
|
test_type=test_type,
|
||||||
|
active_config=active_config,
|
||||||
|
pattern_params=pattern_params,
|
||||||
|
total_patterns=len(pattern_params),
|
||||||
|
display_names=self._get_display_names(mode, len(pattern_params)),
|
||||||
|
)
|
||||||
|
|
||||||
|
def send_session_pattern(self, session, index):
|
||||||
|
if index < 0 or index >= session.total_patterns:
|
||||||
|
raise IndexError(f"pattern 索引越界: {index}")
|
||||||
|
|
||||||
|
pattern_param = session.pattern_params[index]
|
||||||
|
if not self.app.ucd.send_current_pattern_params(pattern_param):
|
||||||
|
raise RuntimeError(f"发送 pattern 失败: {index}")
|
||||||
|
return pattern_param
|
||||||
|
|
||||||
|
def send_rgb(self, rgb, *, session=None, test_type=None):
|
||||||
|
active_session = session or self.prepare_session(
|
||||||
|
"rgb",
|
||||||
|
test_type=test_type,
|
||||||
|
log_details=False,
|
||||||
|
)
|
||||||
|
converted_rgb = self._convert_rgb_for_test_type(rgb, active_session.test_type)
|
||||||
|
send_solid_rgb_pattern(self.app.ucd, converted_rgb, raise_on_error=True)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _prepare_video_session(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
mode,
|
||||||
|
test_type,
|
||||||
|
source_params,
|
||||||
|
data_range,
|
||||||
|
log_title,
|
||||||
|
setup_message,
|
||||||
|
setup_format,
|
||||||
|
log_items,
|
||||||
|
log_details,
|
||||||
|
):
|
||||||
|
if log_details:
|
||||||
|
self._log("=" * 50, "separator")
|
||||||
|
self._log(log_title, "info")
|
||||||
|
self._log("=" * 50, "separator")
|
||||||
|
for label, value in log_items:
|
||||||
|
self._log(f" {label}: {value}", "info")
|
||||||
|
|
||||||
|
success = setup_format()
|
||||||
|
if log_details:
|
||||||
|
self._log(
|
||||||
|
f"{test_type.split('_')[0].upper()} 信号格式设置{'成功' if success else '失败'}",
|
||||||
|
"success" if success else "error",
|
||||||
|
)
|
||||||
|
self._log(setup_message, "info")
|
||||||
|
|
||||||
|
converted_params = convert_pattern_params(
|
||||||
|
pattern_params=source_params,
|
||||||
|
data_range=data_range,
|
||||||
|
verbose=False,
|
||||||
|
)
|
||||||
|
active_config = self.app.config.get_temp_config_with_converted_params(
|
||||||
|
mode=mode,
|
||||||
|
converted_params=converted_params,
|
||||||
|
)
|
||||||
|
self.app.ucd.set_ucd_params(active_config)
|
||||||
|
|
||||||
|
if log_details:
|
||||||
|
self._log(f"图案参数已设置,共 {len(converted_params)} 个图案", "success")
|
||||||
|
|
||||||
|
return active_config
|
||||||
|
|
||||||
|
def _get_source_pattern_params(self, mode):
|
||||||
|
config = self.app.config
|
||||||
|
if mode == "rgb":
|
||||||
|
return copy.deepcopy(config.default_pattern_rgb["pattern_params"])
|
||||||
|
if mode == "gray":
|
||||||
|
return copy.deepcopy(config.default_pattern_gray["pattern_params"])
|
||||||
|
if mode == "accuracy":
|
||||||
|
return copy.deepcopy(config.default_pattern_accuracy["pattern_params"])
|
||||||
|
if mode == "custom":
|
||||||
|
return copy.deepcopy(config.default_pattern_temp["pattern_params"])
|
||||||
|
raise ValueError(f"未知的图案模式: {mode}")
|
||||||
|
|
||||||
|
def _get_display_names(self, mode, total_patterns):
|
||||||
|
if mode == "accuracy":
|
||||||
|
return self.app.config.get_accuracy_color_names()
|
||||||
|
if mode == "custom" and hasattr(self.app.config, "get_temp_pattern_names"):
|
||||||
|
return self.app.config.get_temp_pattern_names()
|
||||||
|
return [f"P {index + 1}" for index in range(total_patterns)]
|
||||||
|
|
||||||
|
def _convert_rgb_for_test_type(self, rgb, test_type):
|
||||||
|
if test_type == "sdr_movie":
|
||||||
|
data_range = self.app.sdr_data_range_var.get()
|
||||||
|
elif test_type == "hdr_movie":
|
||||||
|
data_range = self.app.hdr_data_range_var.get()
|
||||||
|
else:
|
||||||
|
data_range = "Full"
|
||||||
|
|
||||||
|
return convert_pattern_params([list(rgb)], data_range=data_range, verbose=False)[0]
|
||||||
|
|
||||||
|
def _log(self, message, level):
|
||||||
|
if hasattr(self.app, "log_gui"):
|
||||||
|
self.app.log_gui.log(message, level=level)
|
||||||
@@ -435,14 +435,14 @@ def create_test_type_frame(self):
|
|||||||
)
|
)
|
||||||
self.ai_image_btn.pack(fill=tk.X, padx=0, pady=1)
|
self.ai_image_btn.pack(fill=tk.X, padx=0, pady=1)
|
||||||
|
|
||||||
self.single_step_btn = ttk.Button(
|
# self.single_step_btn = ttk.Button(
|
||||||
self.sidebar_frame,
|
# self.sidebar_frame,
|
||||||
text="单步调试",
|
# text="单步调试",
|
||||||
style="Sidebar.TButton",
|
# style="Sidebar.TButton",
|
||||||
command=self.toggle_single_step_panel,
|
# command=self.toggle_single_step_panel,
|
||||||
takefocus=False,
|
# takefocus=False,
|
||||||
)
|
# )
|
||||||
self.single_step_btn.pack(fill=tk.X, padx=0, pady=1)
|
# self.single_step_btn.pack(fill=tk.X, padx=0, pady=1)
|
||||||
|
|
||||||
self.pantone_baseline_btn = ttk.Button(
|
self.pantone_baseline_btn = ttk.Button(
|
||||||
self.sidebar_frame,
|
self.sidebar_frame,
|
||||||
|
|||||||
@@ -2,23 +2,16 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import csv
|
|
||||||
import datetime
|
import datetime
|
||||||
import os
|
import os
|
||||||
import tempfile
|
|
||||||
import threading
|
import threading
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import filedialog, messagebox
|
from tkinter import filedialog, messagebox
|
||||||
|
|
||||||
import ttkbootstrap as ttk
|
import ttkbootstrap as ttk
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
from drivers.ucd_helpers import get_current_resolution, send_image_pattern
|
|
||||||
|
|
||||||
|
|
||||||
_PATTERN_FILE = "pantone_patterns_2670.csv"
|
|
||||||
_TEMPLATE_FILE = "pantone\xa02670\xa0colors.xlsx"
|
_TEMPLATE_FILE = "pantone\xa02670\xa0colors.xlsx"
|
||||||
_TARGET_RESULT_COUNT = 2670
|
|
||||||
|
|
||||||
|
|
||||||
def create_pantone_baseline_panel(self):
|
def create_pantone_baseline_panel(self):
|
||||||
@@ -34,6 +27,7 @@ def create_pantone_baseline_panel(self):
|
|||||||
self._pantone_pause_requested = False
|
self._pantone_pause_requested = False
|
||||||
self._pantone_stop_requested = False
|
self._pantone_stop_requested = False
|
||||||
self._pantone_next_index = 0
|
self._pantone_next_index = 0
|
||||||
|
self._pantone_target_count = 0
|
||||||
|
|
||||||
root = ttk.Frame(frame, padding=10)
|
root = ttk.Frame(frame, padding=10)
|
||||||
root.pack(fill=tk.BOTH, expand=True)
|
root.pack(fill=tk.BOTH, expand=True)
|
||||||
@@ -47,12 +41,12 @@ def create_pantone_baseline_panel(self):
|
|||||||
).pack(side=tk.LEFT)
|
).pack(side=tk.LEFT)
|
||||||
|
|
||||||
self.pantone_status_var = tk.StringVar(value="未开始")
|
self.pantone_status_var = tk.StringVar(value="未开始")
|
||||||
self.pantone_progress_var = tk.StringVar(value="0 / 2670")
|
self.pantone_progress_var = tk.StringVar(value="0 / 0")
|
||||||
self.pantone_settle_var = tk.StringVar(value="0.35")
|
self.pantone_settle_var = tk.StringVar(value="0.35")
|
||||||
|
|
||||||
config_row = ttk.LabelFrame(root, text="任务配置", padding=8)
|
config_row = ttk.LabelFrame(root, text="任务配置", padding=8)
|
||||||
config_row.pack(fill=tk.X)
|
config_row.pack(fill=tk.X)
|
||||||
ttk.Label(config_row, text=f"Pattern来源: settings/{_PATTERN_FILE}").pack(
|
ttk.Label(config_row, text=f"Pattern来源: settings/{_TEMPLATE_FILE}").pack(
|
||||||
side=tk.LEFT
|
side=tk.LEFT
|
||||||
)
|
)
|
||||||
ttk.Label(config_row, text="稳定等待(s):").pack(side=tk.LEFT, padx=(14, 4))
|
ttk.Label(config_row, text="稳定等待(s):").pack(side=tk.LEFT, padx=(14, 4))
|
||||||
@@ -160,38 +154,38 @@ def toggle_pantone_baseline_panel(self):
|
|||||||
|
|
||||||
|
|
||||||
def _load_patterns(self):
|
def _load_patterns(self):
|
||||||
path = os.path.join("settings", _PATTERN_FILE)
|
path = os.path.join("settings", _TEMPLATE_FILE)
|
||||||
if not os.path.isfile(path):
|
if not os.path.isfile(path):
|
||||||
raise FileNotFoundError(f"未找到 pattern 文件: {path}")
|
raise FileNotFoundError(f"未找到模板文件: {path}")
|
||||||
|
|
||||||
|
from openpyxl import load_workbook
|
||||||
|
|
||||||
patterns = []
|
patterns = []
|
||||||
with open(path, "r", encoding="utf-8-sig", newline="") as fp:
|
wb = load_workbook(path, read_only=True, data_only=True)
|
||||||
reader = csv.DictReader(fp)
|
ws = wb.active
|
||||||
for row in reader:
|
|
||||||
try:
|
try:
|
||||||
r = int(row.get("R", "").strip())
|
for row in ws.iter_rows(min_row=2, values_only=True):
|
||||||
g = int(row.get("G", "").strip())
|
if not row:
|
||||||
b = int(row.get("B", "").strip())
|
continue
|
||||||
|
try:
|
||||||
|
r = int(row[0]) if row[0] is not None else None
|
||||||
|
g = int(row[1]) if len(row) > 1 and row[1] is not None else None
|
||||||
|
b = int(row[2]) if len(row) > 2 and row[2] is not None else None
|
||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
|
if r is None or g is None or b is None:
|
||||||
|
continue
|
||||||
if min(r, g, b) < 0 or max(r, g, b) > 255:
|
if min(r, g, b) < 0 or max(r, g, b) > 255:
|
||||||
continue
|
continue
|
||||||
patterns.append((r, g, b))
|
patterns.append((r, g, b))
|
||||||
|
finally:
|
||||||
|
wb.close()
|
||||||
|
|
||||||
if not patterns:
|
if not patterns:
|
||||||
raise RuntimeError("pattern 文件为空或格式不正确,需包含列 R,G,B")
|
raise RuntimeError("模板中未找到有效 RGB 列表(需包含 R/G/B 三列)")
|
||||||
return patterns
|
return patterns
|
||||||
|
|
||||||
|
|
||||||
def _build_temp_patch(self, rgb):
|
|
||||||
width, height = get_current_resolution(self.ucd)
|
|
||||||
temp_dir = os.path.join(tempfile.gettempdir(), "pq_pantone_baseline")
|
|
||||||
os.makedirs(temp_dir, exist_ok=True)
|
|
||||||
file_path = os.path.join(temp_dir, "pantone_current_patch.png")
|
|
||||||
Image.new("RGB", (width, height), rgb).save(file_path, format="PNG")
|
|
||||||
return file_path
|
|
||||||
|
|
||||||
|
|
||||||
def _start_pantone_baseline(self):
|
def _start_pantone_baseline(self):
|
||||||
if self._pantone_running:
|
if self._pantone_running:
|
||||||
messagebox.showinfo("提示", "Pantone 任务正在执行")
|
messagebox.showinfo("提示", "Pantone 任务正在执行")
|
||||||
@@ -213,6 +207,7 @@ def _start_pantone_baseline(self):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self.pantone_patterns = _load_patterns(self)
|
self.pantone_patterns = _load_patterns(self)
|
||||||
|
self._pantone_target_count = len(self.pantone_patterns)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
messagebox.showerror("读取失败", str(exc))
|
messagebox.showerror("读取失败", str(exc))
|
||||||
return
|
return
|
||||||
@@ -228,7 +223,7 @@ def _start_pantone_baseline(self):
|
|||||||
self._pantone_control_event = threading.Event()
|
self._pantone_control_event = threading.Event()
|
||||||
self._pantone_next_index = 0
|
self._pantone_next_index = 0
|
||||||
self.pantone_status_var.set("执行中")
|
self.pantone_status_var.set("执行中")
|
||||||
self.pantone_progress_var.set(f"0 / {_TARGET_RESULT_COUNT}")
|
self.pantone_progress_var.set(f"0 / {self._pantone_target_count}")
|
||||||
self.pantone_results = []
|
self.pantone_results = []
|
||||||
for item in self.pantone_tree.get_children():
|
for item in self.pantone_tree.get_children():
|
||||||
self.pantone_tree.delete(item)
|
self.pantone_tree.delete(item)
|
||||||
@@ -244,9 +239,6 @@ def _resume_pantone_baseline(self):
|
|||||||
if not self._pantone_paused:
|
if not self._pantone_paused:
|
||||||
messagebox.showinfo("提示", "当前没有可继续的暂停任务")
|
messagebox.showinfo("提示", "当前没有可继续的暂停任务")
|
||||||
return
|
return
|
||||||
if self._pantone_next_index >= _TARGET_RESULT_COUNT:
|
|
||||||
messagebox.showinfo("提示", "任务已完成,无需继续")
|
|
||||||
return
|
|
||||||
if not getattr(self, "ucd", None) or not self.ucd.status:
|
if not getattr(self, "ucd", None) or not self.ucd.status:
|
||||||
messagebox.showwarning("警告", "请先连接 UCD323")
|
messagebox.showwarning("警告", "请先连接 UCD323")
|
||||||
return
|
return
|
||||||
@@ -264,10 +256,15 @@ def _resume_pantone_baseline(self):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self.pantone_patterns = _load_patterns(self)
|
self.pantone_patterns = _load_patterns(self)
|
||||||
|
self._pantone_target_count = len(self.pantone_patterns)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
messagebox.showerror("读取失败", str(exc))
|
messagebox.showerror("读取失败", str(exc))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if self._pantone_next_index >= self._pantone_target_count:
|
||||||
|
messagebox.showinfo("提示", "任务已完成,无需继续")
|
||||||
|
return
|
||||||
|
|
||||||
self._pantone_running = True
|
self._pantone_running = True
|
||||||
self._pantone_paused = False
|
self._pantone_paused = False
|
||||||
self._pantone_pause_requested = False
|
self._pantone_pause_requested = False
|
||||||
@@ -280,13 +277,14 @@ def _resume_pantone_baseline(self):
|
|||||||
|
|
||||||
|
|
||||||
def _launch_worker(self, start_index, settle):
|
def _launch_worker(self, start_index, settle):
|
||||||
total = _TARGET_RESULT_COUNT
|
total = self._pantone_target_count or len(self.pantone_patterns)
|
||||||
|
|
||||||
def worker():
|
def worker():
|
||||||
end_state = "completed"
|
end_state = "completed"
|
||||||
try:
|
try:
|
||||||
src = self.pantone_patterns
|
src = self.pantone_patterns
|
||||||
src_count = len(src)
|
src_count = len(src)
|
||||||
|
rgb_session = self.pattern_service.prepare_session("rgb", log_details=False)
|
||||||
self._dispatch_ui(
|
self._dispatch_ui(
|
||||||
self.log_gui.log,
|
self.log_gui.log,
|
||||||
f"Pantone 认证摸底启动: source={src_count}, target={total}, start={start_index + 1}",
|
f"Pantone 认证摸底启动: source={src_count}, target={total}, start={start_index + 1}",
|
||||||
@@ -301,9 +299,10 @@ def _launch_worker(self, start_index, settle):
|
|||||||
break
|
break
|
||||||
|
|
||||||
r, g, b = src[i % src_count]
|
r, g, b = src[i % src_count]
|
||||||
image_path = _build_temp_patch(self, (r, g, b))
|
try:
|
||||||
if not send_image_pattern(self.ucd, image_path):
|
self.pattern_service.send_rgb((r, g, b), session=rgb_session)
|
||||||
raise RuntimeError(f"第 {i + 1} 组发送失败")
|
except Exception as exc:
|
||||||
|
raise RuntimeError(f"第 {i + 1} 组发送失败: {exc}") from exc
|
||||||
|
|
||||||
if settle > 0 and self._pantone_control_event is not None:
|
if settle > 0 and self._pantone_control_event is not None:
|
||||||
self._pantone_control_event.clear()
|
self._pantone_control_event.clear()
|
||||||
@@ -360,6 +359,19 @@ def _launch_worker(self, start_index, settle):
|
|||||||
f"Pantone 任务完成,共 {len(self.pantone_results)} 条数据",
|
f"Pantone 任务完成,共 {len(self.pantone_results)} 条数据",
|
||||||
"success",
|
"success",
|
||||||
)
|
)
|
||||||
|
try:
|
||||||
|
auto_path = _auto_save_template(self)
|
||||||
|
self._dispatch_ui(
|
||||||
|
self.log_gui.log,
|
||||||
|
f"Pantone 模板已自动保存: {auto_path}",
|
||||||
|
"success",
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
self._dispatch_ui(
|
||||||
|
self.log_gui.log,
|
||||||
|
f"Pantone 自动保存模板失败: {exc}",
|
||||||
|
"error",
|
||||||
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
self._pantone_paused = False
|
self._pantone_paused = False
|
||||||
self._dispatch_ui(self.pantone_status_var.set, "执行失败")
|
self._dispatch_ui(self.pantone_status_var.set, "执行失败")
|
||||||
@@ -430,7 +442,8 @@ def _clear_results(self):
|
|||||||
self._pantone_next_index = 0
|
self._pantone_next_index = 0
|
||||||
for item in self.pantone_tree.get_children():
|
for item in self.pantone_tree.get_children():
|
||||||
self.pantone_tree.delete(item)
|
self.pantone_tree.delete(item)
|
||||||
self.pantone_progress_var.set(f"0 / {_TARGET_RESULT_COUNT}")
|
self._pantone_target_count = 0
|
||||||
|
self.pantone_progress_var.set("0 / 0")
|
||||||
self.pantone_status_var.set("结果已清空")
|
self.pantone_status_var.set("结果已清空")
|
||||||
_set_button_states(self)
|
_set_button_states(self)
|
||||||
|
|
||||||
@@ -447,7 +460,7 @@ def _set_button_states(self):
|
|||||||
self.pantone_pause_btn.configure(state=tk.DISABLED)
|
self.pantone_pause_btn.configure(state=tk.DISABLED)
|
||||||
self.pantone_end_btn.configure(state=tk.NORMAL if (self._pantone_paused or self.pantone_results) else tk.DISABLED)
|
self.pantone_end_btn.configure(state=tk.NORMAL if (self._pantone_paused or self.pantone_results) else tk.DISABLED)
|
||||||
|
|
||||||
can_resume = self._pantone_paused and self._pantone_next_index < _TARGET_RESULT_COUNT
|
can_resume = self._pantone_paused and self._pantone_next_index < self._pantone_target_count
|
||||||
self.pantone_resume_btn.configure(state=tk.NORMAL if can_resume else tk.DISABLED)
|
self.pantone_resume_btn.configure(state=tk.NORMAL if can_resume else tk.DISABLED)
|
||||||
|
|
||||||
|
|
||||||
@@ -456,7 +469,7 @@ def _save_as_template(self):
|
|||||||
messagebox.showinfo("提示", "暂无可导出的结果")
|
messagebox.showinfo("提示", "暂无可导出的结果")
|
||||||
return
|
return
|
||||||
|
|
||||||
default_name = "pantone 2670 colors.xlsx"
|
default_name = _TEMPLATE_FILE.replace("\xa0", " ")
|
||||||
path = filedialog.asksaveasfilename(
|
path = filedialog.asksaveasfilename(
|
||||||
title="另存为 Pantone 模板",
|
title="另存为 Pantone 模板",
|
||||||
defaultextension=".xlsx",
|
defaultextension=".xlsx",
|
||||||
@@ -466,9 +479,41 @@ def _save_as_template(self):
|
|||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
_write_template_xlsx(self, path)
|
||||||
|
self.log_gui.log(f"Pantone 模板已保存: {path}", level="success")
|
||||||
|
self.pantone_status_var.set(f"已保存: {os.path.basename(path)}")
|
||||||
|
except Exception as exc:
|
||||||
|
messagebox.showerror("保存失败", f"写入 xlsx 失败: {exc}")
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_results_dir(self):
|
||||||
|
if getattr(self, "config_file", None):
|
||||||
|
root_dir = os.path.dirname(os.path.dirname(self.config_file))
|
||||||
|
else:
|
||||||
|
root_dir = os.path.dirname(
|
||||||
|
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
)
|
||||||
|
results_dir = os.path.join(root_dir, "results")
|
||||||
|
os.makedirs(results_dir, exist_ok=True)
|
||||||
|
return results_dir
|
||||||
|
|
||||||
|
|
||||||
|
def _auto_save_template(self):
|
||||||
|
results_dir = _resolve_results_dir(self)
|
||||||
|
target_count = len(self.pantone_results)
|
||||||
|
filename = (
|
||||||
|
f"pantone_{target_count}_baseline_"
|
||||||
|
f"{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
|
||||||
|
)
|
||||||
|
path = os.path.join(results_dir, filename)
|
||||||
|
_write_template_xlsx(self, path)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def _write_template_xlsx(self, path):
|
||||||
# 优先复制 settings 模板,再覆盖数据区;没有模板时自动创建同结构表。
|
# 优先复制 settings 模板,再覆盖数据区;没有模板时自动创建同结构表。
|
||||||
template_path = os.path.join("settings", _TEMPLATE_FILE)
|
template_path = os.path.join("settings", _TEMPLATE_FILE)
|
||||||
try:
|
|
||||||
from openpyxl import load_workbook, Workbook
|
from openpyxl import load_workbook, Workbook
|
||||||
|
|
||||||
if os.path.isfile(template_path):
|
if os.path.isfile(template_path):
|
||||||
@@ -500,7 +545,3 @@ def _save_as_template(self):
|
|||||||
ws.cell(row=idx, column=6, value=float(item["y"]))
|
ws.cell(row=idx, column=6, value=float(item["y"]))
|
||||||
|
|
||||||
wb.save(path)
|
wb.save(path)
|
||||||
self.log_gui.log(f"Pantone 模板已保存: {path}", level="success")
|
|
||||||
self.pantone_status_var.set(f"已保存: {os.path.basename(path)}")
|
|
||||||
except Exception as exc:
|
|
||||||
messagebox.showerror("保存失败", f"写入 xlsx 失败: {exc}")
|
|
||||||
|
|||||||
@@ -768,28 +768,25 @@ class PQDebugPanel:
|
|||||||
# 禁用按钮
|
# 禁用按钮
|
||||||
self._disable_test_button(test_type, test_item)
|
self._disable_test_button(test_type, test_item)
|
||||||
|
|
||||||
# 根据测试类型设置信号格式
|
|
||||||
self._setup_signal_format(test_type)
|
|
||||||
|
|
||||||
# 获取图案索引并发送
|
# 获取图案索引并发送
|
||||||
if test_item in ["gamma", "eotf"]:
|
if test_item in ["gamma", "eotf"]:
|
||||||
pattern_index = self.get_gray_index(selected)
|
pattern_index = self.get_gray_index(selected)
|
||||||
self.app.config.set_current_pattern("gray")
|
pattern_mode = "gray"
|
||||||
elif test_item == "accuracy":
|
elif test_item == "accuracy":
|
||||||
pattern_index = self.get_color_index(selected)
|
pattern_index = self.get_color_index(selected)
|
||||||
self.app.config.set_current_pattern("accuracy")
|
pattern_mode = "accuracy"
|
||||||
elif test_item == "rgb":
|
elif test_item == "rgb":
|
||||||
pattern_index = self.get_rgb_index(selected)
|
pattern_index = self.get_rgb_index(selected)
|
||||||
self.app.config.set_current_pattern("rgb")
|
pattern_mode = "rgb"
|
||||||
|
else:
|
||||||
|
raise ValueError(f"不支持的测试项目: {test_item}")
|
||||||
|
|
||||||
# 设置图案
|
session = self.app.pattern_service.prepare_session(
|
||||||
self.app.ucd.set_ucd_params(self.app.config)
|
pattern_mode,
|
||||||
|
test_type=test_type,
|
||||||
# 跳转到目标图案
|
log_details=False,
|
||||||
for i in range(pattern_index + 1):
|
)
|
||||||
self.app.ucd.set_next_pattern()
|
self.app.pattern_service.send_session_pattern(session, pattern_index)
|
||||||
|
|
||||||
self.app.ucd.run()
|
|
||||||
time.sleep(1.5)
|
time.sleep(1.5)
|
||||||
|
|
||||||
# 测量数据
|
# 测量数据
|
||||||
@@ -815,28 +812,6 @@ class PQDebugPanel:
|
|||||||
self.app.log_gui.log(traceback.format_exc(), level="error")
|
self.app.log_gui.log(traceback.format_exc(), level="error")
|
||||||
self._enable_test_button(test_type, test_item)
|
self._enable_test_button(test_type, test_item)
|
||||||
|
|
||||||
def _setup_signal_format(self, test_type):
|
|
||||||
"""设置信号格式"""
|
|
||||||
if test_type == "screen_module":
|
|
||||||
self.app.ucd.set_ucd_params(self.app.config)
|
|
||||||
|
|
||||||
elif test_type == "sdr_movie":
|
|
||||||
self.app.ucd.set_sdr_format(
|
|
||||||
color_space=self.app.sdr_color_space_var.get(),
|
|
||||||
gamma=self.app.sdr_gamma_type_var.get(),
|
|
||||||
data_range=self.app.sdr_data_range_var.get(),
|
|
||||||
bit_depth=self.app.sdr_bit_depth_var.get(),
|
|
||||||
)
|
|
||||||
|
|
||||||
elif test_type == "hdr_movie":
|
|
||||||
self.app.ucd.set_hdr_format(
|
|
||||||
color_space=self.app.hdr_color_space_var.get(),
|
|
||||||
data_range=self.app.hdr_data_range_var.get(),
|
|
||||||
bit_depth=self.app.hdr_bit_depth_var.get(),
|
|
||||||
max_cll=self.app.hdr_maxcll_var.get(),
|
|
||||||
max_fall=self.app.hdr_maxfall_var.get(),
|
|
||||||
)
|
|
||||||
|
|
||||||
def _compare_and_display(self, test_type, test_item, selected, new_data):
|
def _compare_and_display(self, test_type, test_item, selected, new_data):
|
||||||
"""对比数据并显示"""
|
"""对比数据并显示"""
|
||||||
# 获取原始数据
|
# 获取原始数据
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
APP_NAME = "PQ 自动化测试工具"
|
APP_NAME = "PQ 自动化测试工具"
|
||||||
APP_VERSION = "1.1.0"
|
APP_VERSION = "0.1.0"
|
||||||
|
|
||||||
|
|
||||||
def get_app_title():
|
def get_app_title():
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -16,6 +16,7 @@ class UCDController:
|
|||||||
self.config = None
|
self.config = None
|
||||||
self.color_mode = None
|
self.color_mode = None
|
||||||
self.status = False
|
self.status = False
|
||||||
|
self.current_interface = "HDMI"
|
||||||
|
|
||||||
self.current_timing = None
|
self.current_timing = None
|
||||||
self.current_pattern = None
|
self.current_pattern = None
|
||||||
@@ -42,12 +43,14 @@ class UCDController:
|
|||||||
try:
|
try:
|
||||||
self.role = temp_dev.select_role(UniTAP.dev.UCD323.HDMISource)
|
self.role = temp_dev.select_role(UniTAP.dev.UCD323.HDMISource)
|
||||||
self.dev = temp_dev
|
self.dev = temp_dev
|
||||||
|
self.current_interface = "HDMI"
|
||||||
|
|
||||||
except Exception as role_error:
|
except Exception as role_error:
|
||||||
self._close_device_object(temp_dev)
|
self._close_device_object(temp_dev)
|
||||||
raise role_error
|
raise role_error
|
||||||
|
|
||||||
self.timing_manager = self.role.hdtx.pg.timing_manager
|
pg, _ = self.get_tx_modules()
|
||||||
|
self.timing_manager = pg.timing_manager
|
||||||
self.color_mode = UniTAP.ColorInfo()
|
self.color_mode = UniTAP.ColorInfo()
|
||||||
self.status = True
|
self.status = True
|
||||||
|
|
||||||
@@ -72,6 +75,7 @@ class UCDController:
|
|||||||
self.current_pattern_param = None
|
self.current_pattern_param = None
|
||||||
self.current_pattern_params = None
|
self.current_pattern_params = None
|
||||||
self.current_pattern_index = 0
|
self.current_pattern_index = 0
|
||||||
|
self.current_interface = "HDMI"
|
||||||
|
|
||||||
self.lUniTAP = None
|
self.lUniTAP = None
|
||||||
|
|
||||||
@@ -94,6 +98,7 @@ class UCDController:
|
|||||||
self.current_pattern_param = None
|
self.current_pattern_param = None
|
||||||
self.current_pattern_params = None
|
self.current_pattern_params = None
|
||||||
self.current_pattern_index = 0
|
self.current_pattern_index = 0
|
||||||
|
self.current_interface = "HDMI"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.lUniTAP = None
|
self.lUniTAP = None
|
||||||
@@ -139,10 +144,51 @@ class UCDController:
|
|||||||
self.current_pattern_param = None
|
self.current_pattern_param = None
|
||||||
self.current_pattern_params = None
|
self.current_pattern_params = None
|
||||||
self.current_pattern_index = 0
|
self.current_pattern_index = 0
|
||||||
|
self.current_interface = "HDMI"
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def get_tx_modules(self):
|
||||||
|
"""根据当前接口返回 (pg, ag) 模块。"""
|
||||||
|
if not self.role:
|
||||||
|
raise RuntimeError("UCD 未打开,无法获取 TX 模块")
|
||||||
|
|
||||||
|
interface = getattr(self, "current_interface", None)
|
||||||
|
if interface in (None, "HDMI"):
|
||||||
|
return self.role.hdtx.pg, self.role.hdtx.ag
|
||||||
|
if interface in ("DP", "Type-C"):
|
||||||
|
return self.role.dptx.pg, self.role.dptx.ag
|
||||||
|
raise ValueError(f"不支持的接口类型: {interface}")
|
||||||
|
|
||||||
|
def _resolve_timing(self, pg=None):
|
||||||
|
"""优先从 current_timing 读取 timing,必要时回退到 TX 模块。"""
|
||||||
|
if self.current_timing is not None:
|
||||||
|
return self.current_timing
|
||||||
|
|
||||||
|
if pg is not None:
|
||||||
|
get_vm = getattr(pg, "get_vm", None)
|
||||||
|
if callable(get_vm):
|
||||||
|
try:
|
||||||
|
vm = get_vm()
|
||||||
|
return getattr(vm, "timing", None)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_current_resolution(self, default=(3840, 2160)):
|
||||||
|
"""从当前 timing 获取 (width, height),失败时返回 default。"""
|
||||||
|
try:
|
||||||
|
pg, _ = self.get_tx_modules()
|
||||||
|
timing = self._resolve_timing(pg)
|
||||||
|
if timing and hasattr(timing, "h_active") and hasattr(timing, "v_active"):
|
||||||
|
return timing.h_active, timing.v_active
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return default
|
||||||
|
|
||||||
def _cleanup(self):
|
def _cleanup(self):
|
||||||
"""清理设备资源"""
|
"""清理设备资源"""
|
||||||
try:
|
try:
|
||||||
@@ -161,6 +207,9 @@ class UCDController:
|
|||||||
self.config = config
|
self.config = config
|
||||||
test_type = self.config.current_test_type
|
test_type = self.config.current_test_type
|
||||||
|
|
||||||
|
pg, _ = self.get_tx_modules()
|
||||||
|
self.timing_manager = pg.timing_manager
|
||||||
|
|
||||||
if test_type == "hdr_movie":
|
if test_type == "hdr_movie":
|
||||||
color_format_str = self.config.current_test_types[test_type]["color_format"]
|
color_format_str = self.config.current_test_types[test_type]["color_format"]
|
||||||
color_format = UCDEnum.ColorInfo.get_color_format(color_format_str)
|
color_format = UCDEnum.ColorInfo.get_color_format(color_format_str)
|
||||||
@@ -197,9 +246,58 @@ class UCDController:
|
|||||||
"""运行设备"""
|
"""运行设备"""
|
||||||
self.apply_video_mode()
|
self.apply_video_mode()
|
||||||
self.apply_pattern()
|
self.apply_pattern()
|
||||||
self.role.hdtx.pg.apply()
|
pg, _ = self.get_tx_modules()
|
||||||
|
pg.apply()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def send_image_pattern(self, image_path):
|
||||||
|
"""发送图片 Pattern(依赖当前 timing/color_mode 状态)。"""
|
||||||
|
if not self.status or not self.role:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
pg, _ = self.get_tx_modules()
|
||||||
|
self.apply_video_mode()
|
||||||
|
pg.set_pattern(pattern=image_path)
|
||||||
|
pg.apply()
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def send_solid_rgb_pattern(self, rgb):
|
||||||
|
"""发送纯色 RGB Pattern(依赖当前 timing/color_mode 状态)。"""
|
||||||
|
if not self.status or not self.role:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.current_pattern = UCDEnum.VideoPatternInfo.get_video_pattern("solidcolor")
|
||||||
|
if self.current_pattern is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return self.send_current_pattern_params(list(rgb))
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def send_current_pattern_params(self, pattern_params):
|
||||||
|
"""发送当前已配置的 pattern,并可附带当前 pattern 参数。"""
|
||||||
|
if not self.status or not self.role:
|
||||||
|
return False
|
||||||
|
|
||||||
|
try:
|
||||||
|
if self.current_pattern is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if pattern_params is not None and not self.set_pattern(
|
||||||
|
self.current_pattern,
|
||||||
|
pattern_params,
|
||||||
|
):
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.run()
|
||||||
|
return True
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
|
||||||
def set_color_mode(self, cf, bpc, cm):
|
def set_color_mode(self, cf, bpc, cm):
|
||||||
"""设置颜色模式"""
|
"""设置颜色模式"""
|
||||||
current_dynamic_range = None
|
current_dynamic_range = None
|
||||||
@@ -246,7 +344,8 @@ class UCDController:
|
|||||||
video_mode = UniTAP.VideoMode(
|
video_mode = UniTAP.VideoMode(
|
||||||
timing=self.current_timing, color_info=self.color_mode
|
timing=self.current_timing, color_info=self.color_mode
|
||||||
)
|
)
|
||||||
self.role.hdtx.pg.set_vm(vm=video_mode)
|
pg, _ = self.get_tx_modules()
|
||||||
|
pg.set_vm(vm=video_mode)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def set_pattern(self, pattern, pattern_params=None):
|
def set_pattern(self, pattern, pattern_params=None):
|
||||||
@@ -292,10 +391,11 @@ class UCDController:
|
|||||||
def apply_pattern(self):
|
def apply_pattern(self):
|
||||||
"""应用当前pattern"""
|
"""应用当前pattern"""
|
||||||
if self.current_pattern:
|
if self.current_pattern:
|
||||||
self.role.hdtx.pg.set_pattern(self.current_pattern)
|
pg, _ = self.get_tx_modules()
|
||||||
|
pg.set_pattern(self.current_pattern)
|
||||||
|
|
||||||
if self.current_pattern_param:
|
if self.current_pattern_param:
|
||||||
self.role.hdtx.pg.set_pattern_params(self.current_pattern_param)
|
pg.set_pattern_params(self.current_pattern_param)
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -1,80 +1,46 @@
|
|||||||
"""通用 UCD323/UCDController 辅助函数。
|
"""通用 UCD323/UCDController 辅助函数。
|
||||||
|
|
||||||
封装"按当前接口取 tx 模块"、"读取分辨率"、"发送图片 Pattern"等所有
|
保留为兼容层和薄代理,避免业务模块直接依赖控制器内部实现细节。
|
||||||
测试模块共用的低层 UCD 操作,避免在多个业务模块中重复 if/else。
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import UniTAP
|
|
||||||
|
|
||||||
|
|
||||||
def get_tx_modules(ucd):
|
def get_tx_modules(ucd):
|
||||||
"""根据当前接口返回 (pg, ag) 模块。
|
"""根据当前接口返回 (pg, ag) 模块。"""
|
||||||
|
return ucd.get_tx_modules()
|
||||||
兼容 UCD323Controller(多接口,含 current_interface 属性)
|
|
||||||
与老的 UCDController(仅 HDMI)。
|
|
||||||
"""
|
|
||||||
interface = getattr(ucd, "current_interface", None)
|
|
||||||
if interface in (None, "HDMI"):
|
|
||||||
return ucd.role.hdtx.pg, ucd.role.hdtx.ag
|
|
||||||
if interface in ("DP", "Type-C"):
|
|
||||||
return ucd.role.dptx.pg, ucd.role.dptx.ag
|
|
||||||
raise ValueError(f"不支持的接口类型: {interface}")
|
|
||||||
|
|
||||||
|
|
||||||
def get_current_resolution(ucd, default=(3840, 2160)):
|
def get_current_resolution(ucd, default=(3840, 2160)):
|
||||||
"""从 UCD 当前 timing 获取 (width, height),失败时返回 default。"""
|
"""从 UCD 当前 timing 获取 (width, height),失败时返回 default。"""
|
||||||
try:
|
return ucd.get_current_resolution(default)
|
||||||
pg, _ = get_tx_modules(ucd)
|
|
||||||
vm = pg.get_vm()
|
|
||||||
timing = getattr(vm, "timing", None)
|
|
||||||
if timing and hasattr(timing, "h_active") and hasattr(timing, "v_active"):
|
|
||||||
return timing.h_active, timing.v_active
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
timing = getattr(ucd, "current_timing", None)
|
|
||||||
if timing and hasattr(timing, "h_active") and hasattr(timing, "v_active"):
|
|
||||||
return timing.h_active, timing.v_active
|
|
||||||
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
def send_image_pattern(ucd, image_path, *, bpc=8, color_format=None, colorimetry=None):
|
def send_image_pattern(ucd, image_path):
|
||||||
"""通过 UCD 发送一张本地图片作为显示 Pattern。
|
"""通过 UCDController 发送一张本地图片作为显示 Pattern。"""
|
||||||
|
|
||||||
自动停止音频以避免蜂鸣声。颜色参数留空时使用 RGB / sRGB 默认值。
|
|
||||||
返回 True/False。
|
|
||||||
"""
|
|
||||||
if not getattr(ucd, "status", False):
|
if not getattr(ucd, "status", False):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
send_via_controller = getattr(ucd, "send_image_pattern", None)
|
||||||
pg, ag = get_tx_modules(ucd)
|
if not callable(send_via_controller):
|
||||||
except Exception:
|
return False
|
||||||
|
return bool(send_via_controller(image_path))
|
||||||
|
|
||||||
|
|
||||||
|
def send_solid_rgb_pattern(ucd, rgb, *, raise_on_error=False):
|
||||||
|
"""通过 UCDController 当前状态发送一组纯色 RGB Pattern。"""
|
||||||
|
if not getattr(ucd, "status", False):
|
||||||
|
if raise_on_error:
|
||||||
|
raise RuntimeError("UCD 未连接,无法发送纯色 Pattern")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
try:
|
send_via_controller = getattr(ucd, "send_solid_rgb_pattern", None)
|
||||||
ag.stop_generate()
|
if not callable(send_via_controller):
|
||||||
except Exception:
|
if raise_on_error:
|
||||||
pass
|
raise RuntimeError("UCDController 未实现 send_solid_rgb_pattern")
|
||||||
|
|
||||||
color_mode = UniTAP.ColorInfo()
|
|
||||||
color_mode.color_format = color_format or UniTAP.ColorInfo.ColorFormat.CF_RGB
|
|
||||||
color_mode.bpc = bpc
|
|
||||||
color_mode.colorimetry = colorimetry or UniTAP.ColorInfo.Colorimetry.CM_sRGB
|
|
||||||
|
|
||||||
timing = None
|
|
||||||
try:
|
|
||||||
vm = pg.get_vm()
|
|
||||||
timing = getattr(vm, "timing", None)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
try:
|
|
||||||
if timing:
|
|
||||||
pg.set_vm(vm=UniTAP.VideoMode(timing=timing, color_info=color_mode))
|
|
||||||
pg.set_pattern(pattern=image_path)
|
|
||||||
pg.apply()
|
|
||||||
return True
|
|
||||||
except Exception:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
ok = bool(send_via_controller(list(rgb)))
|
||||||
|
if not ok and raise_on_error:
|
||||||
|
raise RuntimeError(f"发送纯色 Pattern 失败: {rgb}")
|
||||||
|
return ok
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ from app.tests.local_dimming import (
|
|||||||
stop_local_dimming_test as _ld_stop_local_dimming_test,
|
stop_local_dimming_test as _ld_stop_local_dimming_test,
|
||||||
update_ld_results as _ld_update_ld_results,
|
update_ld_results as _ld_update_ld_results,
|
||||||
)
|
)
|
||||||
|
from app.services import PatternService
|
||||||
from app.device.connection import (
|
from app.device.connection import (
|
||||||
check_com_connections as _dev_check_com_connections,
|
check_com_connections as _dev_check_com_connections,
|
||||||
check_port_connection as _dev_check_port_connection,
|
check_port_connection as _dev_check_port_connection,
|
||||||
@@ -139,6 +140,7 @@ class PQAutomationApp:
|
|||||||
|
|
||||||
# 创建配置对象
|
# 创建配置对象
|
||||||
self.config = PQConfig()
|
self.config = PQConfig()
|
||||||
|
self.pattern_service = PatternService(self)
|
||||||
# 结果管理器:按 test_type 保留每次测试结果,始终存在,避免未初始化错误
|
# 结果管理器:按 test_type 保留每次测试结果,始终存在,避免未初始化错误
|
||||||
self.results = PQResultStore()
|
self.results = PQResultStore()
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"device_config": {
|
"device_config": {
|
||||||
"ca_com": "COM1",
|
"ca_com": "COM3",
|
||||||
"ucd_list": "0: UCD-323 [2128C209]",
|
"ucd_list": "0: UCD-323 [2128C209]",
|
||||||
"ca_channel": "0"
|
"ca_channel": "0"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user