修改日志/细节问题

This commit is contained in:
xinzhu.yin
2026-04-21 16:03:11 +08:00
parent e27312d0a3
commit 9a2ac69afb
14 changed files with 230 additions and 144 deletions

View File

@@ -40,7 +40,7 @@ def calculate_gamut_coverage(points1, points2):
return intersection_area, coverage return intersection_area, coverage
except Exception as e: except Exception as e:
print(f"[Error] 计算色域覆盖率失败: {e}") print(f"计算色域覆盖率失败: {e}")
return 0.0, 0.0 return 0.0, 0.0
@@ -227,7 +227,7 @@ def calculate_uv_gamut_coverage(uv_coords, reference="DCI-P3"):
return coverage return coverage
except Exception as e: except Exception as e:
print(f"[Error] 计算 u'v' 色域覆盖率失败: {str(e)}") print(f"计算 u'v' 色域覆盖率失败: {str(e)}")
import traceback import traceback
traceback.print_exc() traceback.print_exc()

View File

@@ -78,7 +78,7 @@ def clear_config_file(self):
self.log_gui.log("配置文件不存在", level="error") self.log_gui.log("配置文件不存在", level="error")
except Exception as e: except Exception as e:
messagebox.showerror("错误", "[Error] 清理失败") messagebox.showerror("错误", "清理失败")
self.log_gui.log(f"[Error] 配置文件清理失败: {str(e)}", level="error") self.log_gui.log(f"配置文件清理失败: {str(e)}", level="error")

View File

@@ -621,14 +621,14 @@ if __name__ == "__main__":
if current_count == 29: if current_count == 29:
print(" set_current_pattern 工作正常") print(" set_current_pattern 工作正常")
else: else:
print(f" [Error] set_current_pattern 失败!只有 {current_count} 个图案") print(f" set_current_pattern 失败!只有 {current_count} 个图案")
else: else:
print(f"\n[Error] 配置错误!") print(f"\n配置错误!")
print(f" 期望: 29 个图案, measurement_max_value=28") print(f" 期望: 29 个图案, measurement_max_value=28")
print(f" 实际: {pattern_count} 个图案, measurement_max_value={max_value}") print(f" 实际: {pattern_count} 个图案, measurement_max_value={max_value}")
print("\n[Error] 请检查 default_pattern_accuracy 定义!") print("\n请检查 default_pattern_accuracy 定义!")
print(" 应该包含:") print(" 应该包含:")
print(" - 5个灰阶") print(" - 5个灰阶")
print(" - 18个 ColorChecker 色块") print(" - 18个 ColorChecker 色块")

View File

@@ -414,6 +414,122 @@ class PQResult:
} }
class PQResultStore:
"""按 test_type 管理多个 PQResult 实例。
解决两个问题:
1. `self.results` 在测试流程启动前就存在(避免 AttributeError / NoneType 访问)。
2. 支持同时保留多种 test_typescreen_module / sdr_movie / hdr_movie的历史结果
而不是每次启动新测试就把上一次的结果覆盖掉。
使用方式:
# 主类 __init__ 中:
self.results = PQResultStore()
# 启动一次新测试时:
self.results.new("screen_module", "屏模组性能测试")
# 现有调用点(隐式访问当前活跃结果)仍然生效:
self.results.add_intermediate_data("gamut", "rgb", data)
self.results.get_intermediate_data("shared", "gray")
# 跨类型显式访问某次历史结果:
pq = self.results.get("sdr_movie")
if pq is not None:
data = pq.get_intermediate_data("gamut", "rgb")
"""
# __getattr__ 代理时,这些方法若在无活跃结果时被调用,返回 None 而不是报错,
# 以兼容"尝试读取但数据还没来"的场景。
_SAFE_GETTERS = frozenset({
"get_intermediate_data",
"has_intermediate_data",
"get_all_intermediate_data",
"get_test_summary",
"get_progress_info",
"to_dict",
})
def __init__(self):
self._results: Dict[str, PQResult] = {}
self._active_type: Optional[str] = None
# ---------- 显式 API ----------
def new(self, test_type: str, test_name: str) -> PQResult:
"""为指定 test_type 创建新的 PQResult并置为当前活跃。
若该 test_type 已有旧结果,会被替换。
"""
result = PQResult(test_type=test_type, test_name=test_name)
self._results[test_type] = result
self._active_type = test_type
return result
def get(self, test_type: str) -> Optional[PQResult]:
"""按 test_type 取结果;不存在则返回 None。"""
return self._results.get(test_type)
def has(self, test_type: str) -> bool:
"""判断某个 test_type 是否有结果。"""
return test_type in self._results
def set_active(self, test_type: str) -> bool:
"""切换当前活跃结果。成功返回 True。"""
if test_type in self._results:
self._active_type = test_type
return True
return False
def clear(self, test_type: Optional[str] = None) -> None:
"""清空结果。不传参数清全部,否则只清指定 test_type。"""
if test_type is None:
self._results.clear()
self._active_type = None
else:
self._results.pop(test_type, None)
if self._active_type == test_type:
self._active_type = None
@property
def current(self) -> Optional[PQResult]:
"""当前活跃的 PQResult无则返回 None。"""
if self._active_type is None:
return None
return self._results.get(self._active_type)
@property
def current_test_type(self) -> Optional[str]:
return self._active_type
@property
def all_types(self) -> List[str]:
return list(self._results.keys())
# ---------- 兼容性:透明代理到 current ----------
def __bool__(self) -> bool:
"""`if self.results:` 仍可用于判断当前是否有活跃结果。"""
return self.current is not None
def __getattr__(self, name: str) -> Any:
# 只有常规属性查找失败时才会走到这里
if name.startswith("_"):
raise AttributeError(name)
current = self.current
if current is not None:
return getattr(current, name)
# 无活跃结果:读类方法返回空值 stub写类方法抛出清晰错误
if name in self._SAFE_GETTERS:
def _null_getter(*_args, **_kwargs):
return None
return _null_getter
raise AttributeError(
f"PQResultStore 当前没有活跃的 PQResult还未调用 new()"
f"无法访问 '{name}'。如需按 test_type 取历史结果,请用 .get(test_type)。"
)
# 使用示例和工具函数 # 使用示例和工具函数
def create_pq_result_from_config(config: Dict[str, Any]) -> PQResult: def create_pq_result_from_config(config: Dict[str, Any]) -> PQResult:
"""根据配置创建PQResult实例""" """根据配置创建PQResult实例"""

