添加信号格式修改
This commit is contained in:
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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]"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"])
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
@@ -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("<<ComboboxSelected>>", 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("<<ComboboxSelected>>", 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("<<ComboboxSelected>>", 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):
|
||||
"""根据当前测试类型更新测试项目复选框"""
|
||||
# 先隐藏所有测试项目框架
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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}"
|
||||
suffix = " [测试版]" if is_beta_version() else ""
|
||||
return f"{APP_NAME} v{APP_VERSION}{suffix}"
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 漏打包。
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user