Files
pqAutomationApp/app/runner/test_runner.py
2026-04-21 16:03:11 +08:00

1465 lines
57 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""测试执行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.data_range_converter import convert_pattern_params
from app.pq.pq_result import PQResult
def new_pq_results(self, 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, 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, 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, test_items):
"""执行客户定制 SDR 测试 - 升级版"""
self.log_gui.log("执行客户定制 SDR 测试...", level="info")
# 获取信号格式设置
color_space = self.sdr_color_space_var.get() # BT.709/BT.601/BT.2020
gamma_type = self.sdr_gamma_type_var.get() # 2.2/2.4/2.6
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}, Gamma={gamma_type}", level="info")
self.log_gui.log(f" 数据范围={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, 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
gamma_type = self.sdr_gamma_type_var.get() # 2.2/2.4/2.6
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}, Gamma={gamma_type}", level="info")
self.log_gui.log(f" 数据范围={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, 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, mode):
"""发送固定图案并采集数据 - 支持不同测试类型的信号格式"""
results = []
try:
# 1. 设置图案模式
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 RangeRGB 保持不变", 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 RangeRGB 保持不变", 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")
# 4. 循环发送图案并采集数据(使用原始配置的数量)
total_patterns = len(self.config.current_pattern["pattern_params"])
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")
# 获取颜色名称列表(用于日志显示)
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):
if not self.testing:
self.log_gui.log("测试已停止", level="error")
return results
should_log_detail = (
i == 0
or (i + 1) == total_patterns
or ((i + 1) % progress_step == 0)
)
# 设置下一个图案(显示颜色名称)
if should_log_detail:
if color_names and i < len(color_names):
self.log_gui.log(
f"发送第 {i+1}/{total_patterns} 个图案: {color_names[i]}..."
, level="info")
else:
self.log_gui.log(f"发送第 {i+1}/{total_patterns} 个图案...", level="info")
self.ucd.set_next_pattern()
self.ucd.run()
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": (
custom_pattern_names[i]
if i < len(custom_pattern_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):
"""执行客户定制 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, 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 == "BT.709":
reference_standard = "BT.709"
elif color_space == "BT.601":
reference_standard = "BT.601"
elif color_space == "BT.2020":
reference_standard = "BT.2020"
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.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, 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曲线
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.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, 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.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, 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.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, 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.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, 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 2000Gamma {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 = "优秀", ""
elif delta_e < 5:
grade, icon = "良好", ""
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.log_gui.log("色准测试完成", level="success")
def on_test_completed(self):
"""测试完成后的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")
if "gamut" in selected_items:
try:
if test_type == "screen_module" and hasattr(self, "recalc_gamut_btn"):
self.recalc_gamut_btn.grid()
self.log_gui.log("屏模组色域参考调整按钮已启用", level="success")
elif test_type == "sdr_movie" and hasattr(self, "sdr_recalc_gamut_btn"):
self.sdr_recalc_gamut_btn.grid()
self.log_gui.log("SDR 色域参考调整按钮已启用", level="success")
elif test_type == "hdr_movie" and hasattr(self, "hdr_recalc_gamut_btn"):
self.hdr_recalc_gamut_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):
"""客户模板测试完成后的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):
"""获取当前测试结果"""
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):
"""测试出错后的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("错误", "测试过程中发生错误,请查看日志")