修改摸底测试功能、修改pattern控制逻辑

This commit is contained in:
xinzhu.yin
2026-05-13 17:17:13 +08:00
parent 9a52e34f2b
commit 0513f810bd
13 changed files with 479 additions and 3067 deletions

View File

@@ -344,6 +344,8 @@ class PQConfig:
temp_config.current_pattern = copy.deepcopy(self.default_pattern_gray)
elif mode == "accuracy":
temp_config.current_pattern = copy.deepcopy(self.default_pattern_accuracy)
elif mode == "custom":
temp_config.current_pattern = copy.deepcopy(self.default_pattern_temp)
# 3. 替换为转换后的参数
temp_config.current_pattern["pattern_params"] = converted_params

View File

@@ -13,7 +13,6 @@ import colour
import numpy as np
import algorithm.pq_algorithm as pq_algorithm
from app.data_range_converter import convert_pattern_params
from app.pq.pq_result import PQResult
def new_pq_results(self, test_type, test_name):
@@ -310,205 +309,12 @@ def send_fix_pattern(self, mode):
results = []
try:
# 1. 设置图案模式
if mode == "rgb":
self.config.set_current_pattern("rgb")
elif mode == "gray":
self.config.set_current_pattern("gray")
elif mode == "accuracy": # 色准模式SDR 和 HDR 通用 29色
self.config.set_current_pattern("accuracy")
elif mode == "custom":
self.config.set_current_pattern("custom")
else:
self.log_gui.log(f"未知的图案模式: {mode}", level="error")
return None
# 2. 获取当前测试类型
test_type = self.config.current_test_type
# 3. 根据测试类型设置信号格式和图案
if test_type == "screen_module":
# 屏模组测试:使用 Timing
self.log_gui.log("=" * 50, level="separator")
self.log_gui.log("设置屏模组信号格式:", level="info")
self.log_gui.log("=" * 50, level="separator")
timing_str = self.config.current_test_types[test_type]["timing"]
self.log_gui.log(f" Timing: {timing_str}", level="info")
# 屏模组测试:直接使用原始配置
self.ucd.set_ucd_params(self.config)
elif test_type == "sdr_movie":
# SDR 测试设置色彩空间、Gamma 等
self.log_gui.log("=" * 50, level="separator")
self.log_gui.log("设置 SDR 信号格式:", level="info")
self.log_gui.log("=" * 50, level="separator")
color_space = self.sdr_color_space_var.get()
gamma = self.sdr_gamma_type_var.get()
data_range = self.sdr_data_range_var.get()
bit_depth = self.sdr_bit_depth_var.get()
self.log_gui.log(f" 色彩空间: {color_space}", level="info")
self.log_gui.log(f" Gamma: {gamma}", level="info")
self.log_gui.log(f" 数据范围: {data_range}", level="info")
self.log_gui.log(f" 编码位深: {bit_depth}", level="info")
success = self.ucd.set_sdr_format(
color_space=color_space,
gamma=gamma,
data_range=data_range,
bit_depth=bit_depth,
)
if success:
self.log_gui.log("SDR 信号格式设置成功", level="success")
else:
self.log_gui.log("SDR 信号格式设置失败", level="error")
# 设置图案参数
if mode == "accuracy":
self.log_gui.log(f"设置 SDR 29色色准测试图案...", level="info")
else:
self.log_gui.log(f"设置 SDR 测试图案({mode} 模式)...", level="info")
# ========== ✅✅修改:使用临时配置对象 ==========
import copy
# 从原始配置获取参数(每次都是干净的)
if mode == "rgb":
original_params = copy.deepcopy(
self.config.default_pattern_rgb["pattern_params"]
)
elif mode == "gray":
original_params = copy.deepcopy(
self.config.default_pattern_gray["pattern_params"]
)
elif mode == "accuracy":
original_params = copy.deepcopy(
self.config.default_pattern_accuracy["pattern_params"]
)
elif mode == "custom":
original_params = copy.deepcopy(
self.config.default_pattern_temp["pattern_params"]
)
self.log_gui.log(f" 使用原始 RGB 参数(前 3 个):", level="info")
for i in range(min(3, len(original_params))):
self.log_gui.log(f" [{i+1}] {original_params[i]}", level="info")
# 根据 data_range 转换
converted_params = convert_pattern_params(
pattern_params=original_params, data_range=data_range, verbose=False
)
if data_range == "Limited":
self.log_gui.log(" 转换为 Limited Range (16-235):", level="info")
for i in range(min(3, len(converted_params))):
self.log_gui.log(
f" {original_params[i]}{converted_params[i]}"
, level="info")
else:
self.log_gui.log("Full RangeRGB 保持不变", level="success")
# 创建临时配置对象(不修改 self.config
temp_config = self.config.get_temp_config_with_converted_params(
mode=mode, converted_params=converted_params
)
# 使用临时配置设置参数
self.ucd.set_ucd_params(temp_config)
self.log_gui.log(f"图案参数已设置,共 {len(converted_params)} 个图案", level="success")
# ========== 修改结束 ==========
elif test_type == "hdr_movie":
# HDR 测试设置色彩空间、Metadata 等
self.log_gui.log("=" * 50, level="separator")
self.log_gui.log("设置 HDR 信号格式:", level="info")
self.log_gui.log("=" * 50, level="separator")
color_space = self.hdr_color_space_var.get()
data_range = self.hdr_data_range_var.get()
bit_depth = self.hdr_bit_depth_var.get()
max_cll = self.hdr_maxcll_var.get()
max_fall = self.hdr_maxfall_var.get()
self.log_gui.log(f" 色彩空间: {color_space}", level="info")
self.log_gui.log(f" 数据范围: {data_range}", level="info")
self.log_gui.log(f" 编码位深: {bit_depth}", level="info")
self.log_gui.log(f" MaxCLL: {max_cll}", level="info")
self.log_gui.log(f" MaxFALL: {max_fall}", level="info")
success = self.ucd.set_hdr_format(
color_space=color_space,
data_range=data_range,
bit_depth=bit_depth,
max_cll=max_cll,
max_fall=max_fall,
)
if success:
self.log_gui.log("HDR 信号格式设置成功", level="success")
else:
self.log_gui.log("HDR 信号格式设置失败", level="error")
# 设置图案参数
if mode == "accuracy":
self.log_gui.log(f"设置 HDR 29色色准测试图案...", level="info")
else:
self.log_gui.log(f"设置 HDR 测试图案({mode} 模式)...", level="info")
# ========== ✅✅修改:使用临时配置对象 ==========
import copy
# 从原始配置获取参数
if mode == "rgb":
original_params = copy.deepcopy(
self.config.default_pattern_rgb["pattern_params"]
)
elif mode == "gray":
original_params = copy.deepcopy(
self.config.default_pattern_gray["pattern_params"]
)
elif mode == "accuracy":
original_params = copy.deepcopy(
self.config.default_pattern_accuracy["pattern_params"]
)
self.log_gui.log(f" 使用原始 RGB 参数(前 3 个):", level="info")
for i in range(min(3, len(original_params))):
self.log_gui.log(f" [{i+1}] {original_params[i]}", level="info")
# 根据 data_range 转换
converted_params = convert_pattern_params(
pattern_params=original_params, data_range=data_range, verbose=False
)
if data_range == "Limited":
self.log_gui.log(" 转换为 Limited Range (16-235):", level="info")
for i in range(min(3, len(converted_params))):
self.log_gui.log(
f" {original_params[i]}{converted_params[i]}"
, level="info")
else:
self.log_gui.log("Full RangeRGB 保持不变", level="success")
# 创建临时配置对象
temp_config = self.config.get_temp_config_with_converted_params(
mode=mode, converted_params=converted_params
)
self.ucd.set_ucd_params(temp_config)
self.log_gui.log(f"图案参数已设置,共 {len(converted_params)} 个图案", level="success")
# ========== 修改结束 ==========
session = self.pattern_service.prepare_session(mode, log_details=True)
self.log_gui.log("=" * 50, level="separator")
# 4. 循环发送图案并采集数据(使用原始配置的数量)
total_patterns = len(self.config.current_pattern["pattern_params"])
# 4. 循环发送图案并采集数据
total_patterns = session.total_patterns
self.log_gui.log(f"开始采集数据,共 {total_patterns} 个图案", level="info")
settle_time = max(0.2, float(getattr(self, "pattern_settle_time", 1.0)))
progress_step = max(
@@ -518,14 +324,7 @@ def send_fix_pattern(self, mode):
f"采集等待时间: {settle_time:.2f}s可通过 pattern_settle_time 调整)"
, level="info")
# 获取颜色名称列表(用于日志显示)
color_names = None
if mode == "accuracy":
color_names = self.config.get_accuracy_color_names()
custom_pattern_names = []
if mode == "custom" and hasattr(self.config, "get_temp_pattern_names"):
custom_pattern_names = self.config.get_temp_pattern_names()
display_names = session.display_names
for i in range(total_patterns):
if not self.testing:
@@ -538,17 +337,15 @@ def send_fix_pattern(self, mode):
or ((i + 1) % progress_step == 0)
)
# 设置下一个图案(显示颜色名称)
if should_log_detail:
if color_names and i < len(color_names):
if display_names and i < len(display_names):
self.log_gui.log(
f"发送第 {i+1}/{total_patterns} 个图案: {color_names[i]}..."
f"发送第 {i+1}/{total_patterns} 个图案: {display_names[i]}..."
, level="info")
else:
self.log_gui.log(f"发送第 {i+1}/{total_patterns} 个图案...", level="info")
self.ucd.set_next_pattern()
self.ucd.run()
self.pattern_service.send_session_pattern(session, i)
time.sleep(settle_time)
# 测量数据
@@ -582,8 +379,8 @@ def send_fix_pattern(self, mode):
)
row_data = {
"pattern_name": (
custom_pattern_names[i]
if i < len(custom_pattern_names)
display_names[i]
if i < len(display_names)
else f"P {i + 1}"
),
"X": X,

View File

@@ -0,0 +1 @@
from app.services.pattern_service import PatternService, PatternSession

View File

@@ -0,0 +1,199 @@
from __future__ import annotations
import copy
from dataclasses import dataclass
from app.data_range_converter import convert_pattern_params
from drivers.ucd_helpers import send_solid_rgb_pattern
@dataclass
class PatternSession:
mode: str
test_type: str
active_config: object
pattern_params: list[list[int]]
total_patterns: int
display_names: list[str]
class PatternService:
def __init__(self, app):
self.app = app
def prepare_session(self, mode, *, test_type=None, log_details=False):
test_type = test_type or self.app.config.current_test_type
if not self.app.config.set_current_pattern(mode):
raise ValueError(f"未知的图案模式: {mode}")
active_config = self.app.config
source_params = self._get_source_pattern_params(mode)
if test_type == "screen_module":
if log_details:
self._log("=" * 50, "separator")
self._log("设置屏模组信号格式:", "info")
self._log("=" * 50, "separator")
self._log(
f" Timing: {self.app.config.current_test_types[test_type]['timing']}",
"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=[
("色彩空间", 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_bit_depth_var.get()),
],
log_details=log_details,
)
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=[
("色彩空间", self.app.hdr_color_space_var.get()),
("数据范围", self.app.hdr_data_range_var.get()),
("编码位深", self.app.hdr_bit_depth_var.get()),
("MaxCLL", self.app.hdr_maxcll_var.get()),
("MaxFALL", self.app.hdr_maxfall_var.get()),
],
log_details=log_details,
)
else:
raise ValueError(f"不支持的测试类型: {test_type}")
pattern_params = copy.deepcopy(active_config.current_pattern["pattern_params"])
return PatternSession(
mode=mode,
test_type=test_type,
active_config=active_config,
pattern_params=pattern_params,
total_patterns=len(pattern_params),
display_names=self._get_display_names(mode, len(pattern_params)),
)
def send_session_pattern(self, session, index):
if index < 0 or index >= session.total_patterns:
raise IndexError(f"pattern 索引越界: {index}")
pattern_param = session.pattern_params[index]
if not self.app.ucd.send_current_pattern_params(pattern_param):
raise RuntimeError(f"发送 pattern 失败: {index}")
return pattern_param
def send_rgb(self, rgb, *, session=None, test_type=None):
active_session = session or self.prepare_session(
"rgb",
test_type=test_type,
log_details=False,
)
converted_rgb = self._convert_rgb_for_test_type(rgb, active_session.test_type)
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):
config = self.app.config
if mode == "rgb":
return copy.deepcopy(config.default_pattern_rgb["pattern_params"])
if mode == "gray":
return copy.deepcopy(config.default_pattern_gray["pattern_params"])
if mode == "accuracy":
return copy.deepcopy(config.default_pattern_accuracy["pattern_params"])
if mode == "custom":
return copy.deepcopy(config.default_pattern_temp["pattern_params"])
raise ValueError(f"未知的图案模式: {mode}")
def _get_display_names(self, mode, total_patterns):
if mode == "accuracy":
return self.app.config.get_accuracy_color_names()
if mode == "custom" and hasattr(self.app.config, "get_temp_pattern_names"):
return self.app.config.get_temp_pattern_names()
return [f"P {index + 1}" for index in range(total_patterns)]
def _convert_rgb_for_test_type(self, rgb, test_type):
if test_type == "sdr_movie":
data_range = self.app.sdr_data_range_var.get()
elif test_type == "hdr_movie":
data_range = self.app.hdr_data_range_var.get()
else:
data_range = "Full"
return convert_pattern_params([list(rgb)], data_range=data_range, verbose=False)[0]
def _log(self, message, level):
if hasattr(self.app, "log_gui"):
self.app.log_gui.log(message, level=level)