View File

@@ -17,7 +17,8 @@ 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):
self.results = PQResult(test_type, test_name) # 通过 PQResultStore 创建/替换指定 test_type 的结果,并设为当前活跃
self.results.new(test_type, test_name)
# 设置配置 # 设置配置
config = { config = {
"test_type": test_type, "test_type": test_type,
@@ -319,7 +320,7 @@ def send_fix_pattern(self, mode):
elif mode == "custom": elif mode == "custom":
self.config.set_current_pattern("custom") self.config.set_current_pattern("custom")
else: else:
self.log_gui.log(f"[Error] 未知的图案模式: {mode}", level="error") self.log_gui.log(f"未知的图案模式: {mode}", level="error")
return None return None
# 2. 获取当前测试类型 # 2. 获取当前测试类型
@@ -364,7 +365,7 @@ def send_fix_pattern(self, mode):
if success: if success:
self.log_gui.log("SDR 信号格式设置成功", level="success") self.log_gui.log("SDR 信号格式设置成功", level="success")
else: else:
self.log_gui.log("[Error] SDR 信号格式设置失败", level="error") self.log_gui.log("SDR 信号格式设置失败", level="error")
# 设置图案参数 # 设置图案参数
if mode == "accuracy": if mode == "accuracy":
@@ -451,7 +452,7 @@ def send_fix_pattern(self, mode):
if success: if success:
self.log_gui.log("HDR 信号格式设置成功", level="success") self.log_gui.log("HDR 信号格式设置成功", level="success")
else: else:
self.log_gui.log("[Error] HDR 信号格式设置失败", level="error") self.log_gui.log("HDR 信号格式设置失败", level="error")
# 设置图案参数 # 设置图案参数
if mode == "accuracy": if mode == "accuracy":
@@ -623,7 +624,7 @@ def send_fix_pattern(self, mode):
return results return results
except Exception as e: except Exception as e:
self.log_gui.log(f"[Error] 发送图案失败: {str(e)}", level="error") self.log_gui.log(f"发送图案失败: {str(e)}", level="error")
import traceback import traceback
self.log_gui.log(traceback.format_exc(), level="error") self.log_gui.log(traceback.format_exc(), level="error")
@@ -1119,7 +1120,7 @@ def test_color_accuracy(self, test_type):
measured_data_list = self.send_fix_pattern("accuracy") measured_data_list = self.send_fix_pattern("accuracy")
if measured_data_list is None or len(measured_data_list) != 29: if measured_data_list is None or len(measured_data_list) != 29:
self.log_gui.log(f"[Error] 数据数量不匹配", level="error") self.log_gui.log(f"数据数量不匹配", level="error")
self.log_gui.log(f" 期望: 29 个", level="info") self.log_gui.log(f" 期望: 29 个", level="info")
self.log_gui.log( self.log_gui.log(
f" 实际: {len(measured_data_list) if measured_data_list else 0}" f" 实际: {len(measured_data_list) if measured_data_list else 0}"

View File

@@ -127,11 +127,11 @@ def start_local_dimming_test(self):
try: try:
image_path = _ensure_window_image(width, height, percentage) image_path = _ensure_window_image(width, height, percentage)
except Exception as e: except Exception as e:
log(f" [Error] 图像生成失败: {e}", level="error") log(f" 图像生成失败: {e}", level="error")
continue continue
if not send_image_pattern(self.ucd, image_path): if not send_image_pattern(self.ucd, image_path):
log(f" [Error] {percentage}% 窗口发送失败,跳过", level="error") log(f" {percentage}% 窗口发送失败,跳过", level="error")
continue continue
log(f"等待 {wait_time} 秒...", level="info") log(f"等待 {wait_time} 秒...", level="info")
@@ -140,10 +140,10 @@ def start_local_dimming_test(self):
try: try:
x, y, lv, _X, _Y, _Z = self.ca.readAllDisplay() x, y, lv, _X, _Y, _Z = self.ca.readAllDisplay()
except Exception as e: except Exception as e:
log(f" [Error] 采集亮度异常: {e}", level="error") log(f" 采集亮度异常: {e}", level="error")
continue continue
if lv is None: if lv is None:
log(f" [Error] {percentage}% 窗口采集失败", level="error") log(f" {percentage}% 窗口采集失败", level="error")
continue continue
log(f"采集亮度: {lv:.2f} cd/m²", level="info") log(f"采集亮度: {lv:.2f} cd/m²", level="info")
@@ -192,12 +192,12 @@ def send_ld_window(self, percentage):
try: try:
image_path = _ensure_window_image(width, height, percentage) image_path = _ensure_window_image(width, height, percentage)
except Exception as e: except Exception as e:
self._dispatch_ui(self.log_gui.log, f"[Error] 图像生成失败: {e}") self._dispatch_ui(self.log_gui.log, f"图像生成失败: {e}")
return return
ok = send_image_pattern(self.ucd, image_path) ok = send_image_pattern(self.ucd, image_path)
msg = ( msg = (
f"{percentage}% 窗口已发送" if ok f"{percentage}% 窗口已发送" if ok
else f"[Error] {percentage}% 窗口发送失败" else f"{percentage}% 窗口发送失败"
) )
self._dispatch_ui(self.log_gui.log, msg) self._dispatch_ui(self.log_gui.log, msg)
@@ -219,10 +219,10 @@ def measure_ld_luminance(self):
try: try:
x, y, lv, _X, _Y, _Z = self.ca.readAllDisplay() x, y, lv, _X, _Y, _Z = self.ca.readAllDisplay()
except Exception as e: except Exception as e:
self._dispatch_ui(self.log_gui.log, f"[Error] 采集异常: {str(e)}") self._dispatch_ui(self.log_gui.log, f"采集异常: {str(e)}")
return return
if lv is None: if lv is None:
self._dispatch_ui(self.log_gui.log, "[Error] 采集失败") self._dispatch_ui(self.log_gui.log, "采集失败")
return return
timestamp = datetime.datetime.now().strftime("%H:%M:%S") timestamp = datetime.datetime.now().strftime("%H:%M:%S")
self._dispatch_ui( self._dispatch_ui(
@@ -277,5 +277,5 @@ def save_local_dimming_results(self):
self.log_gui.log(f"测试结果已保存: {save_path}", level="success") self.log_gui.log(f"测试结果已保存: {save_path}", level="success")
messagebox.showinfo("成功", f"测试结果已保存到:\n{save_path}") messagebox.showinfo("成功", f"测试结果已保存到:\n{save_path}")
except Exception as e: except Exception as e:
self.log_gui.log(f"[Error] 保存失败: {str(e)}", level="error") self.log_gui.log(f"保存失败: {str(e)}", level="error")
messagebox.showerror("错误", f"保存失败: {str(e)}") messagebox.showerror("错误", f"保存失败: {str(e)}")

View File

@@ -801,8 +801,6 @@ def create_result_chart_frame(self):
# 创建单步调试面板实例 # 创建单步调试面板实例
self.debug_panel = PQDebugPanel(self.debug_container, self) self.debug_panel = PQDebugPanel(self.debug_container, self)
self.log_gui.log("单步调试面板已创建(放在测试结果图表下方)", level="success")
def on_chart_tab_changed(self, event): def on_chart_tab_changed(self, event):
"""Tab切换时的事件处理""" """Tab切换时的事件处理"""
try: try:

View File

@@ -94,7 +94,7 @@ def create_cct_params_frame(self):
text="打开调试面板", text="打开调试面板",
command=self.toggle_screen_debug_panel, command=self.toggle_screen_debug_panel,
bootstyle="info-outline", bootstyle="info-outline",
# state=tk.DISABLED, # 初始禁用 state=tk.DISABLED, # 初始禁用
width=15, width=15,
) )
self.screen_debug_btn.grid(row=1, column=3, sticky=tk.W, padx=5, pady=3) self.screen_debug_btn.grid(row=1, column=3, sticky=tk.W, padx=5, pady=3)
@@ -211,7 +211,7 @@ def create_cct_params_frame(self):
text="打开调试面板", text="打开调试面板",
command=self.toggle_sdr_debug_panel, command=self.toggle_sdr_debug_panel,
bootstyle="info-outline", bootstyle="info-outline",
# state=tk.DISABLED, # 初始禁用 state=tk.DISABLED, # 初始禁用
width=15, width=15,
) )
self.sdr_debug_btn.grid(row=1, column=3, sticky=tk.W, padx=5, pady=3) self.sdr_debug_btn.grid(row=1, column=3, sticky=tk.W, padx=5, pady=3)
@@ -328,7 +328,7 @@ def create_cct_params_frame(self):
text="打开调试面板", text="打开调试面板",
command=self.toggle_hdr_debug_panel, command=self.toggle_hdr_debug_panel,
bootstyle="info-outline", bootstyle="info-outline",
# state=tk.DISABLED, # 初始禁用 state=tk.DISABLED, # 初始禁用
width=15, width=15,
) )
self.hdr_debug_btn.grid(row=1, column=3, sticky=tk.W, padx=5, pady=3) self.hdr_debug_btn.grid(row=1, column=3, sticky=tk.W, padx=5, pady=3)
@@ -405,9 +405,9 @@ def _save_cct_params_for(self, test_type):
"""保存指定测试类型的 CCT 参数。""" """保存指定测试类型的 CCT 参数。"""
try: try:
default_params = self.config.get_default_cct_params(test_type) default_params = self.config.get_default_cct_params(test_type)
var_dict = self._get_cct_var_dict(test_type) var_dict = _get_cct_var_dict(test_type)
cct_params = { cct_params = {
key: self._parse_cct_float(var_dict[key], default_params[key]) key: _parse_cct_float(var_dict[key], default_params[key])
for key in default_params for key in default_params
} }
@@ -452,22 +452,22 @@ def _handle_cct_focus_out(self, var, default_value, save_func, label):
def on_sdr_cct_param_focus_out(self, var, default_value): def on_sdr_cct_param_focus_out(self, var, default_value):
"""SDR 色度参数失去焦点时的处理。""" """SDR 色度参数失去焦点时的处理。"""
self._handle_cct_focus_out(var, default_value, self.save_sdr_cct_params, "SDR") _handle_cct_focus_out(var, default_value, self.save_sdr_cct_params, "SDR")
def save_sdr_cct_params(self): def save_sdr_cct_params(self):
"""保存 SDR 色度参数。""" """保存 SDR 色度参数。"""
self._save_cct_params_for("sdr_movie") _save_cct_params_for("sdr_movie")
def on_hdr_cct_param_focus_out(self, var, default_value): def on_hdr_cct_param_focus_out(self, var, default_value):
"""HDR 色度参数失去焦点时的处理。""" """HDR 色度参数失去焦点时的处理。"""
self._handle_cct_focus_out(var, default_value, self.save_hdr_cct_params, "HDR") _handle_cct_focus_out(var, default_value, self.save_hdr_cct_params, "HDR")
def save_hdr_cct_params(self): def save_hdr_cct_params(self):
"""保存 HDR 色度参数。""" """保存 HDR 色度参数。"""
self._save_cct_params_for("hdr_movie") _save_cct_params_for("hdr_movie")
def recalculate_cct(self): def recalculate_cct(self):
@@ -527,7 +527,7 @@ def recalculate_cct(self):
messagebox.showinfo("成功", "色度图已根据新参数重新绘制!") messagebox.showinfo("成功", "色度图已根据新参数重新绘制!")
except Exception as e: except Exception as e:
self.log_gui.log(f"[Error] 重新计算失败: {str(e)}", level="error") self.log_gui.log(f"重新计算失败: {str(e)}", level="error")
self.log_gui.log(traceback.format_exc(), level="error") self.log_gui.log(traceback.format_exc(), level="error")
messagebox.showerror("错误", f"重新计算失败: {str(e)}") messagebox.showerror("错误", f"重新计算失败: {str(e)}")
@@ -675,19 +675,19 @@ def recalculate_gamut(self):
) )
except Exception as e: except Exception as e:
self.log_gui.log(f"[Error] 重新计算失败: {str(e)}", level="error") self.log_gui.log(f"重新计算失败: {str(e)}", level="error")
self.log_gui.log(traceback.format_exc(), level="error") self.log_gui.log(traceback.format_exc(), level="error")
messagebox.showerror("错误", f"重新计算失败: {str(e)}") messagebox.showerror("错误", f"重新计算失败: {str(e)}")
def on_cct_param_focus_out(self, var, default_value): def on_cct_param_focus_out(self, var, default_value):
"""色度参数失去焦点时的处理 - 空值恢复默认""" """色度参数失去焦点时的处理 - 空值恢复默认"""
self._handle_cct_focus_out(var, default_value, self.save_cct_params, "屏模组") _handle_cct_focus_out(var, default_value, self.save_cct_params, "屏模组")
def save_cct_params(self): def save_cct_params(self):
"""保存色度参数 - 简化版""" """保存色度参数 - 简化版"""
self._save_cct_params_for(self.config.current_test_type) _save_cct_params_for(self.config.current_test_type)
def reload_cct_params(self): def reload_cct_params(self):
@@ -743,7 +743,7 @@ def toggle_cct_params_frame(self):
self.hdr_cct_params_frame.pack(fill=tk.X, padx=5, pady=5) self.hdr_cct_params_frame.pack(fill=tk.X, padx=5, pady=5)
else: else:
if hasattr(self, "log_gui"): if hasattr(self, "log_gui"):
self.log_gui.log("[ERROR] HDR 色度参数框尚未创建", level="error") self.log_gui.log("HDR 色度参数框尚未创建", level="error")
# ---- gamut 参考标准改变回调(统一实现) ---- # ---- gamut 参考标准改变回调(统一实现) ----

