"""测试执行(runner)相关逻辑(Step 5 重构)。 从 pqAutomationApp.PQAutomationApp 中搬迁。每个函数第一行 `self = app` 以保留原有 `self.xxx` 属性访问不变。 """ import datetime import time from tkinter import messagebox import tkinter as tk import colour import numpy as np import algorithm.pq_algorithm as pq_algorithm from app.pq.pq_result import PQResult from typing import TYPE_CHECKING if TYPE_CHECKING: from pqAutomationApp import PQAutomationApp def new_pq_results(self: "PQAutomationApp", test_type, test_name): # 通过 PQResultStore 创建/替换指定 test_type 的结果,并设为当前活跃 self.results.new(test_type, test_name) # 设置配置 config = { "test_type": test_type, "test_name": test_name, "test_items": self.config.current_test_types[test_type]["test_items"], "test_items_chinese": self.config.get_test_item_chinese_names( self.config.current_test_types[test_type]["test_items"] ), } self.results.set_test_config(config) # 添加测试项 for item in config["test_items"]: self.results.add_test_item( item, config["test_items_chinese"][config["test_items"].index(item)] ) def run_test(self: "PQAutomationApp", test_type, test_items): """执行测试""" try: self.log_gui.log(f"开始执行{self.get_test_type_name(test_type)}测试", level="info") self.log_gui.log( f"测试项目: {', '.join(self.config.get_test_item_chinese_names(test_items))}" , level="info") # 根据测试类型执行不同的测试流程 if test_type == "screen_module": self.run_screen_module_test(test_items) elif test_type == "sdr_movie": self.run_sdr_movie_test(test_items) elif test_type == "hdr_movie": self.run_hdr_movie_test(test_items) # 测试完成后更新UI状态 if self.testing: # 如果没有被中途停止 self._dispatch_ui(self.on_test_completed) except Exception as e: self.log_gui.log(f"测试过程中发生错误: {str(e)}", level="info") import traceback self.log_gui.log(traceback.format_exc(), level="error") self._dispatch_ui(self.on_test_error) def run_screen_module_test(self: "PQAutomationApp", test_items): """执行屏模组性能测试 - 优化版""" self.log_gui.log("执行屏模组性能测试...", level="info") if test_items: self.new_pq_results("screen_module", "屏模组性能测试") else: self.log_gui.log("未选择任何测试项目", level="info") return # 判断是否需要灰阶数据 needs_gray_data = any( item in test_items for item in ["gamma", "cct", "contrast"] ) shared_gray_data = None # 共享的灰阶数据 # 计算总测试项数量 total_items = len(test_items) current_item = 0 for item in test_items: if not self.testing: # 检查是否被停止 return current_item += 1 self.status_var.set(f"测试进行中... ({current_item}/{total_items})") # ==================== 色域测试 ==================== if item == "gamut": self.test_gamut("screen_module") # ==================== 灰阶数据采集 ==================== # 如果是第一个需要灰阶数据的测试项,统一采集数据 elif ( item in ["gamma", "cct", "contrast"] and shared_gray_data is None and needs_gray_data ): self.log_gui.log("=" * 50, level="separator") self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)", level="info") self.log_gui.log("=" * 50, level="separator") shared_gray_data = self.send_fix_pattern("gray") if not shared_gray_data or len(shared_gray_data) < 2: self.log_gui.log("灰阶数据采集失败或数据不足,跳过相关测试", level="error") return self.log_gui.log( f"灰阶数据采集完成,共 {len(shared_gray_data)} 个数据点" , level="success") # 保存到 results 对象,供所有灰阶测试使用 self.results.add_intermediate_data("shared", "gray", shared_gray_data) # 执行当前测试项 if item == "gamma": self.test_gamma("screen_module", shared_gray_data) elif item == "cct": self.test_cct("screen_module", shared_gray_data) elif item == "contrast": self.test_contrast("screen_module", shared_gray_data) # ==================== 后续灰阶测试(复用数据) ==================== elif item in ["gamma", "cct", "contrast"] and shared_gray_data is not None: self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试", level="info") if item == "gamma": self.test_gamma("screen_module", shared_gray_data) elif item == "cct": self.test_cct("screen_module", shared_gray_data) elif item == "contrast": self.test_contrast("screen_module", shared_gray_data) def run_custom_sdr_test(self: "PQAutomationApp", test_items): """执行客户定制 SDR 测试 - 升级版""" self.log_gui.log("执行客户定制 SDR 测试...", level="info") # 获取信号格式设置 color_space = self.sdr_color_space_var.get() # BT.709/BT.601/BT.2020 data_range = self.sdr_data_range_var.get() # Full/Limited bit_depth = self.sdr_bit_depth_var.get() # 8bit/10bit/12bit self.log_gui.log(f"信号格式: 色彩空间={color_space}, 数据范围={data_range}, 编码位深={bit_depth}", level="info") self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)", level="info") self.test_custom_sdr() if self.testing: self._dispatch_ui(self.on_custom_template_test_completed) def run_sdr_movie_test(self: "PQAutomationApp", test_items): """执行SDR Movie测试""" self.log_gui.log("执行SDR Movie测试...", level="info") if test_items: self.new_pq_results("sdr_movie", "SDR Movie测试") else: self.log_gui.log("未选择任何测试项目", level="info") return # 获取信号格式设置 color_space = self.sdr_color_space_var.get() # BT.709/BT.601/BT.2020 data_range = self.sdr_data_range_var.get() # Full/Limited bit_depth = self.sdr_bit_depth_var.get() # 8bit/10bit/12bit self.log_gui.log(f"信号格式: 色彩空间={color_space}, 数据范围={data_range}, 编码位深={bit_depth}", level="info") # 判断是否需要灰阶数据 needs_gray_data = any( item in test_items for item in ["gamma", "cct", "contrast"] ) shared_gray_data = None # 计算总测试项数量 total_items = len(test_items) current_item = 0 for item in test_items: if not self.testing: return current_item += 1 self.status_var.set(f"测试进行中... ({current_item}/{total_items})") if item == "gamut": self.test_gamut("sdr_movie") elif ( item in ["gamma", "cct", "contrast"] and shared_gray_data is None and needs_gray_data ): self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)", level="info") shared_gray_data = self.send_fix_pattern("gray") if not shared_gray_data or len(shared_gray_data) < 2: self.log_gui.log("灰阶数据采集失败或数据不足", level="error") return self.results.add_intermediate_data("shared", "gray", shared_gray_data) if item == "gamma": self.test_gamma("sdr_movie", shared_gray_data) elif item == "cct": self.test_cct("sdr_movie", shared_gray_data) elif item == "contrast": self.test_contrast("sdr_movie", shared_gray_data) elif item in ["gamma", "cct", "contrast"] and shared_gray_data is not None: self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试", level="info") if item == "gamma": self.test_gamma("sdr_movie", shared_gray_data) elif item == "cct": self.test_cct("sdr_movie", shared_gray_data) elif item == "contrast": self.test_contrast("sdr_movie", shared_gray_data) elif item == "accuracy": self.test_color_accuracy("sdr_movie") def run_hdr_movie_test(self: "PQAutomationApp", test_items): """执行HDR Movie测试""" self.log_gui.log("执行HDR Movie测试...", level="info") if test_items: self.new_pq_results("hdr_movie", "HDR Movie测试") else: self.log_gui.log("未选择任何测试项目", level="info") return # 获取信号格式设置 color_space = self.hdr_color_space_var.get() max_cll = self.hdr_maxcll_var.get() max_fall = self.hdr_maxfall_var.get() data_range = self.hdr_data_range_var.get() bit_depth = self.hdr_bit_depth_var.get() self.log_gui.log(f"信号格式: 色彩空间={color_space}", level="info") self.log_gui.log(f" MaxCLL={max_cll}, MaxFALL={max_fall}", level="info") self.log_gui.log(f" 数据范围={data_range}, 编码位深={bit_depth}", level="info") # 判断是否需要灰阶数据 needs_gray_data = any( item in test_items for item in ["eotf", "cct", "contrast"] ) shared_gray_data = None # 计算总测试项数量 total_items = len(test_items) current_item = 0 for item in test_items: if not self.testing: return current_item += 1 self.status_var.set(f"测试进行中... ({current_item}/{total_items})") if item == "gamut": self.test_gamut("hdr_movie") elif ( item in ["eotf", "cct", "contrast"] and shared_gray_data is None and needs_gray_data ): self.log_gui.log("开始统一采集灰阶数据(用于 EOTF/CCT/对比度测试)", level="info") shared_gray_data = self.send_fix_pattern("gray") if not shared_gray_data or len(shared_gray_data) < 2: self.log_gui.log("灰阶数据采集失败或数据不足", level="error") return self.results.add_intermediate_data("shared", "gray", shared_gray_data) if item == "eotf": self.test_eotf("hdr_movie", shared_gray_data) elif item == "cct": self.test_cct("hdr_movie", shared_gray_data) elif item == "contrast": self.test_contrast("hdr_movie", shared_gray_data) elif item in ["eotf", "cct", "contrast"] and shared_gray_data is not None: self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试", level="info") if item == "eotf": self.test_eotf("hdr_movie", shared_gray_data) elif item == "cct": self.test_cct("hdr_movie", shared_gray_data) elif item == "contrast": self.test_contrast("hdr_movie", shared_gray_data) elif item == "accuracy": self.test_color_accuracy("hdr_movie") def send_fix_pattern(self: "PQAutomationApp", mode): """发送固定图案并采集数据 - 支持不同测试类型的信号格式""" results = [] try: session = self.pattern_service.prepare_session(mode, log_details=True) self.log_gui.log("=" * 50, level="separator") # 判定信号是否变化(决定 settle 长度)。 # - SDR/HDR:prepare_session 内部已调用 ``apply_signal_format`` → ``set_video_mode``, # 此时 ``format_changed`` 反映本次 vs 上次的差异,可直接读取。 # - screen_module:prepare_session 只 stage 了 color_info/timing,未调用 # ``set_video_mode``,``format_changed`` 仍是上次测试的陈旧值,按"潜在变化"处理。 # 注意:必须在 prime 提交之前读取,否则 prime 的 set_video_mode 会拿当次 # 参数与刚写入的 ``_last_sent_config`` 比对,得到 False,把这个标志覆盖掉。 if mode == "screen_module": format_changed = True else: format_changed = bool( getattr(getattr(self, "ucd", None), "format_changed", True) ) # 预热提交:prepare_session 仅 stage 了新的 color/timing/pattern, # 真正的 ``pg.apply()`` 要到第一次发图时才发生。提前发送首个 pattern, # 让 TV 在 signal_settle 期间就开始重新锁定信号; # 否则前 1~2 个 pattern 会落在 TV 锁定窗口里导致测量错误。 self.pattern_service.send_session_pattern(session, 0) if format_changed: signal_settle = max(1.0, float(getattr(self, "signal_settle_time", 5.0))) self.log_gui.log( f"信号格式已变化,等待电视重新锁定: {signal_settle:.1f}s(可通过 signal_settle_time 调整)", level="info", ) else: signal_settle = 0.5 self.log_gui.log("信号格式未变化,短暂等待: 0.5s", level="info") time.sleep(signal_settle) total_patterns = session.total_patterns self.log_gui.log(f"开始采集数据,共 {total_patterns} 个图案", level="info") settle_time = max(0.2, float(getattr(self, "pattern_settle_time", 1.0))) progress_step = max( 1, int(getattr(self, "pattern_progress_log_step", 5)) ) self.log_gui.log( f"采集等待时间: {settle_time:.2f}s(可通过 pattern_settle_time 调整)" , level="info") display_names = session.display_names for i in range(total_patterns): if not self.testing: self.log_gui.log("测试已停止", level="error") return results should_log_detail = ( total_patterns <= progress_step or i == 0 or (i + 1) == total_patterns or ((i + 1) % progress_step == 0) ) if should_log_detail: if display_names and i < len(display_names): self.log_gui.log( f"发送第 {i+1}/{total_patterns} 个图案: {display_names[i]}..." , level="info") else: self.log_gui.log(f"发送第 {i+1}/{total_patterns} 个图案...", level="info") # 首图已在 prime 阶段发送并经 signal_settle 稳定,无需重发也无需再等 # settle_time;后续 pattern 走正常发图 + 等待。 if i == 0: pass else: self.pattern_service.send_session_pattern(session, i) time.sleep(settle_time) # 测量数据 if mode == "custom": result = [] self.ca.set_Display(1) tcp, duv, lv, X, Y, Z = self.ca.readAllDisplay() if should_log_detail: self.log_gui.log( f" 测量完成: TCP={tcp:.4f}, DUV={duv:.4f}, lv={lv:.2f}, " f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}" , level="success") self.ca.set_Display(8) lambda_, Pe, lv, X, Y, Z = self.ca.readAllDisplay() if should_log_detail: self.log_gui.log( f" 测量完成: λ={lambda_:.4f}, Pe={Pe:.4f}, lv={lv:.2f}, " f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}" , level="success") result = [tcp, duv, lv, lambda_, Pe, lv, X, Y, Z] results.append(result) # 每完成一个 pattern,实时写入客户模板结果表。 try: xy = colour.XYZ_to_xy(np.array([X, Y, Z])) u_prime, v_prime, _ = colour.XYZ_to_CIE1976UCS( np.array([X, Y, Z]) ) row_data = { "pattern_name": ( display_names[i] if i < len(display_names) else f"P {i + 1}" ), "X": X, "Y": Y, "Z": Z, "x": xy[0], "y": xy[1], "Lv": lv, "u_prime": u_prime, "v_prime": v_prime, "Tcp": tcp, "duv": duv, "lambda_d": lambda_, "Pe": Pe, } self.root.after( 0, lambda row_no=i + 1, data=row_data: self.append_custom_template_result( row_no, data ), ) except Exception as e: self.log_gui.log(f"第 {i+1} 行实时结果写入失败: {str(e)}", level="error") else: self.ca.set_xyLv_Display() x, y, lv, X, Y, Z = self.ca.readAllDisplay() results.append([x, y, lv, X, Y, Z]) if should_log_detail: self.log_gui.log( f" 测量完成: x={x:.4f}, y={y:.4f}, lv={lv:.2f}, " f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}" , level="success") self.log_gui.log(f"数据采集完成,共 {len(results)} 组数据", level="success") return results except Exception as e: self.log_gui.log(f"发送图案失败: {str(e)}", level="error") import traceback self.log_gui.log(traceback.format_exc(), level="error") return None def test_custom_sdr(self: "PQAutomationApp"): """执行客户定制 SDR 测试 - 升级版""" self.log_gui.log("执行客户定制 SDR 测试...", level="info") results = self.send_fix_pattern("custom") if not results: self.log_gui.log("客户模板SDR测试被中断", level="info") return self.log_gui.log(f"客户模板采集完成,共 {len(results)} 组数据", level="success") def test_gamut(self: "PQAutomationApp", test_type): """测试色域""" self.log_gui.log("开始测试色域...", level="info") self.results.start_test_item("gamut") try: # 存储测量结果 results = self.send_fix_pattern("rgb") # 检查结果是否为空 if not results: self.log_gui.log("色域测试被中断", level="info") return self.results.add_intermediate_data("gamut", "rgb", results) # 计算色域覆盖率 self.log_gui.log("计算色域覆盖率...", level="info") # 提取 x, y 坐标用于计算色域 xy_points = [[result[0], result[1]] for result in results] # ========== 测试时:使用色彩空间的值作为参考标准 ========== reference_standard = None area = None coverage = None if test_type == "screen_module": # 屏模组测试:固定使用 DCI-P3(因为没有色彩空间设置) reference_standard = "DCI-P3" # 同步更新到色域参考标准变量(供后续重绘使用) self.screen_gamut_ref_var.set(reference_standard) elif test_type == "sdr_movie": # SDR 测试:使用色彩空间设置 color_space = self.sdr_color_space_var.get() if color_space in ("sRGB", "BT.709"): reference_standard = "BT.709" elif color_space == "BT.601": reference_standard = "BT.601" elif color_space == "BT.2020": reference_standard = "BT.2020" elif color_space == "DCI-P3": reference_standard = "DCI-P3" else: reference_standard = "BT.709" self.log_gui.log( f"未识别的色彩空间 '{color_space}',使用默认标准 BT.709" , level="error") # 同步更新到色域参考标准变量 self.sdr_gamut_ref_var.set(reference_standard) elif test_type == "hdr_movie": # HDR 测试:使用色彩空间设置 color_space = self.hdr_color_space_var.get() if color_space == "BT.2020": reference_standard = "BT.2020" elif color_space == "DCI-P3": reference_standard = "DCI-P3" else: reference_standard = "BT.2020" self.log_gui.log( f"未识别的色彩空间 '{color_space}',使用默认标准 BT.2020" , level="error") # 同步更新到色域参考标准变量 self.hdr_gamut_ref_var.set(reference_standard) else: # 未知测试类型,使用 DCI-P3 作为后备 reference_standard = "DCI-P3" self.log_gui.log( f"未识别的测试类型 '{test_type}',使用默认标准 DCI-P3" , level="error") # ========== 根据参考标准计算 XY 覆盖率 ========== if reference_standard == "BT.2020": area, coverage = pq_algorithm.calculate_gamut_coverage_BT2020(xy_points) elif reference_standard == "BT.709": area, coverage = pq_algorithm.calculate_gamut_coverage_BT709(xy_points) elif reference_standard == "DCI-P3": area, coverage = pq_algorithm.calculate_gamut_coverage_DCIP3(xy_points) elif reference_standard == "BT.601": area, coverage = pq_algorithm.calculate_gamut_coverage_BT601(xy_points) else: # 默认使用 DCI-P3 area, coverage = pq_algorithm.calculate_gamut_coverage_DCIP3(xy_points) reference_standard = "DCI-P3" self.log_gui.log( f"未识别的参考标准 '{reference_standard}',使用默认标准 DCI-P3" , level="error") # ========== ✅✅新增:计算 UV 覆盖率 ========== uv_coverage = 0 try: # 将 XY 转换为 UV uv_points = [] for x, y in xy_points: u, v = pq_algorithm.xy_to_uv_1976(x, y) uv_points.append([u, v]) # 根据参考标准计算 UV 覆盖率 if len(uv_points) >= 3: if reference_standard == "BT.2020": _, uv_coverage = ( pq_algorithm.calculate_gamut_coverage_BT2020_uv(uv_points) ) elif reference_standard == "BT.709": _, uv_coverage = pq_algorithm.calculate_gamut_coverage_BT709_uv( uv_points ) elif reference_standard == "DCI-P3": _, uv_coverage = pq_algorithm.calculate_gamut_coverage_DCIP3_uv( uv_points ) elif reference_standard == "BT.601": _, uv_coverage = pq_algorithm.calculate_gamut_coverage_BT601_uv( uv_points ) else: _, uv_coverage = pq_algorithm.calculate_gamut_coverage_DCIP3_uv( uv_points ) self.log_gui.log( f"XY 覆盖率: {coverage:.1f}% | UV 覆盖率: {uv_coverage:.1f}%" , level="success") except: uv_coverage = 0 # ========== 保存结果时包含 XY 和 UV 覆盖率 ========== self.results.set_test_item_result( "gamut", { "area": area, "coverage": coverage, "uv_coverage": uv_coverage, # 新增 UV 覆盖率 "reference": reference_standard, }, ) # 传递完整的 results 用于绘图 self.plot_gamut(results, coverage, test_type) self._save_chart_snapshot(test_type, "gamut", (results, coverage, test_type)) self.log_gui.log("色域测试完成", level="success") except Exception as e: self.log_gui.log(f"色域测试失败: {str(e)}", level="error") import traceback self.log_gui.log(traceback.format_exc(), level="error") raise def test_gamma(self: "PQAutomationApp", test_type, gray_data=None): """测试Gamma曲线 Args: test_type: 测阶数据,如果提供则使用,否则重新采集 """ self.log_gui.log("开始测试Gamma曲线...", level="info") self.results.start_test_item("gamma") try: # 使用传入的灰阶数据或独立采集 if gray_data is not None: self.log_gui.log("使用共享的灰阶数据", level="info") results = gray_data else: self.log_gui.log("独立采集灰阶数据", level="info") results = self.send_fix_pattern("gray") if not results or len(results) < 2: self.log_gui.log("Gamma测试被中断或数据不足", level="info") return self.results.add_intermediate_data("gamma", "gray", results) self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行计算", level="info") self.log_gui.log("计算Gamma值...", level="info") # ========== 修复:正确获取 max_index_fix ========== # 获取配置中的值 config_max_value = self.config.current_pattern.get( "measurement_max_value", 10 ) # 强制转换为整数 try: max_index_fix = int(config_max_value) except (ValueError, TypeError): self.log_gui.log(f"警告: measurement_max_value 转换失败,使用默认值 10", level="warning") max_index_fix = 10 self.log_gui.log(f"配置中的 max_index_fix = {max_index_fix}", level="info") # 关键修复:验证并调整 max_index_fix # max_index_fix 应该是数据点的最大索引(从0开始,所以是 len - 1) actual_max_index = len(results) - 1 if max_index_fix > actual_max_index: self.log_gui.log( f"警告: 配置的 max_index_fix({max_index_fix}) > 实际最大索引({actual_max_index})", level="warning" ) self.log_gui.log(f"自动调整为: {actual_max_index}", level="info") max_index_fix = actual_max_index self.log_gui.log(f"最终使用的 max_index_fix = {max_index_fix}", level="info") # ======================================================== # 获取灰阶 pattern 参数(用于22293 Gamma数据对齐) pattern_params = self.config.default_pattern_gray.get( "pattern_params", None ) # 计算Gamma值(使用修正后的 max_index_fix 和 8bit pattern参数) results_with_gamma_list, L_bar = self.calculate_gamma( results, max_index_fix, pattern_params ) self.results.set_test_item_result( "gamma", {"gamma": results_with_gamma_list, "L_bar": L_bar} ) # 绘制Gamma曲线(SDR 使用用户选择的参考值) if test_type == "sdr_movie": try: target_gamma = float(self.sdr_gamma_type_var.get()) except (ValueError, AttributeError): target_gamma = 2.2 else: target_gamma = 2.2 self.plot_gamma(L_bar, results_with_gamma_list, target_gamma, test_type) self._save_chart_snapshot(test_type, "gamma", (L_bar, results_with_gamma_list, target_gamma, test_type)) self.log_gui.log("Gamma测试完成", level="success") except Exception as e: self.log_gui.log(f"Gamma测试失败: {str(e)}", level="error") import traceback self.log_gui.log(traceback.format_exc(), level="error") raise def test_eotf(self: "PQAutomationApp", test_type, gray_data=None): """测试 EOTF 曲线(HDR 专用) Args: test_type: 测试类型阶数据,如果提供则使用,否则重新采集 """ self.log_gui.log("开始测试 EOTF 曲线(HDR)...", level="info") self.results.start_test_item("eotf") try: # 使用传入的灰阶数据或独立采集 if gray_data is not None: self.log_gui.log("使用共享的灰阶数据", level="info") results = gray_data else: self.log_gui.log("独立采集灰阶数据", level="info") results = self.send_fix_pattern("gray") if not results or len(results) < 2: self.log_gui.log("EOTF 测试被中断或数据不足", level="info") return self.results.add_intermediate_data("eotf", "gray", results) self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行计算", level="info") self.log_gui.log("计算 EOTF 值...", level="info") # ========== 获取 max_index_fix ========== config_max_value = self.config.current_pattern.get( "measurement_max_value", 10 ) try: max_index_fix = int(config_max_value) except (ValueError, TypeError): self.log_gui.log(f"警告: measurement_max_value 转换失败,使用默认值 10", level="warning") max_index_fix = 10 self.log_gui.log(f"配置中的 max_index_fix = {max_index_fix}", level="info") # 验证并调整 max_index_fix actual_max_index = len(results) - 1 if max_index_fix > actual_max_index: self.log_gui.log( f"警告: 配置的 max_index_fix({max_index_fix}) > 实际最大索引({actual_max_index})", level="warning" ) self.log_gui.log(f"自动调整为: {actual_max_index}", level="info") max_index_fix = actual_max_index self.log_gui.log(f"最终使用的 max_index_fix = {max_index_fix}", level="info") # 获取灰阶 pattern 参数(用于22293 Gamma数据对齐) pattern_params = self.config.default_pattern_gray.get( "pattern_params", None ) # ========== 计算 EOTF(复用 Gamma 计算逻辑,使用8bit pattern参数)========== results_with_eotf_list, L_bar = self.calculate_gamma( results, max_index_fix, pattern_params ) # 保存结果 self.results.set_test_item_result( "eotf", {"eotf": results_with_eotf_list, "L_bar": L_bar} ) # ========== 绘制 EOTF 曲线 ========== # HDR 使用 PQ 曲线,目标 gamma 设为 None(不使用传统 gamma) self.plot_eotf(L_bar, results_with_eotf_list, test_type) self._save_chart_snapshot(test_type, "eotf", (L_bar, results_with_eotf_list, test_type)) self.log_gui.log("EOTF 测试完成", level="success") except Exception as e: self.log_gui.log(f"EOTF 测试失败: {str(e)}", level="error") import traceback self.log_gui.log(traceback.format_exc(), level="error") raise def test_cct(self: "PQAutomationApp", test_type, gray_data=None): """测试色度一致性""" self.log_gui.log("开始测试色度一致性...", level="info") self.results.start_test_item("cct") try: if gray_data is not None: self.log_gui.log("使用共享的灰阶数据", level="info") results = gray_data else: self.log_gui.log("独立采集灰阶数据", level="info") results = self.send_fix_pattern("gray") if not results: self.log_gui.log("色度一致性测试被中断", level="info") return self.results.add_intermediate_data("cct", "gray", results) self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行色度计算", level="info") # 提取色度坐标 cct_values = pq_algorithm.calculate_cct_from_results(results) # 保存到结果 self.results.set_test_item_result("cct", {"cct_values": cct_values}) # 绘制图表 self.plot_cct(test_type) self._save_chart_snapshot(test_type, "cct", (test_type,)) self.log_gui.log("色度一致性测试完成", level="success") except Exception as e: self.log_gui.log(f"色度一致性测试失败: {str(e)}", level="error") import traceback self.log_gui.log(traceback.format_exc(), level="error") raise def test_contrast(self: "PQAutomationApp", test_type, gray_data=None): """测试对比度 Args: test_type: 阶数据,如果提供则使用,否则重新采集 """ self.log_gui.log("开始测试对比度...", level="info") self.results.start_test_item("contrast") try: # 优先使用传入的灰阶数据 if gray_data is not None: self.log_gui.log("使用共享的灰阶数据", level="info") results = gray_data else: self.log_gui.log("独立采集灰阶数据", level="info") results = self.send_fix_pattern("gray") if not results: self.log_gui.log("对比度测试被中断", level="info") return self.results.add_intermediate_data("contrast", "gray", results) # 获取最亮和最暗的亮度值 luminance_values = [result[2] for result in results] # 提取lv值 max_luminance = max(luminance_values) # 最大亮度(白) min_luminance = min(luminance_values) # 最小亮度(黑) # 防止除以0 if min_luminance < 0.001: min_luminance = 0.001 # 计算对比度 contrast_ratio = max_luminance / min_luminance # 保存结果 contrast_data = { "max_luminance": max_luminance, "min_luminance": min_luminance, "contrast_ratio": contrast_ratio, "luminance_values": luminance_values, } self.results.set_test_item_result("contrast", contrast_data) # 显示对比度结果到日志 self.log_gui.log(f"最大亮度 (白场): {max_luminance:.2f} cd/m²", level="info") self.log_gui.log(f"最小亮度 (黑场): {min_luminance:.4f} cd/m²", level="info") self.log_gui.log(f"对比度: {contrast_ratio:.0f}:1", level="info") # 绘制对比度图表 self.plot_contrast(contrast_data, test_type) self._save_chart_snapshot(test_type, "contrast", (contrast_data, test_type)) self.log_gui.log("对比度测试完成", level="success") except Exception as e: self.log_gui.log(f"对比度测试失败: {str(e)}", level="error") import traceback self.log_gui.log(traceback.format_exc(), level="error") raise def test_color_accuracy(self: "PQAutomationApp", test_type): """测试色准 - 使用手工实现的 ΔE 2000(应用 Gamma)""" # ========== Gamma 参考值 ========== if test_type == "sdr_movie": try: target_gamma = float(self.sdr_gamma_type_var.get()) except (ValueError, AttributeError): target_gamma = 2.2 self.log_gui.log("=" * 50, level="separator") self.log_gui.log(f"开始测试色准(SDR Movie 标准 - 29色)", level="info") self.log_gui.log(f"使用 Gamma: {target_gamma}", level="success") self.log_gui.log("=" * 50, level="separator") elif test_type == "hdr_movie": target_gamma = 2.4 # HDR 使用 PQ,但保留参考值 self.log_gui.log("=" * 50, level="separator") self.log_gui.log(f"开始测试色准(HDR Movie 标准 - 29色)", level="info") self.log_gui.log(f"使用 Gamma: PQ (参考γ={target_gamma})", level="success") # ← 新增 self.log_gui.log("=" * 50, level="separator") else: # screen_module target_gamma = 2.2 self.log_gui.log("=" * 50, level="separator") self.log_gui.log(f"开始测试色准(屏模组 标准 - 29色)", level="info") self.log_gui.log(f"使用 Gamma: {target_gamma}", level="success") self.log_gui.log("=" * 50, level="separator") # 获取 29色名称 color_names = self.config.get_accuracy_color_names() self.log_gui.log(f"将测试 {len(color_names)} 个色块", level="success") self.log_gui.log(f" 色块分组:", level="info") self.log_gui.log(f" 灰阶 (5个): {', '.join(color_names[:5])}", level="info") self.log_gui.log(f" ColorChecker (18个): {', '.join(color_names[5:23])}", level="info") self.log_gui.log(f" 饱和色 (6个): {', '.join(color_names[23:])}", level="info") self.log_gui.log("=" * 50, level="separator") self.log_gui.log("开始发送色准图案并采集数据...", level="info") self.log_gui.log("=" * 50, level="separator") # 发送 29色图案 measured_data_list = self.send_fix_pattern("accuracy") if measured_data_list is None or len(measured_data_list) != 29: self.log_gui.log(f"数据数量不匹配", level="error") self.log_gui.log(f" 期望: 29 个", level="info") self.log_gui.log( f" 实际: {len(measured_data_list) if measured_data_list else 0} 个" , level="info") return # 保存原始测量数据供单步调试使用 self.results.add_intermediate_data("accuracy", "measured", measured_data_list) # ========== 计算 ΔE 2000(显示 Gamma)========== self.log_gui.log("=" * 50, level="separator") self.log_gui.log(f"计算色准(ΔE 2000,Gamma {target_gamma})...", level="info") self.log_gui.log("=" * 50, level="separator") # 获取标准 xy 坐标 standards = self.get_accuracy_color_standards(test_type) delta_e_values = [] color_patches = [] for i, (name, measured_data) in enumerate(zip(color_names, measured_data_list)): measured_x = measured_data[0] measured_y = measured_data[1] measured_lv = measured_data[2] standard_x, standard_y = standards.get(name, (0.3127, 0.3290)) delta_e = self.calculate_delta_e_2000( measured_x, measured_y, measured_lv, standard_x, standard_y, ) delta_e_values.append(delta_e) color_patches.append(name) if delta_e < 3: grade, icon = "优秀", "OK" elif delta_e < 5: grade, icon = "良好", "WARN" else: grade, icon = "偏差", "[Error]" self.log_gui.log( f" [{i+1:2d}] {name:20s} ΔE={delta_e:5.2f} {icon} {grade}" , level="info") # ========== 统计 ========== avg_delta_e_all = sum(delta_e_values) / len(delta_e_values) max_delta_e_all = max(delta_e_values) min_delta_e_all = min(delta_e_values) excellent_count_all = sum(1 for de in delta_e_values if de < 3) good_count_all = sum(1 for de in delta_e_values if 3 <= de < 5) poor_count_all = sum(1 for de in delta_e_values if de >= 5) delta_e_gray = delta_e_values[0:5] avg_delta_e_gray = sum(delta_e_gray) / len(delta_e_gray) delta_e_colorchecker = delta_e_values[5:23] avg_delta_e_colorchecker = sum(delta_e_colorchecker) / len(delta_e_colorchecker) delta_e_saturated = delta_e_values[23:29] avg_delta_e_saturated = sum(delta_e_saturated) / len(delta_e_saturated) self.log_gui.log("=" * 50, level="separator") self.log_gui.log("色准统计(全 29色):", level="info") self.log_gui.log("=" * 50, level="separator") self.log_gui.log(f" 平均 ΔE: {avg_delta_e_all:.2f}", level="info") self.log_gui.log(f" 最大 ΔE: {max_delta_e_all:.2f}", level="info") self.log_gui.log(f" 最小 ΔE: {min_delta_e_all:.2f}", level="info") self.log_gui.log(f" 优秀 (ΔE<3): {excellent_count_all} 个", level="info") self.log_gui.log(f" 良好 (3≤ΔE<5): {good_count_all} 个", level="info") self.log_gui.log(f" 偏差 (ΔE≥5): {poor_count_all} 个", level="info") self.log_gui.log("", level="blank") self.log_gui.log("分组统计:", level="info") self.log_gui.log(f" 灰阶 (5个): 平均 ΔE = {avg_delta_e_gray:.2f}", level="info") self.log_gui.log( f" ColorChecker (18个): 平均 ΔE = {avg_delta_e_colorchecker:.2f}" , level="info") self.log_gui.log(f" 饱和色 (6个): 平均 ΔE = {avg_delta_e_saturated:.2f}", level="info") # ========== 保存测试结果 ========== accuracy_data = { "color_patches": color_patches, "delta_e_values": delta_e_values, "color_measurements": measured_data_list, "avg_delta_e": avg_delta_e_all, "max_delta_e": max_delta_e_all, "min_delta_e": min_delta_e_all, "excellent_count": excellent_count_all, "good_count": good_count_all, "poor_count": poor_count_all, "avg_delta_e_gray": avg_delta_e_gray, "avg_delta_e_colorchecker": avg_delta_e_colorchecker, "avg_delta_e_saturated": avg_delta_e_saturated, "target_gamma": target_gamma, } self.results.set_test_item_result("accuracy", accuracy_data) # ========== 绘制图表 ========== self.plot_accuracy(accuracy_data, test_type) self._save_chart_snapshot(test_type, "accuracy", (accuracy_data, test_type)) self.log_gui.log("色准测试完成", level="success") def run_simulation_test(self: "PQAutomationApp"): """运行模拟测试(无需 UCD/CA),直接在 UI 展示结果。""" try: test_type = self.config.current_test_type selected_items = self.get_selected_test_items() if not selected_items: self.log_gui.log("未选择测试项目,无法执行模拟测试", level="error") messagebox.showwarning("提示", "请先勾选至少一个测试项目") return self.log_gui.log("=" * 60, level="separator") self.log_gui.log("开始执行模拟测试(无需 UCD/CA 设备)", level="info") self.log_gui.log(f"测试类型: {self.get_test_type_name(test_type)}", level="info") self.log_gui.log( f"测试项目: {', '.join(self.config.get_test_item_chinese_names(selected_items))}", level="info", ) if hasattr(self, "update_chart_tabs_state"): self.update_chart_tabs_state() if hasattr(self, "clear_chart"): self.clear_chart() self.new_pq_results(test_type, f"{self.get_test_type_name(test_type)} 模拟测试") self.status_var.set("模拟测试进行中...") rng = np.random.default_rng() def _read_ideal_xy(): try: if test_type == "sdr_movie": return float(self.sdr_cct_x_ideal_var.get()), float(self.sdr_cct_y_ideal_var.get()) if test_type == "hdr_movie": return float(self.hdr_cct_x_ideal_var.get()), float(self.hdr_cct_y_ideal_var.get()) return float(self.cct_x_ideal_var.get()), float(self.cct_y_ideal_var.get()) except Exception: return 0.3127, 0.3290 def _xyY_to_xyz_row(x, y, lv): if y <= 1e-8: return [x, y, lv, 0.0, lv, 0.0] X = x * lv / y Z = (1 - x - y) * lv / y return [x, y, lv, X, lv, Z] # 共享灰阶数据:用于 Gamma/EOTF/CCT/对比度 gray_results = [] x_ideal, y_ideal = _read_ideal_xy() peak_lv = 900.0 if test_type == "hdr_movie" else 220.0 gamma_shape = 2.25 if test_type == "hdr_movie" else 2.20 for i in range(11): p = i / 10.0 lv = 0.08 + peak_lv * (p ** gamma_shape) lv *= 1.0 + float(rng.normal(0.0, 0.015)) lv = max(lv, 0.03) x = x_ideal + float(rng.normal(0.0, 0.0012)) y = y_ideal + float(rng.normal(0.0, 0.0012)) gray_results.append(_xyY_to_xyz_row(x, y, lv)) if any(item in selected_items for item in ("gamma", "eotf", "cct", "contrast")): self.results.add_intermediate_data("shared", "gray", gray_results) # 色域模拟 if "gamut" in selected_items: ref_map = { "BT.709": [(0.6400, 0.3300), (0.3000, 0.6000), (0.1500, 0.0600)], "DCI-P3": [(0.6800, 0.3200), (0.2650, 0.6900), (0.1500, 0.0600)], "BT.2020": [(0.7080, 0.2920), (0.1700, 0.7970), (0.1310, 0.0460)], "BT.601": [(0.6300, 0.3400), (0.3100, 0.5950), (0.1550, 0.0700)], } if test_type == "hdr_movie": reference = self.hdr_gamut_ref_var.get() if hasattr(self, "hdr_gamut_ref_var") else "BT.2020" elif test_type == "sdr_movie": reference = self.sdr_gamut_ref_var.get() if hasattr(self, "sdr_gamut_ref_var") else "BT.709" else: reference = self.screen_gamut_ref_var.get() if hasattr(self, "screen_gamut_ref_var") else "DCI-P3" if reference not in ref_map: reference = "DCI-P3" gamut_results = [] for rx, ry in ref_map[reference]: mx = rx + float(rng.normal(0.0, 0.006)) my = ry + float(rng.normal(0.0, 0.006)) gamut_results.append(_xyY_to_xyz_row(mx, my, 120.0)) self.results.add_intermediate_data("gamut", "rgb", gamut_results) self.results.set_test_item_result( "gamut", { "area": 0.0, "coverage": 95.0, "uv_coverage": 93.0, "reference": reference, }, ) self.plot_gamut(gamut_results, 95.0, test_type) # Gamma / EOTF 模拟 if "gamma" in selected_items and test_type != "hdr_movie": pattern_params = self.config.default_pattern_gray.get("pattern_params", None) results_with_gamma, L_bar = self.calculate_gamma( gray_results, len(gray_results) - 1, pattern_params ) self.results.set_test_item_result("gamma", {"gamma": results_with_gamma, "L_bar": L_bar}) try: target_gamma = float(self.sdr_gamma_type_var.get()) if test_type == "sdr_movie" else 2.2 except Exception: target_gamma = 2.2 self.plot_gamma(L_bar, results_with_gamma, target_gamma, test_type) if "eotf" in selected_items and test_type == "hdr_movie": pattern_params = self.config.default_pattern_gray.get("pattern_params", None) results_with_eotf, L_bar = self.calculate_gamma( gray_results, len(gray_results) - 1, pattern_params ) self.results.set_test_item_result("eotf", {"eotf": results_with_eotf, "L_bar": L_bar}) self.plot_eotf(L_bar, results_with_eotf, test_type) # CCT 模拟 if "cct" in selected_items: cct_values = pq_algorithm.calculate_cct_from_results(gray_results) self.results.set_test_item_result("cct", {"cct_values": cct_values}) self.plot_cct(test_type) # 对比度模拟 if "contrast" in selected_items: luminance_values = [row[2] for row in gray_results] max_luminance = max(luminance_values) min_luminance = max(min(luminance_values), 0.001) contrast_ratio = max_luminance / min_luminance contrast_data = { "max_luminance": max_luminance, "min_luminance": min_luminance, "contrast_ratio": contrast_ratio, "luminance_values": luminance_values, } self.results.set_test_item_result("contrast", contrast_data) self.plot_contrast(contrast_data, test_type) # 色准模拟 if "accuracy" in selected_items: color_names = self.config.get_accuracy_color_names() standards = self.get_accuracy_color_standards(test_type) color_patches = [] measured_data = [] delta_e_values = [] for idx, name in enumerate(color_names): sx, sy = standards.get(name, (0.3127, 0.3290)) # 前 20 个色块偏差更小,后 9 个稍大,方便 UI 看出差异 noise_sigma = 0.0008 if idx < 20 else 0.0018 mx = sx + float(rng.normal(0.0, noise_sigma)) my = sy + float(rng.normal(0.0, noise_sigma)) lv = max(5.0, 40.0 + idx * 2.3 + float(rng.normal(0.0, 4.0))) row = _xyY_to_xyz_row(mx, my, lv) measured_data.append(row) color_patches.append(name) delta_e = self.calculate_delta_e_2000(mx, my, lv, sx, sy) delta_e_values.append(delta_e) avg_delta_e = float(np.mean(delta_e_values)) if delta_e_values else 0.0 max_delta_e = float(np.max(delta_e_values)) if delta_e_values else 0.0 min_delta_e = float(np.min(delta_e_values)) if delta_e_values else 0.0 excellent_count = sum(1 for d in delta_e_values if d < 3) good_count = sum(1 for d in delta_e_values if 3 <= d < 5) poor_count = sum(1 for d in delta_e_values if d >= 5) delta_e_gray = delta_e_values[0:5] delta_e_colorchecker = delta_e_values[5:23] delta_e_saturated = delta_e_values[23:29] try: target_gamma = float(self.sdr_gamma_type_var.get()) if test_type == "sdr_movie" else 2.2 except Exception: target_gamma = 2.2 accuracy_data = { "color_patches": color_patches, "delta_e_values": delta_e_values, "color_measurements": measured_data, "avg_delta_e": avg_delta_e, "max_delta_e": max_delta_e, "min_delta_e": min_delta_e, "excellent_count": excellent_count, "good_count": good_count, "poor_count": poor_count, "avg_delta_e_gray": float(np.mean(delta_e_gray)) if delta_e_gray else 0.0, "avg_delta_e_colorchecker": float(np.mean(delta_e_colorchecker)) if delta_e_colorchecker else 0.0, "avg_delta_e_saturated": float(np.mean(delta_e_saturated)) if delta_e_saturated else 0.0, "target_gamma": target_gamma, } self.results.add_intermediate_data("accuracy", "measured", measured_data) self.results.set_test_item_result("accuracy", accuracy_data) self.plot_accuracy(accuracy_data, test_type) self.save_btn.config(state=tk.NORMAL) self.status_var.set("模拟测试完成") self.log_gui.log("模拟测试完成,结果已显示到 UI", level="success") self.log_gui.log("=" * 60, level="separator") messagebox.showinfo("完成", "模拟测试已完成(无需 UCD/CA)") except Exception as e: self.status_var.set("模拟测试失败") self.log_gui.log(f"模拟测试失败: {str(e)}", level="error") import traceback self.log_gui.log(traceback.format_exc(), level="error") messagebox.showerror("错误", f"模拟测试失败: {str(e)}") def on_test_completed(self: "PQAutomationApp"): """测试完成后的UI更新""" self.testing = False self.start_btn.config(state=tk.NORMAL) self.stop_btn.config(state=tk.DISABLED) self.save_btn.config(state=tk.NORMAL) self.clear_config_btn.config(state=tk.NORMAL) self.status_var.set("测试完成") self.log_gui.log("测试完成", level="success") # 恢复配置项按钮 if hasattr(self, "config_panel_frame"): try: self.config_panel_frame.btn.configure(state="normal") except: pass # 启用色域参考标准下拉框 try: test_type = self.config.current_test_type if test_type == "screen_module" and hasattr(self, "screen_gamut_combo"): self.screen_gamut_combo.configure(state="readonly") self.log_gui.log("屏模组色域参考标准已启用", level="success") elif test_type == "sdr_movie" and hasattr(self, "sdr_gamut_combo"): self.sdr_gamut_combo.configure(state="readonly") self.log_gui.log("SDR 色域参考标准已启用", level="success") elif test_type == "hdr_movie" and hasattr(self, "hdr_gamut_combo"): self.hdr_gamut_combo.configure(state="readonly") self.log_gui.log("HDR 色域参考标准已启用", level="success") except Exception as e: self.log_gui.log(f"启用色域参考标准失败: {str(e)}", level="error") # 获取当前测试类型和选中的测试项 selected_items = self.get_selected_test_items() test_type = self.config.current_test_type # ==================== 启用单步调试按钮 ==================== if hasattr(self, "debug_panel"): try: # 屏模组:启用 Gamma 和 RGB 单步调试 if test_type == "screen_module": if "gamma" in selected_items: gray_data = self.results.get_intermediate_data("shared", "gray") if gray_data: self.debug_panel.enable_debug( "screen_module", "gamma", gray_data ) # 启用 RGB 单步调试(色域测试完成后) if "gamut" in selected_items: rgb_data = self.results.get_intermediate_data("gamut", "rgb") if rgb_data: self.debug_panel.enable_debug( "screen_module", "rgb", rgb_data ) # 启用单步调试按钮 if hasattr(self, "screen_debug_btn"): self.screen_debug_btn.config(state=tk.NORMAL) self.log_gui.log("屏模组单步调试按钮已启用", level="success") # SDR:启用 Gamma、色准和 RGB 单步调试 elif test_type == "sdr_movie": if "gamma" in selected_items: gray_data = self.results.get_intermediate_data("shared", "gray") if gray_data: self.debug_panel.enable_debug( "sdr_movie", "gamma", gray_data ) if "accuracy" in selected_items: accuracy_data = self.results.get_intermediate_data( "accuracy", "measured" ) if accuracy_data: self.debug_panel.enable_debug( "sdr_movie", "accuracy", accuracy_data ) # 启用 RGB 单步调试(色域测试完成后) if "gamut" in selected_items: rgb_data = self.results.get_intermediate_data("gamut", "rgb") if rgb_data: self.debug_panel.enable_debug("sdr_movie", "rgb", rgb_data) # 启用单步调试按钮 if hasattr(self, "sdr_debug_btn"): self.sdr_debug_btn.config(state=tk.NORMAL) self.log_gui.log("SDR 单步调试按钮已启用", level="success") # HDR:启用 EOTF、色准和 RGB 单步调试 elif test_type == "hdr_movie": if "eotf" in selected_items: gray_data = self.results.get_intermediate_data("shared", "gray") if gray_data: self.debug_panel.enable_debug( "hdr_movie", "eotf", gray_data ) if "accuracy" in selected_items: accuracy_data = self.results.get_intermediate_data( "accuracy", "measured" ) if accuracy_data: self.debug_panel.enable_debug( "hdr_movie", "accuracy", accuracy_data ) # 启用 RGB 单步调试(色域测试完成后) if "gamut" in selected_items: rgb_data = self.results.get_intermediate_data("gamut", "rgb") if rgb_data: self.debug_panel.enable_debug("hdr_movie", "rgb", rgb_data) # 启用单步调试按钮 if hasattr(self, "hdr_debug_btn"): self.hdr_debug_btn.config(state=tk.NORMAL) self.log_gui.log("HDR 单步调试按钮已启用", level="success") except Exception as e: self.log_gui.log(f"启用单步调试失败: {str(e)}", level="error") # ==================== 显示色度/色域重新计算按钮 ==================== if "cct" in selected_items: try: if test_type == "screen_module" and hasattr(self, "recalc_cct_btn"): self.recalc_cct_btn.grid() self.log_gui.log("屏模组色度参数调整按钮已启用", level="success") elif test_type == "sdr_movie" and hasattr(self, "sdr_recalc_cct_btn"): self.sdr_recalc_cct_btn.grid() self.log_gui.log("SDR 色度参数调整按钮已启用", level="success") elif test_type == "hdr_movie" and hasattr(self, "hdr_recalc_cct_btn"): self.hdr_recalc_cct_btn.grid() self.log_gui.log("HDR 色度参数调整按钮已启用", level="success") except Exception as e: self.log_gui.log(f"显示色度重新计算按钮失败: {str(e)}", level="error") messagebox.showinfo("完成", "测试已完成!") def on_custom_template_test_completed(self: "PQAutomationApp"): """客户模板测试完成后的UI更新""" self.testing = False self.set_custom_result_table_locked(False) self.start_btn.config(state=tk.NORMAL) self.stop_btn.config(state=tk.DISABLED) self.save_btn.config(state=tk.DISABLED) self.clear_config_btn.config(state=tk.NORMAL) self.custom_btn.config(state=tk.NORMAL) self.status_var.set("客户模板测试完成") if hasattr(self, "config_panel_frame"): try: self.config_panel_frame.btn.configure(state="normal") except: pass self.log_gui.log("客户模板测试完成", level="success") messagebox.showinfo("完成", "客户模板测试已完成!") def get_current_test_result(self: "PQAutomationApp"): """获取当前测试结果""" test_type = self.test_type_var.get() test_items = self.get_selected_test_items() # 构建测试结果字典 result = { "test_type": test_type, "test_type_name": self.get_test_type_name(test_type), "test_items": test_items, "test_items_names": self.config.get_test_item_chinese_names(test_items), "timestamp": datetime.datetime.now(), "status": "完成", "results": {}, } # 根据测试项目收集结果数据 for item in test_items: if item == "gamut" and hasattr(self, "gamut_results"): result["results"]["gamut"] = getattr(self, "gamut_results", {}) elif item in ["gamma", "eotf"] and hasattr(self, "gamma_results"): result["results"][item] = getattr(self, "gamma_results", {}) elif item == "cct" and hasattr(self, "cct_results"): result["results"]["cct"] = getattr(self, "cct_results", {}) elif item == "contrast" and hasattr(self, "contrast_results"): result["results"]["contrast"] = getattr(self, "contrast_results", {}) elif item == "accuracy" and hasattr(self, "accuracy_results"): result["results"]["accuracy"] = getattr(self, "accuracy_results", {}) return result def on_test_error(self: "PQAutomationApp"): """测试出错后的UI更新""" self.testing = False self.set_custom_result_table_locked(False) self.start_btn.config(state=tk.NORMAL) self.stop_btn.config(state=tk.DISABLED) self.clear_config_btn.config(state=tk.NORMAL) if hasattr(self, "custom_btn"): self.custom_btn.config(state=tk.NORMAL) self.status_var.set("测试出错") # 恢复配置项按钮 if hasattr(self, "config_panel_frame"): try: self.config_panel_frame.btn.configure(state="normal") except: pass messagebox.showerror("错误", "测试过程中发生错误,请查看日志") class TestRunnerMixin: """由 tools/refactor_to_mixins.py 自动生成。 把本模块的自由函数挂到 PQAutomationApp 上,便于 F12 跳转与类型推断。 """ new_pq_results = new_pq_results run_test = run_test run_screen_module_test = run_screen_module_test run_custom_sdr_test = run_custom_sdr_test run_sdr_movie_test = run_sdr_movie_test run_hdr_movie_test = run_hdr_movie_test send_fix_pattern = send_fix_pattern test_custom_sdr = test_custom_sdr test_gamut = test_gamut test_gamma = test_gamma test_eotf = test_eotf test_cct = test_cct test_contrast = test_contrast test_color_accuracy = test_color_accuracy run_simulation_test = run_simulation_test on_test_completed = on_test_completed on_custom_template_test_completed = on_custom_template_test_completed get_current_test_result = get_current_test_result on_test_error = on_test_error