重构移动utils文件夹

This commit is contained in:
xinzhu.yin
2026-04-20 11:48:38 +08:00
parent b6c1c2ab93
commit 2e92b48496
27 changed files with 2866 additions and 3085 deletions

View File

@@ -1,127 +1,214 @@
"""Local Dimming 测试逻辑(Step 4 重构)。
"""Local Dimming 测试逻辑(应用层)。
从 pqAutomationApp.PQAutomationApp 中搬迁。每个函数第一行 `self = app`
以保留原有 `self.xxx` 属性访问不变
整合自原 drivers/local_dimming_test.py窗口图片生成与测试主循环
直接落在本模块UCD 通用操作下沉到 drivers.ucd_helpers
"""
import atexit
import csv
import datetime
import os
import shutil
import sys
import threading
import time
import tkinter as tk
from tkinter import filedialog, messagebox
import numpy as np
from PIL import Image
from drivers.ucd_helpers import get_current_resolution, send_image_pattern
# --------------------------------------------------------------------------
# 模块级常量与窗口图片缓存
# --------------------------------------------------------------------------
DEFAULT_WINDOW_PERCENTAGES = [1, 2, 5, 10, 18, 25, 50, 75, 100]
_TEMP_DIR = None
_IMAGE_CACHE = {} # {(width, height, percentage): file_path}
def _cleanup_temp_dir():
global _TEMP_DIR
if _TEMP_DIR and os.path.exists(_TEMP_DIR):
try:
shutil.rmtree(_TEMP_DIR)
except Exception:
pass
_TEMP_DIR = None
_IMAGE_CACHE.clear()
def _get_temp_dir():
global _TEMP_DIR
if _TEMP_DIR is None:
if getattr(sys, "frozen", False):
base = os.path.dirname(sys.executable)
else:
base = os.getcwd()
_TEMP_DIR = os.path.join(base, "temp_local_dimming")
os.makedirs(_TEMP_DIR, exist_ok=True)
atexit.register(_cleanup_temp_dir)
return _TEMP_DIR
def _make_window_image_array(width, height, percentage):
"""生成黑底+居中白窗的 numpy 图像,保持屏幕比例。"""
image = np.zeros((height, width, 3), dtype=np.uint8)
if percentage >= 100:
ww, wh = width, height
else:
scale = (percentage / 100.0) ** 0.5
ww = int(width * scale)
wh = int(height * scale)
x1 = (width - ww) // 2
y1 = (height - wh) // 2
image[y1:y1 + wh, x1:x1 + ww] = 255
return image
def _ensure_window_image(width, height, percentage):
"""生成或复用缓存的窗口 PNG 文件,返回路径。"""
key = (width, height, percentage)
cached = _IMAGE_CACHE.get(key)
if cached and os.path.exists(cached):
return cached
arr = _make_window_image_array(width, height, percentage)
fname = f"window_{width}x{height}_{percentage:03d}percent.png"
path = os.path.join(_get_temp_dir(), fname)
Image.fromarray(arr, mode="RGB").save(path, format="PNG")
_IMAGE_CACHE[key] = path
return path
# --------------------------------------------------------------------------
# GUI 入口(绑定为 PQAutomationApp 方法)
# --------------------------------------------------------------------------
def start_local_dimming_test(self):
"""开始 Local Dimming 测试"""
# 检查设备连接
"""开始 Local Dimming 测试"""
if not self.ca or not self.ucd.status:
messagebox.showerror("错误", "请先连接 CA410 和 UCD323")
return
# 禁用按钮
self.ld_start_btn.config(state=tk.DISABLED)
self.ld_stop_btn.config(state=tk.NORMAL)
self.ld_save_btn.config(state=tk.DISABLED)
# 清空结果
for item in self.ld_tree.get_children():
self.ld_tree.delete(item)
# 获取配置
wait_time = float(self.ld_wait_time_var.get())
stop_event = threading.Event()
self.ld_stop_event = stop_event
# 在新线程中执行测试
def run_test():
from utils.local_dimming_test import LocalDimmingTest, LocalDimmingController
def worker():
log = self.log_gui.log
log("=" * 60)
log("开始 Local Dimming 测试")
log("=" * 60)
# 从设备当前 timing 获取分辨率
ld_ctrl = LocalDimmingController(self.ucd)
cur_w, cur_h = ld_ctrl.get_current_resolution()
resolution = f"{cur_w}x{cur_h}"
width, height = get_current_resolution(self.ucd)
total = len(DEFAULT_WINDOW_PERCENTAGES)
log(f" 分辨率: {width}x{height}")
log(f" 测试窗口: {DEFAULT_WINDOW_PERCENTAGES}")
log(f" 等待时间: {wait_time}")
ld_test = LocalDimmingTest(
self.ucd,
self.ca,
log_callback=self.log_gui.log,
)
results = []
for i, percentage in enumerate(DEFAULT_WINDOW_PERCENTAGES, 1):
if stop_event.is_set():
log("⚠️ 测试已停止")
break
ld_test.wait_time = wait_time
log(f"[{i}/{total}] 测试 {percentage}% 窗口...")
try:
image_path = _ensure_window_image(width, height, percentage)
except Exception as e:
log(f" ❌ 图像生成失败: {e}")
continue
results = ld_test.run_test(resolution=resolution)
if not send_image_pattern(self.ucd, image_path):
log(f"{percentage}% 窗口发送失败,跳过")
continue
log(f" ⏳ 等待 {wait_time} 秒...")
time.sleep(wait_time)
try:
x, y, lv, _X, _Y, _Z = self.ca.readAllDisplay()
except Exception as e:
log(f" ❌ 采集亮度异常: {e}")
continue
if lv is None:
log(f"{percentage}% 窗口采集失败")
continue
log(f" ✓ 采集亮度: {lv:.2f} cd/m²")
results.append((percentage, x, y, lv, _X, _Y, _Z))
log("=" * 60)
log(f"✅ Local Dimming 测试完成 ({len(results)}/{total})")
log("=" * 60)
# 保存到实例变量
self.ld_test_instance = ld_test
self.ld_test_results = results
# 更新结果显示
self.root.after(0, lambda: self.update_ld_results(results))
# 清理临时文件
ld_test.cleanup()
# 恢复按钮状态
self.root.after(0, lambda: self.ld_start_btn.config(state=tk.NORMAL))
self.root.after(0, lambda: self.ld_stop_btn.config(state=tk.DISABLED))
self.root.after(0, lambda: self.ld_save_btn.config(state=tk.NORMAL))
threading.Thread(target=run_test, daemon=True).start()
threading.Thread(target=worker, daemon=True).start()
def update_ld_results(self, results):
"""更新 Local Dimming 结果显示"""
for percentage, x, y, lv, X, Y, Z in results:
"""把批量测试结果填入 Treeview。"""
for percentage, x, y, lv, _X, _Y, _Z in results:
self.ld_tree.insert(
"",
tk.END,
"", tk.END,
values=(f"{percentage}%", f"{lv:.2f}", f"{x:.4f}", f"{y:.4f}"),
)
def stop_local_dimming_test(self):
"""停止 Local Dimming 测试"""
if hasattr(self, "ld_test_instance"):
self.ld_test_instance.stop()
"""请求停止当前 Local Dimming 测试"""
ev = getattr(self, "ld_stop_event", None)
if ev:
ev.set()
def send_ld_window(self, percentage):
"""发送指定百分比的窗口"""
"""发送指定百分比的白色窗口(手动模式)。"""
if not self.ucd.status:
messagebox.showwarning("警告", "请先连接 UCD323 设备")
return
self.log_gui.log(f"🔆 发送 {percentage}% 窗口...")
# 记录当前百分比(用于测量)
self.current_ld_percentage = percentage
def send():
from utils.local_dimming_test import LocalDimmingController
ld_controller = LocalDimmingController(self.ucd)
# 从设备当前 timing 获取分辨率
width, height = ld_controller.get_current_resolution()
# 生成并发送图片
success = ld_controller.send_window_pattern_with_resolution(
percentage, width, height
width, height = get_current_resolution(self.ucd)
try:
image_path = _ensure_window_image(width, height, percentage)
except Exception as e:
self.root.after(0, lambda: self.log_gui.log(f"❌ 图像生成失败: {e}"))
return
ok = send_image_pattern(self.ucd, image_path)
msg = (
f"{percentage}% 窗口已发送" if ok
else f"{percentage}% 窗口发送失败"
)
if success:
self.root.after(
0, lambda: self.log_gui.log(f"{percentage}% 窗口已发送")
)
else:
self.root.after(
0, lambda: self.log_gui.log(f"{percentage}% 窗口发送失败")
)
self.root.after(0, lambda: self.log_gui.log(msg))
threading.Thread(target=send, daemon=True).start()
def measure_ld_luminance(self):
"""测量当前亮度"""
"""测量当前显示的亮度并追加一行到 Treeview。"""
if not self.ca:
messagebox.showwarning("警告", "请先连接 CA410 色度计")
return
if self.current_ld_percentage is None:
messagebox.showinfo("提示", "请先发送一个窗口图案")
return
@@ -130,51 +217,31 @@ def measure_ld_luminance(self):
def measure():
try:
x, y, lv, X, Y, Z = self.ca.readAllDisplay()
if lv is not None:
import datetime
timestamp = datetime.datetime.now().strftime("%H:%M:%S")
# 更新显示
self.root.after(
0,
lambda: self.ld_result_label.config(
text=f"亮度: {lv:.2f} cd/m² | x: {x:.4f} | y: {y:.4f}"
),
)
# 添加到表格
self.root.after(
0,
lambda: self.ld_tree.insert(
"",
tk.END,
values=(
f"{self.current_ld_percentage}%",
f"{lv:.2f}",
f"{x:.4f}",
f"{y:.4f}",
timestamp,
),
),
)
self.root.after(
0, lambda: self.log_gui.log(f"✅ 采集完成: {lv:.2f} cd/m²")
)
else:
self.root.after(0, lambda: self.log_gui.log("❌ 采集失败"))
x, y, lv, _X, _Y, _Z = self.ca.readAllDisplay()
except Exception as e:
self.root.after(0, lambda: self.log_gui.log(f"❌ 采集异常: {str(e)}"))
return
if lv is None:
self.root.after(0, lambda: self.log_gui.log("❌ 采集失败"))
return
timestamp = datetime.datetime.now().strftime("%H:%M:%S")
self.root.after(0, lambda: self.ld_result_label.config(
text=f"亮度: {lv:.2f} cd/m² | x: {x:.4f} | y: {y:.4f}"
))
self.root.after(0, lambda: self.ld_tree.insert(
"", tk.END,
values=(
f"{self.current_ld_percentage}%",
f"{lv:.2f}", f"{x:.4f}", f"{y:.4f}", timestamp,
),
))
self.root.after(0, lambda: self.log_gui.log(f"✅ 采集完成: {lv:.2f} cd/m²"))
threading.Thread(target=measure, daemon=True).start()
def clear_ld_records(self):
"""清空测试记录"""
"""清空 Treeview 中的测试记录"""
for item in self.ld_tree.get_children():
self.ld_tree.delete(item)
self.ld_result_label.config(text="亮度: -- cd/m² | x: -- | y: --")
@@ -183,24 +250,20 @@ def clear_ld_records(self):
def save_local_dimming_results(self):
"""保存 Local Dimming 结果"""
from tkinter import filedialog
import csv
import datetime
"""把 Treeview 中的全部记录导出为 CSV。"""
if len(self.ld_tree.get_children()) == 0:
messagebox.showinfo("提示", "没有可保存的数据")
return
default_name = f"LocalDimming_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
default_name = (
f"LocalDimming_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
)
save_path = filedialog.asksaveasfilename(
title="保存测试结果",
initialfile=default_name,
defaultextension=".csv",
filetypes=[("CSV 文件", "*.csv"), ("所有文件", "*.*")],
)
if not save_path:
return
@@ -208,14 +271,10 @@ def save_local_dimming_results(self):
with open(save_path, "w", newline="", encoding="utf-8-sig") as f:
writer = csv.writer(f)
writer.writerow(["窗口百分比", "亮度 (cd/m²)", "x", "y", "时间"])
for item in self.ld_tree.get_children():
values = self.ld_tree.item(item, "values")
writer.writerow(values)
writer.writerow(self.ld_tree.item(item, "values"))
self.log_gui.log(f"✓ 测试结果已保存: {save_path}")
messagebox.showinfo("成功", f"测试结果已保存到:\n{save_path}")
except Exception as e:
self.log_gui.log(f"❌ 保存失败: {str(e)}")
messagebox.showerror("错误", f"保存失败: {str(e)}")