View File

@@ -232,10 +232,10 @@ def start_custom_row_single_step(self):
children = list(self.custom_result_tree.get_children()) children = list(self.custom_result_tree.get_children())
row_no = children.index(item_id) + 1 if item_id in children else 1 row_no = children.index(item_id) + 1 if item_id in children else 1
self._clear_custom_result_row(item_id, row_no) _clear_custom_result_row(item_id, row_no)
threading.Thread( threading.Thread(
target=self._run_custom_row_single_step, target=_run_custom_row_single_step,
args=(item_id, row_no), args=(item_id, row_no),
daemon=True, daemon=True,
).start() ).start()
@@ -295,7 +295,7 @@ def _run_custom_row_single_step(self, item_id, row_no):
) )
if row_no > len(converted_params): if row_no > len(converted_params):
self.log_gui.log(f"[Error] 行号超出 pattern 范围: {row_no}/{len(converted_params)}", level="error") self.log_gui.log(f"行号超出 pattern 范围: {row_no}/{len(converted_params)}", level="error")
self._dispatch_ui(self.status_var.set, "单步测试失败:行号超范围") self._dispatch_ui(self.status_var.set, "单步测试失败:行号超范围")
return return
@@ -332,14 +332,14 @@ def _run_custom_row_single_step(self, item_id, row_no):
} }
self._dispatch_ui( self._dispatch_ui(
self._update_custom_result_row, item_id, row_no, row_data _update_custom_result_row, item_id, row_no, row_data
) )
self.log_gui.log(f"{row_no} 行单步测试完成并已覆盖", level="success") self.log_gui.log(f"{row_no} 行单步测试完成并已覆盖", level="success")
self._dispatch_ui(self.status_var.set, f"{row_no} 行单步测试完成") self._dispatch_ui(self.status_var.set, f"{row_no} 行单步测试完成")
except Exception as e: except Exception as e:
self.log_gui.log(f"[Error] 单步测试失败: {str(e)}", level="error") self.log_gui.log(f"单步测试失败: {str(e)}", level="error")
self._dispatch_ui(self.status_var.set, "单步测试失败") self._dispatch_ui(self.status_var.set, "单步测试失败")
@@ -425,49 +425,6 @@ def copy_custom_result_table(self):
if hasattr(self, "log_gui"): if hasattr(self, "log_gui"):
self.log_gui.log(f"已复制客户模板表格数据({len(items)} 行)", level="success") self.log_gui.log(f"已复制客户模板表格数据({len(items)} 行)", level="success")
def fill_custom_result_test_data(self):
"""填充 147 行客户模板测试数据(用于界面验证)"""
if not hasattr(self, "custom_result_tree"):
return
self.clear_custom_template_results()
pattern_names = []
if hasattr(self, "config") and hasattr(self.config, "get_temp_pattern_names"):
pattern_names = self.config.get_temp_pattern_names()
total_rows = 147
for i in range(1, total_rows + 1):
ratio = (i - 1) / (total_rows - 1) if total_rows > 1 else 0
row_data = {
"pattern_name": (
pattern_names[i - 1] if i - 1 < len(pattern_names) else f"P {i}"
),
"X": 0.8 + ratio * 120,
"Y": 0.9 + ratio * 135,
"Z": 1.1 + ratio * 145,
"x": 0.24 + ratio * 0.10,
"y": 0.26 + ratio * 0.10,
"Lv": 1.0 + ratio * 500,
"u_prime": 0.16 + ratio * 0.12,
"v_prime": 0.42 + ratio * 0.08,
"Tcp": 1800 + ratio * 12000,
"duv": -0.01 + ratio * 0.03,
"lambda_d": 430 + ratio * 200,
"Pe": 10 + ratio * 90,
}
self.append_custom_template_result(i, row_data)
if hasattr(self, "chart_notebook") and hasattr(self, "custom_template_tab_frame"):
self.chart_notebook.select(self.custom_template_tab_frame)
if hasattr(self, "status_var"):
self.status_var.set("已填充 147 行客户模板测试数据")
if hasattr(self, "log_gui"):
self.log_gui.log("已填充 147 行客户模板测试数据", level="success")
def clear_custom_template_results(self): def clear_custom_template_results(self):
"""清空客户模板结果表格""" """清空客户模板结果表格"""
if not hasattr(self, "custom_result_tree"): if not hasattr(self, "custom_result_tree"):
@@ -615,3 +572,45 @@ def update_custom_button_visibility(self):
else: else:
if self.custom_btn.winfo_manager(): if self.custom_btn.winfo_manager():
self.custom_btn.pack_forget() self.custom_btn.pack_forget()
# def fill_custom_result_test_data(self):
# """填充 147 行客户模板测试数据(用于界面验证)"""
# if not hasattr(self, "custom_result_tree"):
# return
# self.clear_custom_template_results()
# pattern_names = []
# if hasattr(self, "config") and hasattr(self.config, "get_temp_pattern_names"):
# pattern_names = self.config.get_temp_pattern_names()
# total_rows = 147
# for i in range(1, total_rows + 1):
# ratio = (i - 1) / (total_rows - 1) if total_rows > 1 else 0
# row_data = {
# "pattern_name": (
# pattern_names[i - 1] if i - 1 < len(pattern_names) else f"P {i}"
# ),
# "X": 0.8 + ratio * 120,
# "Y": 0.9 + ratio * 135,
# "Z": 1.1 + ratio * 145,
# "x": 0.24 + ratio * 0.10,
# "y": 0.26 + ratio * 0.10,
# "Lv": 1.0 + ratio * 500,
# "u_prime": 0.16 + ratio * 0.12,
# "v_prime": 0.42 + ratio * 0.08,
# "Tcp": 1800 + ratio * 12000,
# "duv": -0.01 + ratio * 0.03,
# "lambda_d": 430 + ratio * 200,
# "Pe": 10 + ratio * 90,
# }
# self.append_custom_template_result(i, row_data)
# if hasattr(self, "chart_notebook") and hasattr(self, "custom_template_tab_frame"):
# self.chart_notebook.select(self.custom_template_tab_frame)
# if hasattr(self, "status_var"):
# self.status_var.set("已填充 147 行客户模板测试数据")
# if hasattr(self, "log_gui"):
# self.log_gui.log("已填充 147 行客户模板测试数据", level="success")

