From 9b2bc44e174bf6472062d43f2583ae39c425ac43 Mon Sep 17 00:00:00 2001 From: "xinzhu.yin" Date: Fri, 22 May 2026 11:31:36 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=BF=A1=E5=8F=B7=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/plots/plot_accuracy.py | 8 +- app/runner/test_runner.py | 41 ++-- app/services/ai_image.py | 10 +- app/services/pattern_service.py | 142 +++++------ app/views/panels/ai_image_panel.py | 14 +- app/views/panels/main_layout.py | 159 ++++++++++-- app/views/panels/pantone_baseline_panel.py | 21 +- app_version.py | 13 +- drivers/UCD323_Enum.py | 59 +++-- drivers/UCD323_Function.py | 272 +++++++-------------- installer.nsi | 8 +- pqAutomationApp.py | 3 + pqAutomationApp.spec | 28 +-- settings/pq_config.json | 12 +- 14 files changed, 434 insertions(+), 356 deletions(-) diff --git a/app/plots/plot_accuracy.py b/app/plots/plot_accuracy.py index c5aa40f..b060437 100644 --- a/app/plots/plot_accuracy.py +++ b/app/plots/plot_accuracy.py @@ -49,6 +49,7 @@ def plot_accuracy(self, accuracy_data, test_type): fontsize=11, y=0.98, fontweight="bold", + color="#111111", ) # ========== 29色:6行5列布局 ========== @@ -205,6 +206,7 @@ def plot_accuracy(self, accuracy_data, test_type): va="top", fontsize=7.5, fontweight="bold", + color="#111111", transform=self.accuracy_ax.transAxes, ) @@ -228,6 +230,7 @@ def plot_accuracy(self, accuracy_data, test_type): va="center", fontsize=7, fontweight="bold", + color="#111111", transform=self.accuracy_ax.transAxes, ) @@ -279,11 +282,11 @@ def plot_accuracy(self, accuracy_data, test_type): grade_color = "darkgreen" elif avg_delta_e < 3: grade = "优秀" - grade_icon = "✓✓" + grade_icon = "OK" grade_color = "green" elif avg_delta_e < 5: grade = "良好" - grade_icon = "✓" + grade_icon = "PASS" grade_color = "orange" else: grade = "需要校准" @@ -298,6 +301,7 @@ def plot_accuracy(self, accuracy_data, test_type): va="bottom", fontsize=7, fontweight="bold", + color="#111111", transform=self.accuracy_ax.transAxes, ) diff --git a/app/runner/test_runner.py b/app/runner/test_runner.py index 3b67fcf..e58692c 100644 --- a/app/runner/test_runner.py +++ b/app/runner/test_runner.py @@ -143,12 +143,10 @@ def run_custom_sdr_test(self, test_items): 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(f"信号格式: 色彩空间={color_space}, 数据范围={data_range}, 编码位深={bit_depth}", level="info") self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)", level="info") self.test_custom_sdr() @@ -168,12 +166,10 @@ def run_sdr_movie_test(self, test_items): # 获取信号格式设置 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(f"信号格式: 色彩空间={color_space}, 数据范围={data_range}, 编码位深={bit_depth}", level="info") # 判断是否需要灰阶数据 needs_gray_data = any( @@ -313,7 +309,20 @@ def send_fix_pattern(self, mode): self.log_gui.log("=" * 50, level="separator") - # 4. 循环发送图案并采集数据 + # 信号格式设置后等待电视重新锁定 HDMI 信号 + # format_changed=True 表示本次 set_video_mode 的参数与上次不同,TV 需要重新锁定 + format_changed = getattr(getattr(self, "ucd", None), "format_changed", True) + 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))) @@ -332,7 +341,8 @@ def send_fix_pattern(self, mode): return results should_log_detail = ( - i == 0 + total_patterns <= progress_step + or i == 0 or (i + 1) == total_patterns or ((i + 1) % progress_step == 0) ) @@ -477,12 +487,14 @@ def test_gamut(self, test_type): # SDR 测试:使用色彩空间设置 color_space = self.sdr_color_space_var.get() - if color_space == "BT.709": + 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( @@ -665,7 +677,7 @@ def test_gamma(self, test_type, gray_data=None): "gamma", {"gamma": results_with_gamma_list, "L_bar": L_bar} ) - # 绘制Gamma曲线 + # 绘制Gamma曲线(SDR 使用用户选择的参考值) if test_type == "sdr_movie": try: target_gamma = float(self.sdr_gamma_type_var.get()) @@ -673,7 +685,6 @@ def test_gamma(self, test_type, gray_data=None): 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)) @@ -877,7 +888,7 @@ def test_contrast(self, test_type, gray_data=None): def test_color_accuracy(self, test_type): """测试色准 - 使用手工实现的 ΔE 2000(应用 Gamma)""" - # ========== 读取用户选择的 Gamma ========== + # ========== Gamma 参考值 ========== if test_type == "sdr_movie": try: target_gamma = float(self.sdr_gamma_type_var.get()) @@ -886,7 +897,7 @@ def test_color_accuracy(self, test_type): 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(f"使用 Gamma: {target_gamma}", level="success") self.log_gui.log("=" * 50, level="separator") elif test_type == "hdr_movie": @@ -962,9 +973,9 @@ def test_color_accuracy(self, test_type): color_patches.append(name) if delta_e < 3: - grade, icon = "优秀", "✓" + grade, icon = "优秀", "OK" elif delta_e < 5: - grade, icon = "良好", "○" + grade, icon = "良好", "WARN" else: grade, icon = "偏差", "[Error]" diff --git a/app/services/ai_image.py b/app/services/ai_image.py index 089c5b5..01b04a8 100644 --- a/app/services/ai_image.py +++ b/app/services/ai_image.py @@ -16,6 +16,7 @@ import json import logging import mimetypes import os +import sys import shutil import threading import time @@ -253,8 +254,13 @@ def _call_pqtest_generate(user_message: str, session_id: str, timeout: float = A def get_cache_dir(base_dir: Optional[str] = None) -> str: - """返回缓存目录,如不存在则创建。``base_dir`` 默认使用当前工作目录。""" - root = base_dir if base_dir else os.getcwd() + """返回缓存目录,如不存在则创建。``base_dir`` 留空时使用应用根目录。""" + if base_dir: + root = base_dir + elif getattr(sys, "frozen", False): + root = os.path.dirname(sys.executable) + else: + root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) path = os.path.join(root, _CACHE_DIRNAME) os.makedirs(path, exist_ok=True) return path diff --git a/app/services/pattern_service.py b/app/services/pattern_service.py index 39274f3..d8a3e6b 100644 --- a/app/services/pattern_service.py +++ b/app/services/pattern_service.py @@ -40,56 +40,72 @@ class PatternService: "info", ) self.app.ucd.set_ucd_params(active_config) + elif test_type == "sdr_movie": - active_config = self._prepare_video_session( - mode=mode, - test_type=test_type, - source_params=source_params, - data_range=self.app.sdr_data_range_var.get(), - log_title="设置 SDR 信号格式:", - setup_message=f"设置 SDR 测试图案({mode} 模式)..." - if mode != "accuracy" - else "设置 SDR 29色色准测试图案...", - setup_format=lambda: self.app.ucd.set_sdr_format( - color_space=self.app.sdr_color_space_var.get(), - gamma=self.app.sdr_gamma_type_var.get(), - data_range=self.app.sdr_data_range_var.get(), - bit_depth=self.app.sdr_bit_depth_var.get(), - ), - log_items=[ + data_range = self.app.sdr_data_range_var.get() + if log_details: + self._log("=" * 50, "separator") + self._log("设置 SDR 信号格式:", "info") + self._log("=" * 50, "separator") + for label, value in [ ("色彩空间", self.app.sdr_color_space_var.get()), - ("Gamma", self.app.sdr_gamma_type_var.get()), - ("数据范围", self.app.sdr_data_range_var.get()), + ("色彩格式", self.app.sdr_output_format_var.get()), + ("Gamma", self.app.sdr_gamma_type_var.get()), + ("数据范围", data_range), ("编码位深", self.app.sdr_bit_depth_var.get()), - ], - log_details=log_details, + ]: + self._log(f" {label}: {value}", "info") + converted_params = convert_pattern_params( + source_params, data_range=data_range, verbose=False ) + active_config = self.app.config.get_temp_config_with_converted_params( + mode=mode, converted_params=converted_params + ) + self.app.ucd.set_ucd_params(active_config) + success = self.app.ucd.apply_signal_format( + color_space=self.app.sdr_color_space_var.get(), + data_range=data_range, + bit_depth=self.app.sdr_bit_depth_var.get(), + color_format=self.app.sdr_output_format_var.get(), + ) + if log_details: + self._log(f"SDR 信号格式设置{'成功' if success else '失败'}", "success" if success else "error") + self._log(f"图案参数已设置,共 {len(converted_params)} 个图案", "success") + elif test_type == "hdr_movie": - active_config = self._prepare_video_session( - mode=mode, - test_type=test_type, - source_params=source_params, - data_range=self.app.hdr_data_range_var.get(), - log_title="设置 HDR 信号格式:", - setup_message=f"设置 HDR 测试图案({mode} 模式)..." - if mode != "accuracy" - else "设置 HDR 29色色准测试图案...", - setup_format=lambda: self.app.ucd.set_hdr_format( - color_space=self.app.hdr_color_space_var.get(), - data_range=self.app.hdr_data_range_var.get(), - bit_depth=self.app.hdr_bit_depth_var.get(), - max_cll=self.app.hdr_maxcll_var.get(), - max_fall=self.app.hdr_maxfall_var.get(), - ), - log_items=[ + data_range = self.app.hdr_data_range_var.get() + if log_details: + self._log("=" * 50, "separator") + self._log("设置 HDR 信号格式:", "info") + self._log("=" * 50, "separator") + for label, value in [ ("色彩空间", self.app.hdr_color_space_var.get()), - ("数据范围", self.app.hdr_data_range_var.get()), + ("色彩格式", self.app.hdr_output_format_var.get()), + ("数据范围", data_range), ("编码位深", self.app.hdr_bit_depth_var.get()), - ("MaxCLL", self.app.hdr_maxcll_var.get()), + ("MaxCLL", self.app.hdr_maxcll_var.get()), ("MaxFALL", self.app.hdr_maxfall_var.get()), - ], - log_details=log_details, + ]: + self._log(f" {label}: {value}", "info") + converted_params = convert_pattern_params( + source_params, data_range=data_range, verbose=False ) + active_config = self.app.config.get_temp_config_with_converted_params( + mode=mode, converted_params=converted_params + ) + self.app.ucd.set_ucd_params(active_config) + success = self.app.ucd.apply_signal_format( + color_space=self.app.hdr_color_space_var.get(), + data_range=data_range, + bit_depth=self.app.hdr_bit_depth_var.get(), + color_format=self.app.hdr_output_format_var.get(), + max_cll=self.app.hdr_maxcll_var.get(), + max_fall=self.app.hdr_maxfall_var.get(), + ) + if log_details: + self._log(f"HDR 信号格式设置{'成功' if success else '失败'}", "success" if success else "error") + self._log(f"图案参数已设置,共 {len(converted_params)} 个图案", "success") + else: raise ValueError(f"不支持的测试类型: {test_type}") @@ -122,50 +138,6 @@ class PatternService: send_solid_rgb_pattern(self.app.ucd, converted_rgb, raise_on_error=True) return True - def _prepare_video_session( - self, - *, - mode, - test_type, - source_params, - data_range, - log_title, - setup_message, - setup_format, - log_items, - log_details, - ): - if log_details: - self._log("=" * 50, "separator") - self._log(log_title, "info") - self._log("=" * 50, "separator") - for label, value in log_items: - self._log(f" {label}: {value}", "info") - - success = setup_format() - if log_details: - self._log( - f"{test_type.split('_')[0].upper()} 信号格式设置{'成功' if success else '失败'}", - "success" if success else "error", - ) - self._log(setup_message, "info") - - converted_params = convert_pattern_params( - pattern_params=source_params, - data_range=data_range, - verbose=False, - ) - active_config = self.app.config.get_temp_config_with_converted_params( - mode=mode, - converted_params=converted_params, - ) - self.app.ucd.set_ucd_params(active_config) - - if log_details: - self._log(f"图案参数已设置,共 {len(converted_params)} 个图案", "success") - - return active_config - def _get_source_pattern_params(self, mode): return copy.deepcopy(get_pattern(mode)["pattern_params"]) diff --git a/app/views/panels/ai_image_panel.py b/app/views/panels/ai_image_panel.py index db38029..2bc2545 100644 --- a/app/views/panels/ai_image_panel.py +++ b/app/views/panels/ai_image_panel.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +import sys import threading import tkinter as tk from tkinter import filedialog, messagebox, simpledialog @@ -193,6 +194,15 @@ def toggle_ai_image_panel(self): self.show_panel("ai_image") +def _get_app_base_dir(self) -> str: + """返回应用根目录(settings 的上一级)。""" + if getattr(self, "config_file", None): + return os.path.dirname(os.path.dirname(self.config_file)) + if getattr(sys, "frozen", False): + return os.path.dirname(sys.executable) + return os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) + + # ---------------- 列表 / 选中 ---------------- @@ -203,7 +213,7 @@ def reload_ai_image_list(self, auto_select_first=True): 其下列出该轮生成的所有图片。会话按"最近使用"倒序,组内按时间倒序。 auto_select_first: 是否自动选中第一张图片(默认 True)。 """ - self.ai_image_records = _svc.list_records() + self.ai_image_records = _svc.list_records(base_dir=_get_app_base_dir(self)) self.ai_image_listbox.delete(0, tk.END) # 维护行号 → 记录索引的映射;分隔头处为 None self._ai_image_row_map = [] @@ -414,6 +424,7 @@ def _send_prompt(self): prompt, on_success=_success, on_error=_error, + base_dir=_get_app_base_dir(self), cancel_event=self._ai_image_cancel_event, ) return @@ -422,6 +433,7 @@ def _send_prompt(self): prompt, on_success=_success, on_error=_error, + base_dir=_get_app_base_dir(self), cancel_event=self._ai_image_cancel_event, ) diff --git a/app/views/panels/main_layout.py b/app/views/panels/main_layout.py index 687f819..45214a5 100644 --- a/app/views/panels/main_layout.py +++ b/app/views/panels/main_layout.py @@ -137,21 +137,21 @@ def create_signal_format_content(self): sdr_color_space_combo = ttk.Combobox( self.sdr_signal_frame, textvariable=self.sdr_color_space_var, - values=["BT.709", "BT.601", "BT.2020"], + values=["sRGB", "BT.709", "BT.601", "BT.2020", "DCI-P3"], width=10, state="readonly", ) sdr_color_space_combo.grid(row=0, column=1, sticky=tk.W, padx=5, pady=2) - # Gamma + # Gamma(测试参考值,用于Gamma曲线绘制和色准计算) ttk.Label(self.sdr_signal_frame, text="Gamma:").grid( row=1, column=0, sticky=tk.W, padx=5, pady=2 ) - self.sdr_gamma_type_var = tk.StringVar(value="2.2") + self.sdr_gamma_type_var = tk.StringVar(value=UCDEnum.SignalFormat.GammaType.GAMMA_22) sdr_gamma_combo = ttk.Combobox( self.sdr_signal_frame, textvariable=self.sdr_gamma_type_var, - values=["2.2", "2.4", "2.6"], + values=UCDEnum.SignalFormat.GammaType.get_list(), width=10, state="readonly", ) @@ -161,11 +161,11 @@ def create_signal_format_content(self): ttk.Label(self.sdr_signal_frame, text="数据范围:").grid( row=2, column=0, sticky=tk.W, padx=5, pady=2 ) - self.sdr_data_range_var = tk.StringVar(value="Full") + self.sdr_data_range_var = tk.StringVar(value=UCDEnum.SignalFormat.DataRange.FULL) sdr_range_combo = ttk.Combobox( self.sdr_signal_frame, textvariable=self.sdr_data_range_var, - values=["Full", "Limited"], + values=UCDEnum.SignalFormat.DataRange.get_list(), width=10, state="readonly", ) @@ -175,16 +175,50 @@ def create_signal_format_content(self): ttk.Label(self.sdr_signal_frame, text="编码位深:").grid( row=3, column=0, sticky=tk.W, padx=5, pady=2 ) - self.sdr_bit_depth_var = tk.StringVar(value="8bit") + self.sdr_bit_depth_var = tk.StringVar(value=UCDEnum.SignalFormat.BitDepth.BIT_8) sdr_bit_depth_combo = ttk.Combobox( self.sdr_signal_frame, textvariable=self.sdr_bit_depth_var, - values=["8bit", "10bit", "12bit"], + values=UCDEnum.SignalFormat.BitDepth.get_list(), width=10, state="readonly", ) sdr_bit_depth_combo.grid(row=3, column=1, sticky=tk.W, padx=5, pady=2) + # 分辨率 + ttk.Label(self.sdr_signal_frame, text="分辨率:").grid( + row=4, column=0, sticky=tk.W, padx=5, pady=2 + ) + self.sdr_timing_var = tk.StringVar( + value=self.config.current_test_types.get("sdr_movie", {}).get( + "timing", "DMT 1920x1080@60Hz" + ) + ) + sdr_timing_combo = ttk.Combobox( + self.sdr_signal_frame, + textvariable=self.sdr_timing_var, + values=UCDEnum.TimingInfo.get_formatted_resolution_list(), + width=20, + state="readonly", + ) + sdr_timing_combo.bind("<>", self.on_sdr_timing_changed) + sdr_timing_combo.grid(row=4, column=1, sticky=tk.W, padx=5, pady=2) + + # 色彩格式 + ttk.Label(self.sdr_signal_frame, text="色彩格式:").grid( + row=5, column=0, sticky=tk.W, padx=5, pady=2 + ) + self.sdr_output_format_var = tk.StringVar(value=UCDEnum.SignalFormat.OutputFormat.RGB) + sdr_output_format_combo = ttk.Combobox( + self.sdr_signal_frame, + textvariable=self.sdr_output_format_var, + values=UCDEnum.SignalFormat.OutputFormat.get_list(), + width=10, + state="readonly", + ) + sdr_output_format_combo.bind("<>", self.on_sdr_output_format_changed) + sdr_output_format_combo.grid(row=5, column=1, sticky=tk.W, padx=5, pady=2) + # ==================== HDR信号格式设置 ==================== self.hdr_signal_frame = ttk.Frame(self.signal_tabs) # 配置列权重 @@ -235,11 +269,11 @@ def create_signal_format_content(self): ttk.Label(self.hdr_signal_frame, text="数据范围:").grid( row=3, column=0, sticky=tk.W, padx=5, pady=2 ) - self.hdr_data_range_var = tk.StringVar(value="Full") + self.hdr_data_range_var = tk.StringVar(value=UCDEnum.SignalFormat.DataRange.FULL) hdr_range_combo = ttk.Combobox( self.hdr_signal_frame, textvariable=self.hdr_data_range_var, - values=["Full", "Limited"], + values=UCDEnum.SignalFormat.DataRange.get_list(), width=10, state="readonly", ) @@ -249,16 +283,31 @@ def create_signal_format_content(self): ttk.Label(self.hdr_signal_frame, text="编码位深:").grid( row=4, column=0, sticky=tk.W, padx=5, pady=2 ) - self.hdr_bit_depth_var = tk.StringVar(value="8bit") + self.hdr_bit_depth_var = tk.StringVar(value=UCDEnum.SignalFormat.BitDepth.BIT_8) hdr_bit_depth_combo = ttk.Combobox( self.hdr_signal_frame, textvariable=self.hdr_bit_depth_var, - values=["8bit", "10bit", "12bit"], + values=UCDEnum.SignalFormat.BitDepth.get_list(), width=10, state="readonly", ) hdr_bit_depth_combo.grid(row=4, column=1, sticky=tk.W, padx=5, pady=2) + # 色彩格式 + ttk.Label(self.hdr_signal_frame, text="色彩格式:").grid( + row=5, column=0, sticky=tk.W, padx=5, pady=2 + ) + self.hdr_output_format_var = tk.StringVar(value=UCDEnum.SignalFormat.OutputFormat.RGB) + hdr_output_format_combo = ttk.Combobox( + self.hdr_signal_frame, + textvariable=self.hdr_output_format_var, + values=UCDEnum.SignalFormat.OutputFormat.get_list(), + width=10, + state="readonly", + ) + hdr_output_format_combo.bind("<>", self.on_hdr_output_format_changed) + hdr_output_format_combo.grid(row=5, column=1, sticky=tk.W, padx=5, pady=2) + # ==================== 初始化:默认只启用屏模组 Tab ==================== self.signal_tabs.select(0) # 选中屏模组 self.signal_tabs.tab(1, state="disabled") # 禁用 SDR @@ -271,8 +320,9 @@ def create_connection_content(self): com_frame = ttk.Frame(self.connection_frame) com_frame.pack(fill=tk.X, pady=5) - # 获取可用的COM端口列表 + # 获取可用的COM端口列表和UCD设备列表 available_ports = self.get_available_com_ports() + available_ucd_list = self.get_available_ucd_ports() # 使用网格布局,更整齐 ttk.Label(com_frame, text="UCD列表:").grid( @@ -282,7 +332,7 @@ def create_connection_content(self): self.ucd_list_combo = ttk.Combobox( com_frame, textvariable=self.ucd_list_var, - values=available_ports, + values=available_ucd_list, width=10, state="readonly", ) @@ -453,6 +503,19 @@ def create_test_type_frame(self): ) self.pantone_baseline_btn.pack(fill=tk.X, padx=0, pady=1) + # 测试版水印标签(版本 x.x.0.0 时显示) + from app_version import is_beta_version, APP_VERSION + if is_beta_version(): + beta_lbl = tk.Label( + self.sidebar_frame, + text=f"[测试版] v{APP_VERSION}", + foreground="#ffffff", + background="#cc3300", + font=("微软雅黑", 8, "bold"), + anchor="center", + ) + beta_lbl.pack(fill=tk.X, side=tk.BOTTOM, padx=4, pady=(6, 4)) + # 注册面板按钮 if hasattr(self, "panels"): if "log" in self.panels: @@ -569,6 +632,74 @@ def on_screen_module_timing_changed(self, event=None): self.log_gui.log(f"屏模组信号格式更改失败: {str(e)}", level="error") +def on_sdr_timing_changed(self, event=None): + """SDR测试分辨率改变时的回调""" + try: + selected_timing = self.sdr_timing_var.get() + self.log_gui.log(f"SDR测试分辨率已更改为: {selected_timing}", level="info") + + # 直接更新 sdr_movie 的 timing 配置 + self.config.current_test_types["sdr_movie"]["timing"] = selected_timing + + if self.testing: + self.log_gui.log("警告: 测试进行中,分辨率更改将在下次测试时生效", level="error") + + self.save_pq_config() + + except Exception as e: + self.log_gui.log(f"SDR测试分辨率更改失败: {str(e)}", level="error") + + +def on_sdr_output_format_changed(self, event=None): + """SDR 色彩格式改变时的回调""" + try: + fmt = self.sdr_output_format_var.get() + self.log_gui.log(f"SDR色彩格式已更改为: {fmt}", level="info") + + if self.testing: + self.log_gui.log("警告: 测试进行中,格式更改将在下次测试时生效", level="error") + return + + if getattr(self.ucd, "status", False): + ok = self.ucd.apply_signal_format( + color_space=self.sdr_color_space_var.get(), + data_range=self.sdr_data_range_var.get(), + bit_depth=self.sdr_bit_depth_var.get(), + color_format=fmt, + ) + if not ok: + self.log_gui.log("SDR色彩格式应用到UCD失败", level="error") + + except Exception as e: + self.log_gui.log(f"SDR色彩格式更改失败: {str(e)}", level="error") + + +def on_hdr_output_format_changed(self, event=None): + """HDR 色彩格式改变时的回调""" + try: + fmt = self.hdr_output_format_var.get() + self.log_gui.log(f"HDR色彩格式已更改为: {fmt}", level="info") + + if self.testing: + self.log_gui.log("警告: 测试进行中,格式更改将在下次测试时生效", level="error") + return + + if getattr(self.ucd, "status", False): + ok = self.ucd.apply_signal_format( + 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(), + color_format=fmt, + ) + if not ok: + self.log_gui.log("HDR色彩格式应用到UCD失败", level="error") + + except Exception as e: + self.log_gui.log(f"HDR色彩格式更改失败: {str(e)}", level="error") + + def update_test_items(self): """根据当前测试类型更新测试项目复选框""" # 先隐藏所有测试项目框架 diff --git a/app/views/panels/pantone_baseline_panel.py b/app/views/panels/pantone_baseline_panel.py index d0ad59e..431d1e7 100644 --- a/app/views/panels/pantone_baseline_panel.py +++ b/app/views/panels/pantone_baseline_panel.py @@ -4,6 +4,7 @@ from __future__ import annotations import datetime import os +import sys import threading import tkinter as tk from tkinter import filedialog, messagebox @@ -11,7 +12,7 @@ from tkinter import filedialog, messagebox import ttkbootstrap as ttk -_TEMPLATE_FILE = "pantone\xa02670\xa0colors.xlsx" +_TEMPLATE_FILE = "pantone_2670_colors.xlsx" def create_pantone_baseline_panel(self): @@ -153,8 +154,22 @@ def toggle_pantone_baseline_panel(self): self.show_panel("pantone_baseline") +def _get_settings_dir(self): + """返回 settings 绝对目录,避免依赖当前工作目录。""" + if getattr(self, "config_file", None): + return os.path.dirname(self.config_file) + + if getattr(sys, "frozen", False): + base_dir = os.path.dirname(sys.executable) + else: + base_dir = os.path.dirname( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + ) + return os.path.join(base_dir, "settings") + + def _load_patterns(self): - path = os.path.join("settings", _TEMPLATE_FILE) + path = os.path.join(_get_settings_dir(self), _TEMPLATE_FILE) if not os.path.isfile(path): raise FileNotFoundError(f"未找到模板文件: {path}") @@ -513,7 +528,7 @@ def _auto_save_template(self): def _write_template_xlsx(self, path): # 优先复制 settings 模板,再覆盖数据区;没有模板时自动创建同结构表。 - template_path = os.path.join("settings", _TEMPLATE_FILE) + template_path = os.path.join(_get_settings_dir(self), _TEMPLATE_FILE) from openpyxl import load_workbook, Workbook if os.path.isfile(template_path): diff --git a/app_version.py b/app_version.py index 2551a50..f27b34d 100644 --- a/app_version.py +++ b/app_version.py @@ -1,6 +1,15 @@ APP_NAME = "PQ 自动化测试工具" -APP_VERSION = "0.1.0" +APP_VERSION = "106.26.0.0" + + +def is_beta_version(version: str = APP_VERSION) -> bool: + """版本号第3、4段均为 '0' 时(格式 x.x.0.0)判定为测试版。""" + parts = version.split(".") + if len(parts) >= 4: + return parts[2] == "0" and parts[3] == "0" + return False def get_app_title(): - return f"{APP_NAME} v{APP_VERSION}" \ No newline at end of file + suffix = " [测试版]" if is_beta_version() else "" + return f"{APP_NAME} v{APP_VERSION}{suffix}" \ No newline at end of file diff --git a/drivers/UCD323_Enum.py b/drivers/UCD323_Enum.py index 57c3ca9..6fdee11 100644 --- a/drivers/UCD323_Enum.py +++ b/drivers/UCD323_Enum.py @@ -522,11 +522,7 @@ class UCDEnum: @staticmethod def get_list(): - return [ - SignalFormat.GammaType.GAMMA_22, - SignalFormat.GammaType.GAMMA_24, - SignalFormat.GammaType.GAMMA_26, - ] + return ["2.2", "2.4", "2.6"] @staticmethod def get_gamma_value(gamma_str): @@ -542,12 +538,7 @@ class UCDEnum: @staticmethod def get_list(): - return [SignalFormat.DataRange.FULL, SignalFormat.DataRange.LIMITED] - - @staticmethod - def is_full_range(range_str): - """判断是否为 Full Range""" - return range_str == SignalFormat.DataRange.FULL + return ["Full", "Limited"] class BitDepth: """编码位深枚举""" @@ -558,11 +549,7 @@ class UCDEnum: @staticmethod def get_list(): - return [ - SignalFormat.BitDepth.BIT_8, - SignalFormat.BitDepth.BIT_10, - SignalFormat.BitDepth.BIT_12, - ] + return ["8bit", "10bit", "12bit"] @staticmethod def get_bit_value(bit_str): @@ -583,8 +570,44 @@ class UCDEnum: @staticmethod def get_maxcll_list(): - return SignalFormat.HDRMetadata.MAX_CLL_OPTIONS + return [400, 600, 800, 1000, 1200, 1500, 2000, 4000] @staticmethod def get_maxfall_list(): - return SignalFormat.HDRMetadata.MAX_FALL_OPTIONS + return [200, 300, 400, 500, 600, 800, 1000] + + class OutputFormat: + """输出色彩格式枚举(决定信号是 RGB 还是 YCbCr 格式)""" + + RGB = "RGB" + YCBCR_422 = "YCbCr 4:2:2" + YCBCR_444 = "YCbCr 4:4:4" + YCBCR_420 = "YCbCr 4:2:0" + Y_ONLY = "Y Only" + IDO_DEFINED = "IDO Defined" + RAW = "RAW" + DSC = "DSC" + + @staticmethod + def get_list(): + return ["RGB", "YCbCr 4:4:4", "YCbCr 4:2:2", "YCbCr 4:2:0", + "Y Only", "IDO Defined", "RAW", "DSC"] + + @staticmethod + def is_ycbcr(format_str): + return "YCbCr" in (format_str or "") + + @staticmethod + def get_format_key(format_str): + """将显示字符串转换为 UCDEnum.ColorInfo.get_color_format() 的 key""" + fmt_map = { + "RGB": "rgb", + "YCbCr 4:4:4": "ycbcr444", + "YCbCr 4:2:2": "ycbcr422", + "YCbCr 4:2:0": "ycbcr420", + "Y Only": "yonly", + "IDO Defined": "ido_defined", + "RAW": "raw", + "DSC": "dsc", + } + return fmt_map.get(format_str, "rgb") diff --git a/drivers/UCD323_Function.py b/drivers/UCD323_Function.py index e3b0950..1afb5af 100644 --- a/drivers/UCD323_Function.py +++ b/drivers/UCD323_Function.py @@ -14,7 +14,7 @@ class UCDController: self.role = None self.timing_manager = None self.config = None - self.color_mode = None + self.color_info = None self.status = False self.current_interface = "HDMI" @@ -51,7 +51,7 @@ class UCDController: pg, _ = self.get_tx_modules() self.timing_manager = pg.timing_manager - self.color_mode = UniTAP.ColorInfo() + self.color_info = UniTAP.ColorInfo() self.status = True return True @@ -60,23 +60,26 @@ class UCDController: self._force_cleanup() return False + def _reset_state(self): + """重置所有运行时状态(不关闭设备句柄)""" + self.dev = None + self.role = None + self.status = False + self.timing_manager = None + self.current_timing = None + self.current_pattern = None + self.current_pattern_param = None + self.current_pattern_params = None + self.current_pattern_index = 0 + self.current_interface = "HDMI" + def close(self): """关闭设备""" try: if self.dev: self._close_device_object(self.dev) - self.dev = None - self.role = None - self.status = False - self.timing_manager = None - self.current_timing = None - self.current_pattern = None - self.current_pattern_param = None - self.current_pattern_params = None - self.current_pattern_index = 0 - self.current_interface = "HDMI" - + self._reset_state() self.lUniTAP = None for i in range(3): @@ -89,16 +92,7 @@ class UCDController: return True except Exception as e: - self.dev = None - self.role = None - self.status = False - self.timing_manager = None - self.current_timing = None - self.current_pattern = None - self.current_pattern_param = None - self.current_pattern_params = None - self.current_pattern_index = 0 - self.current_interface = "HDMI" + self._reset_state() try: self.lUniTAP = None @@ -135,16 +129,7 @@ class UCDController: if self.dev: self._close_device_object(self.dev) - self.dev = None - self.role = None - self.status = False - self.timing_manager = None - self.current_timing = None - self.current_pattern = None - self.current_pattern_param = None - self.current_pattern_params = None - self.current_pattern_index = 0 - self.current_interface = "HDMI" + self._reset_state() except Exception as e: pass @@ -210,22 +195,12 @@ class UCDController: pg, _ = self.get_tx_modules() self.timing_manager = pg.timing_manager - if test_type == "hdr_movie": - color_format_str = self.config.current_test_types[test_type]["color_format"] - color_format = UCDEnum.ColorInfo.get_color_format(color_format_str) + color_format = self.config.current_test_types[test_type]["color_format"] + bpc = self.config.current_test_types[test_type]["bpc"] + colorimetry = self.config.current_test_types[test_type]["colorimetry"] - if color_format: - self.color_mode.color_format = color_format - else: - return False - - else: - color_format = self.config.current_test_types[test_type]["color_format"] - bpc = self.config.current_test_types[test_type]["bpc"] - colorimetry = self.config.current_test_types[test_type]["colorimetry"] - - if not self.set_color_mode(color_format, bpc, colorimetry): - return False + if not self.set_color_mode(color_format, bpc, colorimetry): + return False timing_str = self.config.current_test_types[test_type]["timing"] self.set_timing_from_string(timing_str) @@ -251,7 +226,7 @@ class UCDController: return True def send_image_pattern(self, image_path): - """发送图片 Pattern(依赖当前 timing/color_mode 状态)。""" + """发送图片 Pattern(依赖当前 timing/color_info 状态)。""" if not self.status or not self.role: return False @@ -265,7 +240,7 @@ class UCDController: return False def send_solid_rgb_pattern(self, rgb): - """发送纯色 RGB Pattern(依赖当前 timing/color_mode 状态)。""" + """发送纯色 RGB Pattern(依赖当前 timing/color_info 状态)。""" if not self.status or not self.role: return False @@ -300,14 +275,7 @@ class UCDController: def set_color_mode(self, cf, bpc, cm): """设置颜色模式""" - current_dynamic_range = None - current_transfer_characteristic = None - - if hasattr(self.color_mode, "dynamic_range"): - current_dynamic_range = self.color_mode.dynamic_range - - if hasattr(self.color_mode, "transfer_characteristic"): - current_transfer_characteristic = self.color_mode.transfer_characteristic + current_dynamic_range = self.color_info.dynamic_range color_format = UCDEnum.ColorInfo.get_color_format(cf) if color_format is None: @@ -320,20 +288,15 @@ class UCDController: if colorimetry is None: return False - self.color_mode.color_format = color_format - self.color_mode.bpc = bpc - self.color_mode.colorimetry = colorimetry - - if current_dynamic_range is not None: - self.color_mode.dynamic_range = current_dynamic_range - - if current_transfer_characteristic is not None: - self.color_mode.transfer_characteristic = current_transfer_characteristic + self.color_info.color_format = color_format + self.color_info.bpc = bpc + self.color_info.colorimetry = colorimetry + self.color_info.dynamic_range = current_dynamic_range return True def apply_video_mode(self): - """应用当前colormode和timing""" + """应用当前 color_info 和 timing""" if self.current_timing: self.set_video_mode() return True @@ -341,25 +304,34 @@ class UCDController: def set_video_mode(self): """设置视频模式""" + # 对比上次发出的配置,判断是否会触发电视重新锁定信号 + current_config = ( + self.current_timing, + self.color_info.color_format, + self.color_info.colorimetry, + self.color_info.dynamic_range, + self.color_info.bpc, + ) + self.format_changed = (current_config != getattr(self, "_last_sent_config", None)) video_mode = UniTAP.VideoMode( - timing=self.current_timing, color_info=self.color_mode + timing=self.current_timing, color_info=self.color_info ) pg, _ = self.get_tx_modules() pg.set_vm(vm=video_mode) + self._last_sent_config = current_config return True def set_pattern(self, pattern, pattern_params=None): """设置pattern""" if self.current_timing: - if ( - pattern == UCDEnum.VideoPatternInfo.VideoPatternParams.SolidColor - or pattern == UCDEnum.VideoPatternInfo.VideoPatternParams.WhiteVStrips - or pattern - == UCDEnum.VideoPatternInfo.VideoPatternParams.GradientRGBStripes - or pattern == UCDEnum.VideoPatternInfo.VideoPatternParams.MotionPattern - or pattern == UCDEnum.VideoPatternInfo.VideoPatternParams.SquareWindow - and pattern_params - ): + needs_params = { + UCDEnum.VideoPatternInfo.VideoPatternParams.SolidColor, + UCDEnum.VideoPatternInfo.VideoPatternParams.WhiteVStrips, + UCDEnum.VideoPatternInfo.VideoPatternParams.GradientRGBStripes, + UCDEnum.VideoPatternInfo.VideoPatternParams.MotionPattern, + UCDEnum.VideoPatternInfo.VideoPatternParams.SquareWindow, + } + if pattern in needs_params and pattern_params: self.set_pattern_params(pattern, pattern_params) return True return False @@ -552,138 +524,62 @@ class UCDController: else: return False - def set_sdr_format( - self, color_space=None, gamma=None, data_range=None, bit_depth=None + def apply_signal_format( + self, color_space=None, data_range=None, bit_depth=None, color_format=None, **_ ): - """设置SDR信号格式""" - - def _get_colorimetry_from_color_space(color_space_name): - """将色彩空间名称转换为UniTAP colorimetry枚举值""" - try: - colorimetry_map = { - "BT.709": UniTAP.ColorInfo.Colorimetry.CM_ITUR_BT709, - "BT.601": UniTAP.ColorInfo.Colorimetry.CM_ITUR_BT601, - "BT.2020": UniTAP.ColorInfo.Colorimetry.CM_ITUR_BT2020_RGB, - } - result = colorimetry_map.get(color_space_name) - return result if result else UniTAP.ColorInfo.Colorimetry.CM_ITUR_BT709 - except Exception as e: - return UniTAP.ColorInfo.Colorimetry.CM_ITUR_BT709 - - def _set_gamma_transfer_characteristic(gamma_value_str): - """设置Gamma传输特性""" - try: - gamma_value = float(gamma_value_str) - - if abs(gamma_value - 2.2) < 0.1: - self.color_mode.transfer_characteristic = ( - UniTAP.ColorInfo.TransferCharacteristic.TRANSFER_BT709 - ) - return True - - elif abs(gamma_value - 2.4) < 0.1: - if hasattr( - UniTAP.ColorInfo.TransferCharacteristic, "TRANSFER_GAMMA24" - ): - self.color_mode.transfer_characteristic = ( - UniTAP.ColorInfo.TransferCharacteristic.TRANSFER_GAMMA24 - ) - return True - else: - self.color_mode.transfer_characteristic = ( - UniTAP.ColorInfo.TransferCharacteristic.TRANSFER_BT709 - ) - return False - - elif abs(gamma_value - 2.6) < 0.1: - if hasattr( - UniTAP.ColorInfo.TransferCharacteristic, "TRANSFER_GAMMA26" - ): - self.color_mode.transfer_characteristic = ( - UniTAP.ColorInfo.TransferCharacteristic.TRANSFER_GAMMA26 - ) - return True - else: - self.color_mode.transfer_characteristic = ( - UniTAP.ColorInfo.TransferCharacteristic.TRANSFER_BT709 - ) - return False - else: - return False - - except Exception as e: - return False - + """统一设置信号格式(color_format / colorimetry / dynamic_range / bpc)。 + 注:Gamma/EOTF 传输特性在 ColorInfo API 中不存在; + max_cll / max_fall 暂无对应 SDK 接口,通过 **_ 接收后忽略。 + """ try: - if color_space: - colorimetry = _get_colorimetry_from_color_space(color_space) - if colorimetry: - self.color_mode.colorimetry = colorimetry + if color_format: + fmt_key = UCDEnum.SignalFormat.OutputFormat.get_format_key(color_format) + cf = UCDEnum.ColorInfo.get_color_format(fmt_key) + if cf is not None: + self.color_info.color_format = cf - if gamma: - _set_gamma_transfer_characteristic(gamma) + if color_space: + colorimetry = self._get_colorimetry_from_color_space(color_space, color_format) + if colorimetry: + self.color_info.colorimetry = colorimetry if data_range: if data_range == "Full": - self.color_mode.dynamic_range = ( - UniTAP.ColorInfo.DynamicRange.DR_VESA - ) + self.color_info.dynamic_range = UniTAP.ColorInfo.DynamicRange.DR_VESA elif data_range == "Limited": - self.color_mode.dynamic_range = UniTAP.ColorInfo.DynamicRange.DR_CTA + self.color_info.dynamic_range = UniTAP.ColorInfo.DynamicRange.DR_CTA if bit_depth: bpc = UCDEnum.SignalFormat.BitDepth.get_bit_value(bit_depth) - self.color_mode.bpc = bpc + self.color_info.bpc = bpc if self.current_timing: self.set_video_mode() return True - except Exception as e: + except Exception: return False - def set_hdr_format( - self, - color_space=None, - data_range=None, - bit_depth=None, - max_cll=None, - max_fall=None, - ): - """设置HDR信号格式""" - try: - if color_space: - colorimetry = self._get_colorimetry_from_color_space(color_space) - if colorimetry: - self.color_mode.colorimetry = colorimetry + # 向后兼容别名 + set_sdr_format = apply_signal_format + set_hdr_format = apply_signal_format - if data_range: - if data_range == "Full": - self.color_mode.dynamic_range = ( - UniTAP.ColorInfo.DynamicRange.DR_VESA - ) - elif data_range == "Limited": - self.color_mode.dynamic_range = UniTAP.ColorInfo.DynamicRange.DR_CTA - - if bit_depth: - bpc = UCDEnum.SignalFormat.BitDepth.get_bit_value(bit_depth) - self.color_mode.bpc = bpc - - if self.current_timing: - self.set_video_mode() - - return True - - except Exception as e: - return False - - def _get_colorimetry_from_color_space(self, color_space): - """将色彩空间字符串转换为UniTAP.ColorInfo.Colorimetry""" + def _get_colorimetry_from_color_space(self, color_space, color_format=None): + """将色彩空间字符串转换为UniTAP.ColorInfo.Colorimetry。 + BT.2020 在 YCbCr 输出时使用 CM_ITUR_BT2020_YCbCr,RGB 输出时使用 CM_ITUR_BT2020_RGB。 + """ + is_ycbcr = UCDEnum.SignalFormat.OutputFormat.is_ycbcr(color_format) + bt2020_cm = ( + UniTAP.ColorInfo.Colorimetry.CM_ITUR_BT2020_YCbCr + if is_ycbcr + else UniTAP.ColorInfo.Colorimetry.CM_ITUR_BT2020_RGB + ) colorimetry_map = { + "sRGB": UniTAP.ColorInfo.Colorimetry.CM_sRGB, "BT.709": UniTAP.ColorInfo.Colorimetry.CM_ITUR_BT709, "BT.601": UniTAP.ColorInfo.Colorimetry.CM_ITUR_BT601, - "BT.2020": UniTAP.ColorInfo.Colorimetry.CM_ITUR_BT2020_RGB, + "BT.2020": bt2020_cm, "DCI-P3": UniTAP.ColorInfo.Colorimetry.CM_DCI_P3, } return colorimetry_map.get(color_space) diff --git a/installer.nsi b/installer.nsi index 43c3724..9cd9f32 100644 --- a/installer.nsi +++ b/installer.nsi @@ -57,10 +57,14 @@ Section "Main Installation" SEC_MAIN SetOutPath "$INSTDIR\internal" File /r /x "*.pdb" /x "*.lib" /x "*.exp" /x "*.h" /x "__pycache__" /x "*.pyc" "${DIST_ROOT}\internal\*.*" - IfFileExists "$INSTDIR\settings\pq_config.json" +3 0 - SetOutPath "$INSTDIR\settings" + SetOutPath "$INSTDIR\settings" + + IfFileExists "$INSTDIR\settings\pq_config.json" +2 0 File /oname=pq_config.json "${PROJECT_ROOT}\settings\pq_config.json" + IfFileExists "$INSTDIR\settings\pantone_2670_colors.xlsx" +2 0 + File /oname=pantone_2670_colors.xlsx "${PROJECT_ROOT}\settings\pantone_2670_colors.xlsx" + WriteUninstaller "$INSTDIR\Uninstall.exe" WriteRegStr HKCU "${APP_REG_KEY}" "InstallDir" "$INSTDIR" diff --git a/pqAutomationApp.py b/pqAutomationApp.py index 5eb1cac..d63b60f 100644 --- a/pqAutomationApp.py +++ b/pqAutomationApp.py @@ -289,8 +289,11 @@ class PQAutomationApp: create_test_type_frame = _main.create_test_type_frame update_config_info_display = _main.update_config_info_display on_screen_module_timing_changed = _main.on_screen_module_timing_changed + on_sdr_timing_changed = _main.on_sdr_timing_changed update_test_items = _main.update_test_items on_test_type_change = _main.on_test_type_change + on_sdr_output_format_changed = _main.on_sdr_output_format_changed + on_hdr_output_format_changed = _main.on_hdr_output_format_changed create_cct_params_frame = _ccp.create_cct_params_frame on_sdr_cct_param_focus_out = _ccp.on_sdr_cct_param_focus_out diff --git a/pqAutomationApp.spec b/pqAutomationApp.spec index 369c6ad..c46e4d3 100644 --- a/pqAutomationApp.spec +++ b/pqAutomationApp.spec @@ -3,6 +3,12 @@ import os import sys +from PyInstaller.utils.hooks import collect_dynamic_libs, collect_data_files + +numpy_binaries = collect_dynamic_libs('numpy') +matplotlib_datas = collect_data_files('matplotlib') + + SPEC_DIR = ( os.path.abspath(os.path.dirname(__file__)) if "__file__" in globals() @@ -22,20 +28,10 @@ from PyInstaller.utils.win32.versioninfo import ( VarStruct, ) - -def build_windows_version(version_text): - parts = [int(part) for part in version_text.split('.') if part.strip()] - parts = (parts + [0, 0, 0, 0])[:4] - return tuple(parts) - - -windows_version = build_windows_version(APP_VERSION) -windows_version_text = '.'.join(str(part) for part in windows_version) - version_info = VSVersionInfo( ffi=FixedFileInfo( - filevers=windows_version, - prodvers=windows_version, + filevers=(5, 26, 1519, 2), + prodvers=(5, 0, 1, 0), mask=0x3F, flags=0x0, OS=0x40004, @@ -51,11 +47,11 @@ version_info = VSVersionInfo( [ StringStruct('CompanyName', 'Moka'), StringStruct('FileDescription', APP_NAME), - StringStruct('FileVersion', windows_version_text), + StringStruct('FileVersion', '5.26.1519.2'), StringStruct('InternalName', 'pqAutomationApp'), StringStruct('OriginalFilename', 'pqAutomationApp.exe'), StringStruct('ProductName', APP_NAME), - StringStruct('ProductVersion', windows_version_text), + StringStruct('ProductVersion', '5.0.1.0'), ], ) ] @@ -68,8 +64,8 @@ version_info = VSVersionInfo( a = Analysis( ['pqAutomationApp.py'], pathex=[], - binaries=[], - datas=[('assets', 'assets'), ('UniTAP', 'UniTAP')], + binaries=numpy_binaries, + datas=[('assets', 'assets'), ('UniTAP', 'UniTAP')] + matplotlib_datas, hiddenimports=[ # app 包的子模块在主文件里是静态 import 的,但惰性调用 / 属性绑定 # 场景较多,显式列出可避免 PyInstaller 漏打包。 diff --git a/settings/pq_config.json b/settings/pq_config.json index 89e3c20..fdd9979 100644 --- a/settings/pq_config.json +++ b/settings/pq_config.json @@ -9,7 +9,7 @@ "cct", "contrast" ], - "timing": "DMT 1920x 1080 @ 60Hz", + "timing": "DMT 1600x 1200 @ 60Hz", "color_format": "RGB", "bpc": 8, "colorimetry": "sRGB", @@ -24,11 +24,7 @@ "sdr_movie": { "name": "SDR Movie测试", "test_items": [ - "gamut", - "gamma", - "cct", - "contrast", - "accuracy" + "gamut" ], "timing": "DMT 1920x 1080 @ 60Hz", "color_format": "RGB", @@ -40,7 +36,7 @@ "y_ideal": 0.329, "y_tolerance": 0.003 }, - "gamut_reference": "DCI-P3" + "gamut_reference": "BT.2020" }, "hdr_movie": { "name": "HDR Movie测试", @@ -64,7 +60,7 @@ } }, "device_config": { - "ca_com": "COM6", + "ca_com": "COM3", "ucd_list": "0: UCD-323 [2128C209]", "ca_channel": "0" },