修改摸底测试功能、修改pattern控制逻辑
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
"""对比数据并显示"""
|
||||
# 获取原始数据
|
||||
|
||||
Reference in New Issue
Block a user