View File

@@ -544,7 +544,7 @@ def on_screen_module_timing_changed(self, event=None):
self.save_pq_config() self.save_pq_config()
except Exception as e: except Exception as e:
self.log_gui.log(f"[Error] 屏模组信号格式更改失败: {str(e)}", level="error") self.log_gui.log(f"屏模组信号格式更改失败: {str(e)}", level="error")
def update_test_items(self): def update_test_items(self):

View File

@@ -1,4 +1,4 @@
"""侧边面板(日志 / Local Dimming / 调试)Step 6 重构)。""" """侧边面板(日志 / Local Dimming / 调试)"""
import traceback import traceback
import tkinter as tk import tkinter as tk
@@ -258,9 +258,11 @@ def _toggle_debug_panel(self, test_type):
try: try:
selected_items = self.get_selected_test_items() selected_items = self.get_selected_test_items()
dlp = cfg["data_log_prefix"] dlp = cfg["data_log_prefix"]
results_obj = getattr(self, "results", None) # 显式按 test_type 拿历史结果,避免依赖"当前活跃"状态
results_store = getattr(self, "results", None)
results_obj = results_store.get(test_type) if results_store is not None else None
if results_obj is None: if results_obj is None:
self.log_gui.log(f"{dlp} 暂无可用测试结果,面板已打开", level="warning") self.log_gui.log(f"{dlp} 暂无 {test_type}测试结果,面板已打开", level="warning")
else: else:
for item_key, debug_key, (cat, sub), data_label, enable_desc in cfg["data_items"]: for item_key, debug_key, (cat, sub), data_label, enable_desc in cfg["data_items"]:
if item_key not in selected_items: if item_key not in selected_items:
@@ -268,11 +270,10 @@ def _toggle_debug_panel(self, test_type):
data = results_obj.get_intermediate_data(cat, sub) data = results_obj.get_intermediate_data(cat, sub)
if not data: if not data:
if test_type == "screen_module" and item_key == "gamma": if test_type == "screen_module" and item_key == "gamma":
self.log_gui.log("[Error] 没有可用的灰阶数据", level="error") self.log_gui.log("没有可用的灰阶数据", level="error")
continue continue
self.log_gui.log(f" → 加载 {len(data)}{data_label}数据点", level="info") self.log_gui.log(f" → 加载 {len(data)}{data_label}数据点", level="info")
debug_panel_instance.enable_debug(test_type, debug_key, data) debug_panel_instance.enable_debug(test_type, debug_key, data)
self.log_gui.log(f"{dlp} {enable_desc}已重新启用", level="success")
except Exception as e: except Exception as e:
self.log_gui.log(f"加载{cfg['failure_data_label']}失败: {str(e)}", level="error") self.log_gui.log(f"加载{cfg['failure_data_label']}失败: {str(e)}", level="error")
self.log_gui.log(traceback.format_exc(), level="error") self.log_gui.log(traceback.format_exc(), level="error")
@@ -282,13 +283,10 @@ def _toggle_debug_panel(self, test_type):
def on_closing(): def on_closing():
btn.config(text="打开调试面板") btn.config(text="打开调试面板")
getattr(self, win_attr).destroy() getattr(self, win_attr).destroy()
self.log_gui.log(f"{wlp}单步调试窗口已关闭", level="success")
win.protocol("WM_DELETE_WINDOW", on_closing) win.protocol("WM_DELETE_WINDOW", on_closing)
win.update_idletasks() win.update_idletasks()
self.log_gui.log(f"{wlp}单步调试面板已打开(独立窗口)", level="success")
def toggle_screen_debug_panel(self): def toggle_screen_debug_panel(self):
_toggle_debug_panel(self, "screen_module") _toggle_debug_panel(self, "screen_module")