View File

@@ -435,14 +435,14 @@ def create_test_type_frame(self):
)
self.ai_image_btn.pack(fill=tk.X, padx=0, pady=1)
self.single_step_btn = ttk.Button(
self.sidebar_frame,
text="单步调试",
style="Sidebar.TButton",
command=self.toggle_single_step_panel,
takefocus=False,
)
self.single_step_btn.pack(fill=tk.X, padx=0, pady=1)
# self.single_step_btn = ttk.Button(
# self.sidebar_frame,
# text="单步调试",
# style="Sidebar.TButton",
# command=self.toggle_single_step_panel,
# takefocus=False,
# )
# self.single_step_btn.pack(fill=tk.X, padx=0, pady=1)
self.pantone_baseline_btn = ttk.Button(
self.sidebar_frame,

View File

@@ -2,23 +2,16 @@
from __future__ import annotations
import csv
import datetime
import os
import tempfile
import threading
import tkinter as tk
from tkinter import filedialog, messagebox
import ttkbootstrap as ttk
from PIL import Image
from drivers.ucd_helpers import get_current_resolution, send_image_pattern
_PATTERN_FILE = "pantone_patterns_2670.csv"
_TEMPLATE_FILE = "pantone\xa02670\xa0colors.xlsx"
_TARGET_RESULT_COUNT = 2670
def create_pantone_baseline_panel(self):
@@ -34,6 +27,7 @@ def create_pantone_baseline_panel(self):
self._pantone_pause_requested = False
self._pantone_stop_requested = False
self._pantone_next_index = 0
self._pantone_target_count = 0
root = ttk.Frame(frame, padding=10)
root.pack(fill=tk.BOTH, expand=True)
@@ -47,12 +41,12 @@ def create_pantone_baseline_panel(self):
).pack(side=tk.LEFT)
self.pantone_status_var = tk.StringVar(value="未开始")
self.pantone_progress_var = tk.StringVar(value="0 / 2670")
self.pantone_progress_var = tk.StringVar(value="0 / 0")
self.pantone_settle_var = tk.StringVar(value="0.35")
config_row = ttk.LabelFrame(root, text="任务配置", padding=8)
config_row.pack(fill=tk.X)
ttk.Label(config_row, text=f"Pattern来源: settings/{_PATTERN_FILE}").pack(
ttk.Label(config_row, text=f"Pattern来源: settings/{_TEMPLATE_FILE}").pack(
side=tk.LEFT
)
ttk.Label(config_row, text="稳定等待(s):").pack(side=tk.LEFT, padx=(14, 4))
@@ -160,38 +154,38 @@ def toggle_pantone_baseline_panel(self):
def _load_patterns(self):
path = os.path.join("settings", _PATTERN_FILE)
path = os.path.join("settings", _TEMPLATE_FILE)
if not os.path.isfile(path):
raise FileNotFoundError(f"未找到 pattern 文件: {path}")
raise FileNotFoundError(f"未找到模板文件: {path}")
from openpyxl import load_workbook
patterns = []
with open(path, "r", encoding="utf-8-sig", newline="") as fp:
reader = csv.DictReader(fp)
for row in reader:
wb = load_workbook(path, read_only=True, data_only=True)
ws = wb.active
try:
for row in ws.iter_rows(min_row=2, values_only=True):
if not row:
continue
try:
r = int(row.get("R", "").strip())
g = int(row.get("G", "").strip())
b = int(row.get("B", "").strip())
r = int(row[0]) if row[0] is not None else None
g = int(row[1]) if len(row) > 1 and row[1] is not None else None
b = int(row[2]) if len(row) > 2 and row[2] is not None else None
except Exception:
continue
if r is None or g is None or b is None:
continue
if min(r, g, b) < 0 or max(r, g, b) > 255:
continue
patterns.append((r, g, b))
finally:
wb.close()
if not patterns:
raise RuntimeError("pattern 文件为空或格式不正确,需包含 R,G,B")
raise RuntimeError("模板中未找到有效 RGB 列表(需包含 R/G/B 三列)")
return patterns
def _build_temp_patch(self, rgb):
width, height = get_current_resolution(self.ucd)
temp_dir = os.path.join(tempfile.gettempdir(), "pq_pantone_baseline")
os.makedirs(temp_dir, exist_ok=True)
file_path = os.path.join(temp_dir, "pantone_current_patch.png")
Image.new("RGB", (width, height), rgb).save(file_path, format="PNG")
return file_path
def _start_pantone_baseline(self):
if self._pantone_running:
messagebox.showinfo("提示", "Pantone 任务正在执行")
@@ -213,6 +207,7 @@ def _start_pantone_baseline(self):
try:
self.pantone_patterns = _load_patterns(self)
self._pantone_target_count = len(self.pantone_patterns)
except Exception as exc:
messagebox.showerror("读取失败", str(exc))
return
@@ -228,7 +223,7 @@ def _start_pantone_baseline(self):
self._pantone_control_event = threading.Event()
self._pantone_next_index = 0
self.pantone_status_var.set("执行中")
self.pantone_progress_var.set(f"0 / {_TARGET_RESULT_COUNT}")
self.pantone_progress_var.set(f"0 / {self._pantone_target_count}")
self.pantone_results = []
for item in self.pantone_tree.get_children():
self.pantone_tree.delete(item)
@@ -244,9 +239,6 @@ def _resume_pantone_baseline(self):
if not self._pantone_paused:
messagebox.showinfo("提示", "当前没有可继续的暂停任务")
return
if self._pantone_next_index >= _TARGET_RESULT_COUNT:
messagebox.showinfo("提示", "任务已完成,无需继续")
return
if not getattr(self, "ucd", None) or not self.ucd.status:
messagebox.showwarning("警告", "请先连接 UCD323")
return
@@ -264,10 +256,15 @@ def _resume_pantone_baseline(self):
try:
self.pantone_patterns = _load_patterns(self)
self._pantone_target_count = len(self.pantone_patterns)
except Exception as exc:
messagebox.showerror("读取失败", str(exc))
return
if self._pantone_next_index >= self._pantone_target_count:
messagebox.showinfo("提示", "任务已完成,无需继续")
return
self._pantone_running = True
self._pantone_paused = False
self._pantone_pause_requested = False
@@ -280,13 +277,14 @@ def _resume_pantone_baseline(self):
def _launch_worker(self, start_index, settle):
total = _TARGET_RESULT_COUNT
total = self._pantone_target_count or len(self.pantone_patterns)
def worker():
end_state = "completed"
try:
src = self.pantone_patterns
src_count = len(src)
rgb_session = self.pattern_service.prepare_session("rgb", log_details=False)
self._dispatch_ui(
self.log_gui.log,
f"Pantone 认证摸底启动: source={src_count}, target={total}, start={start_index + 1}",
@@ -301,9 +299,10 @@ def _launch_worker(self, start_index, settle):
break
r, g, b = src[i % src_count]
image_path = _build_temp_patch(self, (r, g, b))
if not send_image_pattern(self.ucd, image_path):
raise RuntimeError(f"{i + 1} 组发送失败")
try:
self.pattern_service.send_rgb((r, g, b), session=rgb_session)
except Exception as exc:
raise RuntimeError(f"{i + 1} 组发送失败: {exc}") from exc
if settle > 0 and self._pantone_control_event is not None:
self._pantone_control_event.clear()
@@ -360,6 +359,19 @@ def _launch_worker(self, start_index, settle):
f"Pantone 任务完成,共 {len(self.pantone_results)} 条数据",
"success",
)
try:
auto_path = _auto_save_template(self)
self._dispatch_ui(
self.log_gui.log,
f"Pantone 模板已自动保存: {auto_path}",
"success",
)
except Exception as exc:
self._dispatch_ui(
self.log_gui.log,
f"Pantone 自动保存模板失败: {exc}",
"error",
)
except Exception as exc:
self._pantone_paused = False
self._dispatch_ui(self.pantone_status_var.set, "执行失败")
@@ -430,7 +442,8 @@ def _clear_results(self):
self._pantone_next_index = 0
for item in self.pantone_tree.get_children():
self.pantone_tree.delete(item)
self.pantone_progress_var.set(f"0 / {_TARGET_RESULT_COUNT}")
self._pantone_target_count = 0
self.pantone_progress_var.set("0 / 0")
self.pantone_status_var.set("结果已清空")
_set_button_states(self)
@@ -447,7 +460,7 @@ def _set_button_states(self):
self.pantone_pause_btn.configure(state=tk.DISABLED)
self.pantone_end_btn.configure(state=tk.NORMAL if (self._pantone_paused or self.pantone_results) else tk.DISABLED)
can_resume = self._pantone_paused and self._pantone_next_index < _TARGET_RESULT_COUNT
can_resume = self._pantone_paused and self._pantone_next_index < self._pantone_target_count
self.pantone_resume_btn.configure(state=tk.NORMAL if can_resume else tk.DISABLED)
@@ -456,7 +469,7 @@ def _save_as_template(self):
messagebox.showinfo("提示", "暂无可导出的结果")
return
default_name = "pantone 2670 colors.xlsx"
default_name = _TEMPLATE_FILE.replace("\xa0", " ")
path = filedialog.asksaveasfilename(
title="另存为 Pantone 模板",
defaultextension=".xlsx",
@@ -466,41 +479,69 @@ def _save_as_template(self):
if not path:
return
# 优先复制 settings 模板,再覆盖数据区;没有模板时自动创建同结构表。
template_path = os.path.join("settings", _TEMPLATE_FILE)
try:
from openpyxl import load_workbook, Workbook
if os.path.isfile(template_path):
wb = load_workbook(template_path)
ws = wb.active
else:
wb = Workbook()
ws = wb.active
ws.title = "Sheet1"
ws.cell(row=1, column=1, value="R")
ws.cell(row=1, column=2, value="G")
ws.cell(row=1, column=3, value="B")
ws.cell(row=1, column=4, value="L")
ws.cell(row=1, column=5, value="x")
ws.cell(row=1, column=6, value="y")
# 清空旧数据
max_row = max(ws.max_row, 2)
for row in range(2, max_row + 1):
for col in range(1, 7):
ws.cell(row=row, column=col, value=None)
for idx, item in enumerate(self.pantone_results, start=2):
ws.cell(row=idx, column=1, value=int(item["r"]))
ws.cell(row=idx, column=2, value=int(item["g"]))
ws.cell(row=idx, column=3, value=int(item["b"]))
ws.cell(row=idx, column=4, value=float(item["l"]))
ws.cell(row=idx, column=5, value=float(item["x"]))
ws.cell(row=idx, column=6, value=float(item["y"]))
wb.save(path)
_write_template_xlsx(self, path)
self.log_gui.log(f"Pantone 模板已保存: {path}", level="success")
self.pantone_status_var.set(f"已保存: {os.path.basename(path)}")
except Exception as exc:
messagebox.showerror("保存失败", f"写入 xlsx 失败: {exc}")
def _resolve_results_dir(self):
if getattr(self, "config_file", None):
root_dir = os.path.dirname(os.path.dirname(self.config_file))
else:
root_dir = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
)
results_dir = os.path.join(root_dir, "results")
os.makedirs(results_dir, exist_ok=True)
return results_dir
def _auto_save_template(self):
results_dir = _resolve_results_dir(self)
target_count = len(self.pantone_results)
filename = (
f"pantone_{target_count}_baseline_"
f"{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
)
path = os.path.join(results_dir, filename)
_write_template_xlsx(self, path)
return path
def _write_template_xlsx(self, path):
# 优先复制 settings 模板,再覆盖数据区;没有模板时自动创建同结构表。
template_path = os.path.join("settings", _TEMPLATE_FILE)
from openpyxl import load_workbook, Workbook
if os.path.isfile(template_path):
wb = load_workbook(template_path)
ws = wb.active
else:
wb = Workbook()
ws = wb.active
ws.title = "Sheet1"
ws.cell(row=1, column=1, value="R")
ws.cell(row=1, column=2, value="G")
ws.cell(row=1, column=3, value="B")
ws.cell(row=1, column=4, value="L")
ws.cell(row=1, column=5, value="x")
ws.cell(row=1, column=6, value="y")
# 清空旧数据
max_row = max(ws.max_row, 2)
for row in range(2, max_row + 1):
for col in range(1, 7):
ws.cell(row=row, column=col, value=None)
for idx, item in enumerate(self.pantone_results, start=2):
ws.cell(row=idx, column=1, value=int(item["r"]))
ws.cell(row=idx, column=2, value=int(item["g"]))
ws.cell(row=idx, column=3, value=int(item["b"]))
ws.cell(row=idx, column=4, value=float(item["l"]))
ws.cell(row=idx, column=5, value=float(item["x"]))
ws.cell(row=idx, column=6, value=float(item["y"]))
wb.save(path)

View File

@@ -768,28 +768,25 @@ class PQDebugPanel:
# 禁用按钮
self._disable_test_button(test_type, test_item)
# 根据测试类型设置信号格式
self._setup_signal_format(test_type)
# 获取图案索引并发送
if test_item in ["gamma", "eotf"]:
pattern_index = self.get_gray_index(selected)
self.app.config.set_current_pattern("gray")
pattern_mode = "gray"
elif test_item == "accuracy":
pattern_index = self.get_color_index(selected)
self.app.config.set_current_pattern("accuracy")
pattern_mode = "accuracy"
elif test_item == "rgb":
pattern_index = self.get_rgb_index(selected)
self.app.config.set_current_pattern("rgb")
pattern_mode = "rgb"
else:
raise ValueError(f"不支持的测试项目: {test_item}")
# 设置图案
self.app.ucd.set_ucd_params(self.app.config)
# 跳转到目标图案
for i in range(pattern_index + 1):
self.app.ucd.set_next_pattern()
self.app.ucd.run()
session = self.app.pattern_service.prepare_session(
pattern_mode,
test_type=test_type,
log_details=False,
)
self.app.pattern_service.send_session_pattern(session, pattern_index)
time.sleep(1.5)
# 测量数据
@@ -815,28 +812,6 @@ class PQDebugPanel:
self.app.log_gui.log(traceback.format_exc(), level="error")
self._enable_test_button(test_type, test_item)
def _setup_signal_format(self, test_type):
"""设置信号格式"""
if test_type == "screen_module":
self.app.ucd.set_ucd_params(self.app.config)
elif test_type == "sdr_movie":
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(),
)
elif test_type == "hdr_movie":
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(),
)
def _compare_and_display(self, test_type, test_item, selected, new_data):
"""对比数据并显示"""
# 获取原始数据