From 9c15751dd07746748ce862437c63997717791d4a Mon Sep 17 00:00:00 2001 From: "xinzhu.yin" Date: Tue, 16 Jun 2026 15:56:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=86=20pattern=20=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E5=90=8E=E7=9A=84=E7=AD=89=E5=BE=85=E6=97=B6=E9=97=B4=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E6=B7=BB=E5=8A=A0=E5=88=B0pq=5Fconfig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/config_io.py | 12 ++++ app/pq/pq_config.py | 18 ++++++ app/runner/test_runner.py | 3 +- app/views/pq_debug_panel.py | 118 ++++++++++++++++++++++++++++++------ pqAutomationApp.py | 6 +- 5 files changed, 132 insertions(+), 25 deletions(-) diff --git a/app/config_io.py b/app/config_io.py index 5fa0341..666b8b2 100644 --- a/app/config_io.py +++ b/app/config_io.py @@ -36,6 +36,15 @@ def get_config_path(self: "PQAutomationApp"): return config_file +def _apply_acquisition_timing(self: "PQAutomationApp"): + """将 PQConfig 中的采集节奏同步到 app 运行时属性。""" + timing = getattr(self.config, "acquisition_timing", {}) + self.pattern_settle_time = max(0.2, float(timing.get("pattern_settle_time", 0.4))) + self.pattern_progress_log_step = max( + 1, int(timing.get("pattern_progress_log_step", 5)) + ) + + def load_pq_config(self: "PQAutomationApp"): """加载PQ配置(兼容打包后的程序)""" try: @@ -44,12 +53,15 @@ def load_pq_config(self: "PQAutomationApp"): with open(self.config_file, "r", encoding="utf-8") as f: config_dict = json.load(f) self.config.from_dict(config_dict) + _apply_acquisition_timing(self) if hasattr(self, "log_gui"): self.log_gui.log("配置文件加载成功", level="success") else: + _apply_acquisition_timing(self) if hasattr(self, "log_gui"): self.log_gui.log("配置文件不存在,使用默认配置", level="error") except Exception as e: + _apply_acquisition_timing(self) if hasattr(self, "log_gui"): self.log_gui.log(f"加载配置文件失败: {str(e)},使用默认配置", level="error") diff --git a/app/pq/pq_config.py b/app/pq/pq_config.py index e4eaa7b..0f1ecd4 100644 --- a/app/pq/pq_config.py +++ b/app/pq/pq_config.py @@ -655,6 +655,12 @@ class PQConfig: "ca_channel": "0", } + # ---- 采集节奏(settings/pq_config.json → acquisition_timing)---- + self.acquisition_timing = { + "pattern_settle_time": 0.4, + "pattern_progress_log_step": 5, + } + # ---- 自定义图案(用户可变)---- self.custom_pattern = { "pattern_mode": "SolidColor", @@ -710,6 +716,7 @@ class PQConfig: "test_types": self.current_test_types, "device_config": self.device_config, "custom_pattern": self.custom_pattern, + "acquisition_timing": self.acquisition_timing, } def from_dict(self, config_dict): @@ -728,6 +735,17 @@ class PQConfig: self.device_config = config_dict.get("device_config", self.device_config) self.custom_pattern = config_dict.get("custom_pattern", self.custom_pattern) + loaded_timing = config_dict.get("acquisition_timing") + if isinstance(loaded_timing, dict): + if "pattern_settle_time" in loaded_timing: + self.acquisition_timing["pattern_settle_time"] = max( + 0.2, float(loaded_timing["pattern_settle_time"]) + ) + if "pattern_progress_log_step" in loaded_timing: + self.acquisition_timing["pattern_progress_log_step"] = max( + 1, int(loaded_timing["pattern_progress_log_step"]) + ) + def save_to_file(self, filename): """将配置保存到文件""" with open(filename, "w", encoding="utf-8") as f: diff --git a/app/runner/test_runner.py b/app/runner/test_runner.py index 408a0c2..6cf2891 100644 --- a/app/runner/test_runner.py +++ b/app/runner/test_runner.py @@ -356,7 +356,8 @@ def send_fix_pattern(self: "PQAutomationApp", mode): 1, int(getattr(self, "pattern_progress_log_step", 5)) ) self.log_gui.log( - f"采集等待时间: {settle_time:.2f}s(可通过 pattern_settle_time 调整)" + f"采集等待时间: {settle_time:.2f}s" + f"(可在 settings/pq_config.json 的 acquisition_timing.pattern_settle_time 调整)" , level="info") display_names = session.display_names diff --git a/app/views/pq_debug_panel.py b/app/views/pq_debug_panel.py index 2f7b86a..5cc947b 100644 --- a/app/views/pq_debug_panel.py +++ b/app/views/pq_debug_panel.py @@ -3,6 +3,8 @@ PQ 单步调试面板 支持屏模组、SDR、HDR 三种测试类型的单步调试功能 """ +import copy + import ttkbootstrap as ttk import tkinter as tk from tkinter import messagebox @@ -840,8 +842,12 @@ class PQDebugPanel: # ==================== 构建对比数据 ==================== comparison = {} - if test_item == "gamma": - # ========== Gamma 测试:只对比 x, y, lv ========== + if test_item in ("gamma", "eotf"): + result_key = "gamma" if test_item == "gamma" else "eotf" + original_gamma = self._get_gamma_from_results(result_key, index) + new_gamma = self._calculate_gamma_at_index( + test_type, test_item, index, new_data + ) comparison["x"] = ( original_data[0], new_data[0], @@ -857,23 +863,10 @@ class PQDebugPanel: new_data[2], new_data[2] - original_data[2], ) - - elif test_item == "eotf": - # ========== EOTF 测试:只对比 x, y, lv ========== - comparison["x"] = ( - original_data[0], - new_data[0], - new_data[0] - original_data[0], - ) - comparison["y"] = ( - original_data[1], - new_data[1], - new_data[1] - original_data[1], - ) - comparison["lv"] = ( - original_data[2], - new_data[2], - new_data[2] - original_data[2], + comparison["γ"] = ( + original_gamma, + new_gamma, + new_gamma - original_gamma, ) elif test_item == "accuracy": @@ -983,6 +976,16 @@ class PQDebugPanel: diff_str = f"{diff:+.2f}" threshold = 0.5 + elif key == "γ": + original_str = self._format_gamma_value(original) + new_str = self._format_gamma_value(new) + if original_str == "--" or new_str == "--": + diff_str = "--" + threshold = None + else: + diff_str = f"{diff:+.2f}" + threshold = 0.05 + else: # x, y # x, y 使用 4 位小数 original_str = f"{original:.4f}" @@ -991,7 +994,9 @@ class PQDebugPanel: threshold = 0.001 # 判断差异 - if abs(diff) > threshold: + if threshold is None: + tag = "normal" + elif abs(diff) > threshold: tag = "warning" else: tag = "normal" @@ -1114,6 +1119,79 @@ class PQDebugPanel: "100% Yellow", ] + def _format_gamma_value(self, gamma): + """格式化 Gamma 值,无效时显示 --""" + if gamma is None or gamma < 0.5 or gamma > 5.0: + return "--" + return f"{gamma:.2f}" + + def _get_gamma_calc_params(self, results): + """获取 Gamma 计算参数(与批量测试保持一致)""" + config_max_value = self.app.config.current_pattern.get( + "measurement_max_value", 10 + ) + try: + max_index_fix = int(config_max_value) + except (ValueError, TypeError): + max_index_fix = 10 + + actual_max_index = len(results) - 1 + if max_index_fix > actual_max_index: + max_index_fix = actual_max_index + + pattern_params = self.app.config.default_pattern_gray.get( + "pattern_params", None + ) + return max_index_fix, pattern_params + + def _get_gamma_from_results(self, result_key, index): + """从原始测试结果中读取指定灰阶的 Gamma 值""" + try: + if result_key not in self.app.results.test_items: + return 0.0 + + result_data = self.app.results.test_items[result_key].final_result + if not result_data: + return 0.0 + + gamma_list = result_data.get(result_key, []) + if index >= len(gamma_list): + return 0.0 + + item = gamma_list[index] + return item[3] if len(item) > 3 else 0.0 + + except Exception as e: + self.app.log_gui.log(f"读取 Gamma 失败: {str(e)}", level="error") + return 0.0 + + def _calculate_gamma_at_index(self, test_type, test_item, index, new_data): + """用单步测量更新对应灰阶后,重新计算该点的 Gamma 值""" + try: + key = f"{test_type}_{test_item}" + results = copy.deepcopy(self.original_data[key]) + + updated = list(results[index]) + updated[0] = new_data[0] + updated[1] = new_data[1] + updated[2] = new_data[2] + for i in range(3, min(len(updated), len(new_data))): + updated[i] = new_data[i] + results[index] = updated + + max_index_fix, pattern_params = self._get_gamma_calc_params(results) + results_with_gamma, _ = self.app.calculate_gamma( + results, max_index_fix, pattern_params + ) + + if index >= len(results_with_gamma): + return 0.0 + return results_with_gamma[index][3] + + except Exception as e: + self.app.log_gui.log(f"计算 Gamma 失败: {str(e)}", level="error") + return 0.0 + def _get_delta_e_from_results(self, test_type, color_name): """从原始测试结果中读取 ΔE 值""" try: diff --git a/pqAutomationApp.py b/pqAutomationApp.py index 40ec408..5c3fce3 100644 --- a/pqAutomationApp.py +++ b/pqAutomationApp.py @@ -6,7 +6,6 @@ import time import os import datetime import traceback -import matplotlib import matplotlib.pyplot as plt from app_version import APP_NAME, APP_VERSION, get_app_title from app.ucd import ( @@ -61,7 +60,7 @@ from app.plots.plot_gamut import PlotGamutMixin from app.views.chart_frame import ChartFrameMixin from app.config_io import ConfigIOMixin from app.tests.local_dimming import LocalDimmingMixin -from app.device.connection import DeviceConnectionMixin +from app.device.connection import DeviceConnectionMixin, ConnectionController from app.runner.test_runner import TestRunnerMixin plt.rcParams["font.family"] = ["sans-serif"] @@ -123,13 +122,12 @@ class PQAutomationApp( # 连接控制器:统一管理 CA/UCD 生命周期。 # 旧的 check_com_connections / disconnect_com_connections 等模块级 # 函数仍以类属性形式挂在 PQAutomationApp 上,内部全部委托给本对象。 - from app.device.connection import ConnectionController self.connection = ConnectionController(self) # 初始化测试状态 self.testing = False self.test_thread = None - # 采集节奏参数:默认在稳定性与速度之间取平衡,可按现场情况再微调。 + # 采集节奏参数由 settings/pq_config.json → acquisition_timing 加载(见 load_pq_config)。 self.pattern_settle_time = 0.4 self.pattern_progress_log_step = 5