View File

@@ -809,7 +809,7 @@ class PQDebugPanel:
self._enable_test_button(test_type, test_item) self._enable_test_button(test_type, test_item)
except Exception as e: except Exception as e:
self.app.log_gui.log(f"[Error] 单步测试失败: {str(e)}", level="error") self.app.log_gui.log(f"单步测试失败: {str(e)}", level="error")
import traceback import traceback
self.app.log_gui.log(traceback.format_exc(), level="error") self.app.log_gui.log(traceback.format_exc(), level="error")
@@ -1146,7 +1146,7 @@ class PQDebugPanel:
# 正确的访问方式:通过 test_items # 正确的访问方式:通过 test_items
if "accuracy" not in self.app.results.test_items: if "accuracy" not in self.app.results.test_items:
self.app.log_gui.log("[Error] 未找到 accuracy 测试项", level="error") self.app.log_gui.log("未找到 accuracy 测试项", level="error")
return 0.0 return 0.0
test_item = self.app.results.test_items["accuracy"] test_item = self.app.results.test_items["accuracy"]
@@ -1155,7 +1155,7 @@ class PQDebugPanel:
self.app.log_gui.log(f" accuracy_result = {accuracy_result is not None}", level="info") self.app.log_gui.log(f" accuracy_result = {accuracy_result is not None}", level="info")
if not accuracy_result: if not accuracy_result:
self.app.log_gui.log("[Error] accuracy_result 为空", level="error") self.app.log_gui.log("accuracy_result 为空", level="error")
return 0.0 return 0.0
# 获取色块名称列表和 ΔE 值列表 # 获取色块名称列表和 ΔE 值列表
@@ -1179,11 +1179,11 @@ class PQDebugPanel:
, level="success") , level="success")
return delta_e return delta_e
except ValueError: except ValueError:
self.app.log_gui.log(f"[Error] 未找到色块 '{color_name}'", level="error") self.app.log_gui.log(f"未找到色块 '{color_name}'", level="error")
self.app.log_gui.log(f" 可用色块: {color_patches}", level="info") self.app.log_gui.log(f" 可用色块: {color_patches}", level="info")
return 0.0 return 0.0
except IndexError: except IndexError:
self.app.log_gui.log(f"[Error] 索引超出范围: {index}/{len(delta_e_values)}", level="error") self.app.log_gui.log(f"索引超出范围: {index}/{len(delta_e_values)}", level="error")
return 0.0 return 0.0
except Exception as e: except Exception as e:

View File

@@ -11,7 +11,7 @@ import matplotlib.pyplot as plt
from app_version import APP_NAME, APP_VERSION, get_app_title from app_version import APP_NAME, APP_VERSION, get_app_title
from drivers.UCD323_Function import UCDController from drivers.UCD323_Function import UCDController
from app.pq.pq_config import PQConfig from app.pq.pq_config import PQConfig
from app.pq.pq_result import PQResult from app.pq.pq_result import PQResult, PQResultStore
from app.views.pq_debug_panel import PQDebugPanel from app.views.pq_debug_panel import PQDebugPanel
from app.export import ( from app.export import (
save_result_images as _save_result_images_impl, save_result_images as _save_result_images_impl,
@@ -113,6 +113,7 @@ class PQAutomationApp:
self.root.title(get_app_title()) self.root.title(get_app_title())
self.root.geometry("900x650") self.root.geometry("900x650")
self.root.minsize(900, 650) self.root.minsize(900, 650)
self.root.state("zoomed")
self.root.protocol("WM_DELETE_WINDOW", self.on_closing) self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
self.app_name = APP_NAME self.app_name = APP_NAME
self.app_version = APP_VERSION self.app_version = APP_VERSION
@@ -137,7 +138,8 @@ class PQAutomationApp:
# 创建配置对象 # 创建配置对象
self.config = PQConfig() self.config = PQConfig()
self.results = None # 结果管理器:按 test_type 保留每次测试结果,始终存在,避免未初始化错误
self.results = PQResultStore()
# 加载上次保存的设置 # 加载上次保存的设置
self.config_file = self.get_config_path() self.config_file = self.get_config_path()
@@ -278,10 +280,6 @@ class PQAutomationApp:
on_test_type_change = _main.on_test_type_change on_test_type_change = _main.on_test_type_change
create_cct_params_frame = _ccp.create_cct_params_frame create_cct_params_frame = _ccp.create_cct_params_frame
_get_cct_var_dict = _ccp._get_cct_var_dict
_parse_cct_float = _ccp._parse_cct_float
_save_cct_params_for = _ccp._save_cct_params_for
_handle_cct_focus_out = _ccp._handle_cct_focus_out
on_sdr_cct_param_focus_out = _ccp.on_sdr_cct_param_focus_out on_sdr_cct_param_focus_out = _ccp.on_sdr_cct_param_focus_out
save_sdr_cct_params = _ccp.save_sdr_cct_params save_sdr_cct_params = _ccp.save_sdr_cct_params
on_hdr_cct_param_focus_out = _ccp.on_hdr_cct_param_focus_out on_hdr_cct_param_focus_out = _ccp.on_hdr_cct_param_focus_out
@@ -309,11 +307,7 @@ class PQAutomationApp:
show_custom_result_context_menu = _ctp.show_custom_result_context_menu show_custom_result_context_menu = _ctp.show_custom_result_context_menu
set_custom_result_table_locked = _ctp.set_custom_result_table_locked set_custom_result_table_locked = _ctp.set_custom_result_table_locked
start_custom_row_single_step = _ctp.start_custom_row_single_step start_custom_row_single_step = _ctp.start_custom_row_single_step
_clear_custom_result_row = _ctp._clear_custom_result_row
_run_custom_row_single_step = _ctp._run_custom_row_single_step
_update_custom_result_row = _ctp._update_custom_result_row
copy_custom_result_table = _ctp.copy_custom_result_table copy_custom_result_table = _ctp.copy_custom_result_table
fill_custom_result_test_data = _ctp.fill_custom_result_test_data
clear_custom_template_results = _ctp.clear_custom_template_results clear_custom_template_results = _ctp.clear_custom_template_results
auto_expand_custom_result_view = _ctp.auto_expand_custom_result_view auto_expand_custom_result_view = _ctp.auto_expand_custom_result_view
append_custom_template_result = _ctp.append_custom_template_result append_custom_template_result = _ctp.append_custom_template_result
@@ -405,13 +399,13 @@ class PQAutomationApp:
self.debug_panel.disable_all_debug() self.debug_panel.disable_all_debug()
self.log_gui.log("单步调试已禁用", level="success") self.log_gui.log("单步调试已禁用", level="success")
except Exception as e: except Exception as e:
self.log_gui.log(f"[Error] 禁用单步调试失败: {str(e)}", level="error") self.log_gui.log(f"禁用单步调试失败: {str(e)}", level="error")
if hasattr(self, "debug_container"): if hasattr(self, "debug_container"):
try: try:
self.debug_container.pack_forget() self.debug_container.pack_forget()
self.log_gui.log("单步调试面板已隐藏", level="success") self.log_gui.log("单步调试面板已隐藏", level="success")
except Exception as e: except Exception as e:
self.log_gui.log(f"[Error] 隐藏调试面板失败: {str(e)}", level="error") self.log_gui.log(f"隐藏调试面板失败: {str(e)}", level="error")
def _set_config_panel_btn_state(self, state): def _set_config_panel_btn_state(self, state):
"""统一设置配置面板按钮状态disabled/normal""" """统一设置配置面板按钮状态disabled/normal"""
@@ -434,7 +428,7 @@ class PQAutomationApp:
"""切换信号格式 Tab 到目标测试类型。""" """切换信号格式 Tab 到目标测试类型。"""
if not hasattr(self, "signal_tabs"): if not hasattr(self, "signal_tabs"):
if hasattr(self, "log_gui"): if hasattr(self, "log_gui"):
self.log_gui.log("[Error] signal_tabs 尚未创建", level="error") self.log_gui.log("signal_tabs 尚未创建", level="error")
return return
try: try:
@@ -484,24 +478,14 @@ class PQAutomationApp:
if gamma_tab_id in current_tabs: if gamma_tab_id in current_tabs:
gamma_index = current_tabs.index(gamma_tab_id) gamma_index = current_tabs.index(gamma_tab_id)
self.chart_notebook.forget(gamma_index) self.chart_notebook.forget(gamma_index)
if hasattr(self, "log_gui"):
self.log_gui.log("已隐藏 Gamma 曲线 Tab", level="success")
if eotf_tab_id not in current_tabs: if eotf_tab_id not in current_tabs:
self.chart_notebook.insert(1, self.eotf_chart_frame, text="EOTF 曲线") self.chart_notebook.insert(1, self.eotf_chart_frame, text="EOTF 曲线")
if hasattr(self, "log_gui"):
self.log_gui.log("已显示 EOTF 曲线 Tab", level="success")
else: else:
if eotf_tab_id in current_tabs: if eotf_tab_id in current_tabs:
eotf_index = current_tabs.index(eotf_tab_id) eotf_index = current_tabs.index(eotf_tab_id)
self.chart_notebook.forget(eotf_index) self.chart_notebook.forget(eotf_index)
if hasattr(self, "log_gui"):
self.log_gui.log("已隐藏 EOTF 曲线 Tab", level="success")
if gamma_tab_id not in current_tabs: if gamma_tab_id not in current_tabs:
self.chart_notebook.insert(1, self.gamma_chart_frame, text="Gamma 曲线") self.chart_notebook.insert(1, self.gamma_chart_frame, text="Gamma 曲线")
if hasattr(self, "log_gui"):
self.log_gui.log("已显示 Gamma 曲线 Tab", level="success")
custom_tab_id = str(self.custom_template_tab_frame) custom_tab_id = str(self.custom_template_tab_frame)
current_tabs = list(self.chart_notebook.tabs()) current_tabs = list(self.chart_notebook.tabs())
@@ -509,13 +493,9 @@ class PQAutomationApp:
if test_type == "sdr_movie": if test_type == "sdr_movie":
if custom_tab_id not in current_tabs: if custom_tab_id not in current_tabs:
self.chart_notebook.add(self.custom_template_tab_frame, text="客户模板结果显示") self.chart_notebook.add(self.custom_template_tab_frame, text="客户模板结果显示")
if hasattr(self, "log_gui"):
self.log_gui.log("已显示客户模板结果 Tab", level="success")
else: else:
if custom_tab_id in current_tabs: if custom_tab_id in current_tabs:
self.chart_notebook.forget(self.custom_template_tab_frame) self.chart_notebook.forget(self.custom_template_tab_frame)
if hasattr(self, "log_gui"):
self.log_gui.log("已隐藏客户模板结果 Tab", level="success")
self.chart_notebook.update_idletasks() self.chart_notebook.update_idletasks()
except Exception as e: except Exception as e:
@@ -655,7 +635,7 @@ class PQAutomationApp:
time.sleep(0.1) time.sleep(0.1)
self.root.update() self.root.update()
if self.test_thread.is_alive(): if self.test_thread.is_alive():
self.log_gui.log("[Error] 测试线程未能正常结束,将在后台继续等待", level="error") self.log_gui.log("测试线程未能正常结束,将在后台继续等待", level="error")
else: else:
self.log_gui.log("测试线程已结束", level="success") self.log_gui.log("测试线程已结束", level="success")
@@ -663,8 +643,8 @@ class PQAutomationApp:
"""清空测试结果对象与中间数据缓存。""" """清空测试结果对象与中间数据缓存。"""
try: try:
self.log_gui.log("清理测试数据...", level="info") self.log_gui.log("清理测试数据...", level="info")
if hasattr(self, "results"): if hasattr(self, "results") and self.results is not None:
self.results = None self.results.clear()
self.log_gui.log(" 测试结果对象已清空", level="success") self.log_gui.log(" 测试结果对象已清空", level="success")
for attr in [ for attr in [
"gamut_results", "gamut_results",
@@ -677,7 +657,7 @@ class PQAutomationApp:
setattr(self, attr, None) setattr(self, attr, None)
self.log_gui.log(" 所有中间数据已清空", level="success") self.log_gui.log(" 所有中间数据已清空", level="success")
except Exception as e: except Exception as e:
self.log_gui.log(f"[Error] 清理数据时出错: {str(e)}", level="error") self.log_gui.log(f"清理数据时出错: {str(e)}", level="error")
def _clear_charts_and_tables(self): def _clear_charts_and_tables(self):
"""清空图表与客户模板结果表格,并跳转到色域图 Tab。""" """清空图表与客户模板结果表格,并跳转到色域图 Tab。"""
@@ -685,18 +665,18 @@ class PQAutomationApp:
self.clear_chart() self.clear_chart()
self.log_gui.log("图表已清空", level="success") self.log_gui.log("图表已清空", level="success")
except Exception as e: except Exception as e:
self.log_gui.log(f"[Error] 清空图表时出错: {str(e)}", level="error") self.log_gui.log(f"清空图表时出错: {str(e)}", level="error")
try: try:
self.clear_custom_template_results() self.clear_custom_template_results()
self.log_gui.log("客户模板结果表格已清空", level="success") self.log_gui.log("客户模板结果表格已清空", level="success")
except Exception as e: except Exception as e:
self.log_gui.log(f"[Error] 清空客户模板结果表格失败: {str(e)}", level="error") self.log_gui.log(f"清空客户模板结果表格失败: {str(e)}", level="error")
try: try:
if hasattr(self, "chart_notebook"): if hasattr(self, "chart_notebook"):
self.chart_notebook.select(self.gamut_chart_frame) self.chart_notebook.select(self.gamut_chart_frame)
self.root.update_idletasks() self.root.update_idletasks()
except Exception as e: except Exception as e:
self.log_gui.log(f"[Error] 跳转到色域图失败: {str(e)}", level="error") self.log_gui.log(f"跳转到色域图失败: {str(e)}", level="error")
def _restore_ui_after_stop(self): def _restore_ui_after_stop(self):
"""恢复主按钮与状态栏到非测试态。""" """恢复主按钮与状态栏到非测试态。"""
@@ -785,7 +765,7 @@ class PQAutomationApp:
messagebox.showinfo("成功", f"测试结果已保存到目录:\n{result_dir}") messagebox.showinfo("成功", f"测试结果已保存到目录:\n{result_dir}")
except Exception as e: except Exception as e:
self.log_gui.log(f"[Error] 保存测试结果失败: {str(e)}", level="error") self.log_gui.log(f"保存测试结果失败: {str(e)}", level="error")
import traceback import traceback
self.log_gui.log(traceback.format_exc(), level="error") self.log_gui.log(traceback.format_exc(), level="error")
@@ -862,7 +842,7 @@ class PQAutomationApp:
self.save_pq_config() self.save_pq_config()
except Exception as e: except Exception as e:
self.log_gui.log(f"[Error] 更新配置失败: {str(e)}", level="error") self.log_gui.log(f"更新配置失败: {str(e)}", level="error")
def update_config_and_tabs(self): def update_config_and_tabs(self):
"""更新配置并同步Tab状态""" """更新配置并同步Tab状态"""

View File

@@ -52,17 +52,11 @@
"timing": "DMT 1920x 1080 @ 60Hz", "timing": "DMT 1920x 1080 @ 60Hz",
"color_format": "RGB", "color_format": "RGB",
"bpc": 8, "bpc": 8,
"colorimetry": "sRGB", "colorimetry": "sRGB"
"cct_params": {
"x_ideal": 0.3127,
"x_tolerance": 0.003,
"y_ideal": 0.329,
"y_tolerance": 0.003
}
} }
}, },
"device_config": { "device_config": {
"ca_com": "COM3", "ca_com": "COM1",
"ucd_list": "0: UCD-323 [2128C209]", "ucd_list": "0: UCD-323 [2128C209]",
"ca_channel": "0" "ca_channel": "0"
}, },