修复LocalDimming测试错误
This commit is contained in:
@@ -157,36 +157,6 @@ def _ensure_checkerboard_image(width, height, grid_size, center_white):
|
||||
_IMAGE_CACHE[key] = path
|
||||
return path
|
||||
|
||||
|
||||
def _build_ld_result_row(test_item, pattern_label, value, x="--", y="--"):
|
||||
timestamp = datetime.datetime.now().strftime("%H:%M:%S")
|
||||
if isinstance(value, (int, float, np.floating)):
|
||||
display_value = f"{float(value):.4f}"
|
||||
else:
|
||||
display_value = str(value)
|
||||
|
||||
return {
|
||||
"test_item": test_item,
|
||||
"pattern": pattern_label,
|
||||
"value": display_value,
|
||||
"x": x if isinstance(x, str) else f"{x:.4f}",
|
||||
"y": y if isinstance(y, str) else f"{y:.4f}",
|
||||
"time": timestamp,
|
||||
}
|
||||
|
||||
|
||||
def _measure_ld_row(self: "PQAutomationApp", test_item, pattern_label):
|
||||
"""读取一次 CA410 数据并包装为表格行。"""
|
||||
x, y, lv, _X, _Y, _Z = self.read_ca_xyLv()
|
||||
if lv is None:
|
||||
raise RuntimeError(f"{pattern_label} 采集失败")
|
||||
return _build_ld_result_row(test_item, pattern_label, lv, x, y), lv
|
||||
|
||||
|
||||
def _send_ld_image(self: "PQAutomationApp", image_path):
|
||||
self.signal_service.send_image(image_path)
|
||||
|
||||
|
||||
def _apply_ld_ucd_params(self: "PQAutomationApp") -> bool:
|
||||
"""发送 Local Dimming 图案前,按当前测试类型写入 UCD 参数。"""
|
||||
test_type = getattr(self.config, "current_test_type", "screen_module")
|
||||
@@ -315,146 +285,95 @@ def _apply_ld_ucd_params(self: "PQAutomationApp") -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def _run_ld_measurement_step(self: "PQAutomationApp", width, height, wait_time, step, log):
|
||||
label = step["label"]
|
||||
test_item = step["test_item"]
|
||||
kind = step["kind"]
|
||||
|
||||
if kind == "window":
|
||||
percentage = step["percentage"]
|
||||
image_path = _ensure_window_image(width, height, percentage)
|
||||
_send_ld_image(self, image_path)
|
||||
settle_time = wait_time
|
||||
elif kind == "black":
|
||||
image_path = _ensure_solid_image(width, height, (0, 0, 0), "black")
|
||||
_send_ld_image(self, image_path)
|
||||
settle_time = wait_time
|
||||
elif kind == "checkerboard":
|
||||
image_path = _ensure_checkerboard_image(
|
||||
width,
|
||||
height,
|
||||
DEFAULT_CHESSBOARD_GRID,
|
||||
step["center_white"],
|
||||
)
|
||||
_send_ld_image(self, image_path)
|
||||
settle_time = wait_time
|
||||
elif kind == "instant_peak":
|
||||
black_image = _ensure_solid_image(width, height, (0, 0, 0), "black")
|
||||
peak_image = _ensure_window_image(
|
||||
width,
|
||||
height,
|
||||
step["percentage"],
|
||||
)
|
||||
_send_ld_image(self, black_image)
|
||||
log(f" 黑场预置 {wait_time:.1f} 秒", level="info")
|
||||
time.sleep(wait_time)
|
||||
_send_ld_image(self, peak_image)
|
||||
settle_time = min(wait_time, INSTANT_PEAK_CAPTURE_DELAY)
|
||||
else:
|
||||
raise ValueError(f"未知 Local Dimming 测试步骤: {kind}")
|
||||
|
||||
log(f" 等待 {settle_time:.1f} 秒后采集...", level="info")
|
||||
time.sleep(settle_time)
|
||||
return _measure_ld_row(self, test_item, label)
|
||||
|
||||
|
||||
def _set_current_ld_pattern(self: "PQAutomationApp", test_item, pattern_label, percentage=None):
|
||||
self.current_ld_test_item = test_item
|
||||
self.current_ld_pattern_label = pattern_label
|
||||
self.current_ld_percentage = percentage
|
||||
|
||||
def _send_ld_pattern_async(self: "PQAutomationApp", image_builder, success_msg, fail_msg):
|
||||
"""统一的 Local Dimming 图案发送线程"""
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# GUI 入口(绑定为 PQAutomationApp 方法)
|
||||
# --------------------------------------------------------------------------
|
||||
def worker():
|
||||
|
||||
def start_local_dimming_test(self: "PQAutomationApp"):
|
||||
"""Local Dimming 不再提供自动测试,保留接口仅提示用户使用手动模式。"""
|
||||
messagebox.showinfo("提示", "Local Dimming 请使用手动发送图案后再采集亮度")
|
||||
|
||||
|
||||
def update_ld_results(self: "PQAutomationApp", results):
|
||||
"""把批量测试结果填入 Treeview。"""
|
||||
for row in results:
|
||||
self.ld_tree.insert(
|
||||
"", tk.END,
|
||||
values=(
|
||||
row["test_item"],
|
||||
row["pattern"],
|
||||
row["value"],
|
||||
row["x"],
|
||||
row["y"],
|
||||
row["time"],
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def stop_local_dimming_test(self: "PQAutomationApp"):
|
||||
"""兼容旧接口,无操作。"""
|
||||
return
|
||||
|
||||
|
||||
def send_ld_window(self: "PQAutomationApp", percentage):
|
||||
"""发送指定百分比的白色窗口(手动模式)。"""
|
||||
if not self.signal_service.is_connected:
|
||||
messagebox.showwarning("警告", "请先连接 UCD323 设备")
|
||||
return
|
||||
|
||||
try:
|
||||
luminance_percent = float(
|
||||
self.ld_window_luminance_var.get()
|
||||
if hasattr(self, "ld_window_luminance_var")
|
||||
else 100
|
||||
)
|
||||
if luminance_percent < 1 or luminance_percent > 100:
|
||||
raise ValueError("亮度范围应为 1-100")
|
||||
except Exception as e:
|
||||
messagebox.showwarning("参数错误", f"窗口亮度参数无效: {e}")
|
||||
return
|
||||
|
||||
window_level = int(round(luminance_percent / 100.0 * 255.0))
|
||||
|
||||
self.log_gui.log(
|
||||
f"🔆 发送 {percentage}% 窗口(亮度{luminance_percent:.0f}%)...",
|
||||
level="info",
|
||||
)
|
||||
_set_current_ld_pattern(
|
||||
self,
|
||||
"峰值亮度",
|
||||
f"{percentage}%窗口({luminance_percent:.0f}%亮度)",
|
||||
percentage,
|
||||
)
|
||||
|
||||
def send():
|
||||
if not _apply_ld_ucd_params(self):
|
||||
return
|
||||
|
||||
width, height = self.signal_service.current_resolution()
|
||||
|
||||
try:
|
||||
image_path = _ensure_window_image(
|
||||
width,
|
||||
height,
|
||||
percentage,
|
||||
window_level,
|
||||
)
|
||||
image_path = image_builder(width, height)
|
||||
except Exception as e:
|
||||
self._dispatch_ui(self.log_gui.log, f"图像生成失败: {e}")
|
||||
return
|
||||
|
||||
try:
|
||||
self.signal_service.send_image(image_path)
|
||||
ok = True
|
||||
except Exception:
|
||||
ok = False
|
||||
msg = (
|
||||
f"{percentage}% 窗口({luminance_percent:.0f}%亮度)已发送" if ok
|
||||
else f"{percentage}% 窗口({luminance_percent:.0f}%亮度)发送失败"
|
||||
)
|
||||
|
||||
msg = success_msg if ok else fail_msg
|
||||
self._dispatch_ui(self.log_gui.log, msg)
|
||||
|
||||
threading.Thread(target=send, daemon=True).start()
|
||||
threading.Thread(target=worker, daemon=True).start()
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
# GUI 入口(绑定为 PQAutomationApp 方法)
|
||||
# --------------------------------------------------------------------------
|
||||
def send_ld_window(self: "PQAutomationApp", percentage):
|
||||
|
||||
FIXED_WINDOW_PERCENTAGE = 40
|
||||
|
||||
try:
|
||||
luminance_percent = float(percentage)
|
||||
if luminance_percent < 1 or luminance_percent > 100:
|
||||
raise ValueError
|
||||
except Exception:
|
||||
messagebox.showwarning("参数错误", "亮度范围应为 1-100")
|
||||
return
|
||||
|
||||
if not self.signal_service.is_connected:
|
||||
messagebox.showwarning("警告", "请先连接 UCD323 设备")
|
||||
return
|
||||
|
||||
window_level = int(round(luminance_percent / 100 * 255))
|
||||
|
||||
self.log_gui.log(
|
||||
f"发送 {FIXED_WINDOW_PERCENTAGE}%窗口(亮度{luminance_percent:.0f}%)...",
|
||||
level="info",
|
||||
)
|
||||
|
||||
_set_current_ld_pattern(
|
||||
self,
|
||||
"峰值亮度",
|
||||
f"{FIXED_WINDOW_PERCENTAGE}%窗口({luminance_percent:.0f}%亮度)",
|
||||
FIXED_WINDOW_PERCENTAGE,
|
||||
)
|
||||
|
||||
def builder(width, height):
|
||||
return _ensure_window_image(
|
||||
width,
|
||||
height,
|
||||
FIXED_WINDOW_PERCENTAGE,
|
||||
window_level,
|
||||
)
|
||||
|
||||
_send_ld_pattern_async(
|
||||
self,
|
||||
builder,
|
||||
f"{FIXED_WINDOW_PERCENTAGE}%窗口({luminance_percent:.0f}%亮度)已发送",
|
||||
f"{FIXED_WINDOW_PERCENTAGE}%窗口({luminance_percent:.0f}%亮度)发送失败",
|
||||
)
|
||||
|
||||
|
||||
def send_ld_manual_window(self: "PQAutomationApp"):
|
||||
"""按手动输入的窗口百分比和亮度直接发送窗口图案。"""
|
||||
"""按手动输入的窗口大小和亮度发送窗口图案。"""
|
||||
|
||||
if not self.signal_service.is_connected:
|
||||
messagebox.showwarning("警告", "请先连接 UCD323 设备")
|
||||
return
|
||||
|
||||
try:
|
||||
percentage = int(float(self.ld_window_percentage_var.get()))
|
||||
if percentage < 1 or percentage > 100:
|
||||
@@ -463,133 +382,104 @@ def send_ld_manual_window(self: "PQAutomationApp"):
|
||||
messagebox.showwarning("参数错误", f"窗口百分比无效: {e}")
|
||||
return
|
||||
|
||||
self.send_ld_window(percentage)
|
||||
try:
|
||||
luminance_percent = float(self.ld_window_luminance_var.get())
|
||||
if luminance_percent < 1 or luminance_percent > 100:
|
||||
raise ValueError("亮度范围应为 1-100")
|
||||
except Exception as e:
|
||||
messagebox.showwarning("参数错误", f"窗口亮度无效: {e}")
|
||||
return
|
||||
|
||||
window_level = int(round(luminance_percent / 100.0 * 255.0))
|
||||
|
||||
self.log_gui.log(
|
||||
f"发送 {percentage}%窗口(亮度{luminance_percent:.0f}%)...",
|
||||
level="info",
|
||||
)
|
||||
|
||||
_set_current_ld_pattern(
|
||||
self,
|
||||
"峰值亮度",
|
||||
f"{percentage}%窗口({luminance_percent:.0f}%亮度)",
|
||||
percentage,
|
||||
)
|
||||
|
||||
def builder(width, height):
|
||||
return _ensure_window_image(
|
||||
width,
|
||||
height,
|
||||
percentage,
|
||||
window_level,
|
||||
)
|
||||
|
||||
_send_ld_pattern_async(
|
||||
self,
|
||||
builder,
|
||||
f"{percentage}%窗口({luminance_percent:.0f}%亮度)已发送",
|
||||
f"{percentage}%窗口({luminance_percent:.0f}%亮度)发送失败",
|
||||
)
|
||||
|
||||
|
||||
def send_ld_checkerboard(self: "PQAutomationApp", center_white):
|
||||
"""发送棋盘格图案(手动模式)。"""
|
||||
|
||||
if not self.signal_service.is_connected:
|
||||
messagebox.showwarning("警告", "请先连接 UCD323 设备")
|
||||
return
|
||||
|
||||
pattern_label = "棋盘格(中心白)" if center_white else "棋盘格(中心黑)"
|
||||
self.log_gui.log(f"🔲 发送 {pattern_label}...", level="info")
|
||||
|
||||
self.log_gui.log(f"发送 {pattern_label}...", level="info")
|
||||
|
||||
_set_current_ld_pattern(self, "棋盘格对比度", pattern_label)
|
||||
|
||||
def send():
|
||||
if not _apply_ld_ucd_params(self):
|
||||
return
|
||||
width, height = self.signal_service.current_resolution()
|
||||
try:
|
||||
image_path = _ensure_checkerboard_image(
|
||||
width,
|
||||
height,
|
||||
DEFAULT_CHESSBOARD_GRID,
|
||||
center_white,
|
||||
)
|
||||
except Exception as e:
|
||||
self._dispatch_ui(self.log_gui.log, f"图像生成失败: {e}")
|
||||
return
|
||||
def builder(width, height):
|
||||
return _ensure_checkerboard_image(
|
||||
width,
|
||||
height,
|
||||
DEFAULT_CHESSBOARD_GRID,
|
||||
center_white,
|
||||
)
|
||||
|
||||
try:
|
||||
self.signal_service.send_image(image_path)
|
||||
ok = True
|
||||
except Exception:
|
||||
ok = False
|
||||
|
||||
msg = f"{pattern_label} 已发送" if ok else f"{pattern_label} 发送失败"
|
||||
self._dispatch_ui(self.log_gui.log, msg)
|
||||
|
||||
threading.Thread(target=send, daemon=True).start()
|
||||
_send_ld_pattern_async(
|
||||
self,
|
||||
builder,
|
||||
f"{pattern_label} 已发送",
|
||||
f"{pattern_label} 发送失败",
|
||||
)
|
||||
|
||||
|
||||
def send_ld_black_pattern(self: "PQAutomationApp"):
|
||||
"""发送全黑图案(手动模式)。"""
|
||||
|
||||
if not self.signal_service.is_connected:
|
||||
messagebox.showwarning("警告", "请先连接 UCD323 设备")
|
||||
return
|
||||
|
||||
self.log_gui.log("⚫ 发送全黑画面...", level="info")
|
||||
self.log_gui.log("发送全黑画面...", level="info")
|
||||
|
||||
_set_current_ld_pattern(self, "黑电平", "全黑画面")
|
||||
|
||||
def send():
|
||||
if not _apply_ld_ucd_params(self):
|
||||
return
|
||||
width, height = self.signal_service.current_resolution()
|
||||
try:
|
||||
image_path = _ensure_solid_image(width, height, (0, 0, 0), "black")
|
||||
except Exception as e:
|
||||
self._dispatch_ui(self.log_gui.log, f"图像生成失败: {e}")
|
||||
return
|
||||
def builder(width, height):
|
||||
return _ensure_solid_image(width, height, (0, 0, 0), "black")
|
||||
|
||||
try:
|
||||
self.signal_service.send_image(image_path)
|
||||
ok = True
|
||||
except Exception:
|
||||
ok = False
|
||||
|
||||
msg = "全黑画面已发送" if ok else "全黑画面发送失败"
|
||||
self._dispatch_ui(self.log_gui.log, msg)
|
||||
|
||||
threading.Thread(target=send, daemon=True).start()
|
||||
|
||||
|
||||
def send_ld_instant_peak(self: "PQAutomationApp"):
|
||||
"""发送瞬时峰值亮度图案:先黑场,再切到 10% 窗口并保持。"""
|
||||
if not self.signal_service.is_connected:
|
||||
messagebox.showwarning("警告", "请先连接 UCD323 设备")
|
||||
return
|
||||
|
||||
pattern_label = f"黑场后切 {INSTANT_PEAK_WINDOW_PERCENTAGE}%窗口"
|
||||
self.log_gui.log(f"⚡ 发送瞬时峰值图案: {pattern_label}", level="info")
|
||||
_set_current_ld_pattern(
|
||||
_send_ld_pattern_async(
|
||||
self,
|
||||
"瞬时峰值亮度",
|
||||
pattern_label,
|
||||
INSTANT_PEAK_WINDOW_PERCENTAGE,
|
||||
builder,
|
||||
"全黑画面已发送",
|
||||
"全黑画面发送失败",
|
||||
)
|
||||
|
||||
def send():
|
||||
if not _apply_ld_ucd_params(self):
|
||||
return
|
||||
width, height = self.signal_service.current_resolution()
|
||||
try:
|
||||
black_image = _ensure_solid_image(width, height, (0, 0, 0), "black")
|
||||
peak_image = _ensure_window_image(
|
||||
width,
|
||||
height,
|
||||
INSTANT_PEAK_WINDOW_PERCENTAGE,
|
||||
)
|
||||
except Exception as e:
|
||||
self._dispatch_ui(self.log_gui.log, f"图像生成失败: {e}")
|
||||
return
|
||||
|
||||
try:
|
||||
self.signal_service.send_image(black_image)
|
||||
time.sleep(INSTANT_PEAK_CAPTURE_DELAY)
|
||||
self.signal_service.send_image(peak_image)
|
||||
ok = True
|
||||
except Exception:
|
||||
ok = False
|
||||
|
||||
msg = (
|
||||
f"瞬时峰值图案已发送,当前保持 {INSTANT_PEAK_WINDOW_PERCENTAGE}%窗口"
|
||||
if ok else
|
||||
"瞬时峰值图案发送失败"
|
||||
)
|
||||
self._dispatch_ui(self.log_gui.log, msg)
|
||||
|
||||
threading.Thread(target=send, daemon=True).start()
|
||||
|
||||
|
||||
def start_ld_instant_peak_tracking(self: "PQAutomationApp"):
|
||||
"""独立瞬时峰值测试:持续采样直到亮度回落或达到最长测量时长。"""
|
||||
|
||||
if not self.signal_service.is_connected:
|
||||
messagebox.showwarning("警告", "请先连接 UCD323 设备")
|
||||
return
|
||||
|
||||
if not self.ca:
|
||||
messagebox.showwarning("警告", "请先连接 CA410 色度计")
|
||||
return
|
||||
|
||||
if getattr(self, "ld_peak_tracking", False):
|
||||
messagebox.showinfo("提示", "瞬时峰值测试正在进行中")
|
||||
return
|
||||
@@ -603,38 +493,72 @@ def start_ld_instant_peak_tracking(self: "PQAutomationApp"):
|
||||
if window_luminance_percent < 1 or window_luminance_percent > 100:
|
||||
raise ValueError("窗口亮度超出范围")
|
||||
|
||||
max_duration = float(self.ld_peak_duration_var.get())
|
||||
if max_duration <= 0:
|
||||
raise ValueError("测量时长必须大于 0")
|
||||
|
||||
sample_interval = float(
|
||||
self.ld_peak_sample_interval_var.get()
|
||||
if hasattr(self, "ld_peak_sample_interval_var")
|
||||
else INSTANT_PEAK_SAMPLE_INTERVAL
|
||||
)
|
||||
|
||||
if sample_interval <= 0:
|
||||
raise ValueError("采样间隔必须大于 0")
|
||||
|
||||
# 无限模式
|
||||
no_limit = bool(
|
||||
self.ld_peak_no_limit_var.get()
|
||||
if hasattr(self, "ld_peak_no_limit_var")
|
||||
else False
|
||||
)
|
||||
|
||||
if not no_limit:
|
||||
max_duration = float(self.ld_peak_duration_var.get())
|
||||
if max_duration <= 0:
|
||||
raise ValueError("测量时长必须大于 0")
|
||||
else:
|
||||
max_duration = None
|
||||
|
||||
# 回落百分比
|
||||
drop_percent = float(
|
||||
self.ld_peak_drop_percent_var.get()
|
||||
if hasattr(self, "ld_peak_drop_percent_var")
|
||||
else 3
|
||||
)
|
||||
|
||||
if drop_percent <= 0 or drop_percent >= 50:
|
||||
raise ValueError("回落百分比建议 1~50")
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showwarning("参数错误", f"请检查瞬时峰值参数: {e}")
|
||||
return
|
||||
|
||||
record_curve = bool(self.ld_peak_record_curve_var.get())
|
||||
window_level = int(round(window_luminance_percent / 100.0 * 255.0))
|
||||
|
||||
pattern_label = f"黑场后切 {window_percentage}%窗口({window_luminance_percent:.0f}%亮度)"
|
||||
|
||||
duration_text = "直到亮度回落" if no_limit else f"最长 {max_duration:.1f}s"
|
||||
|
||||
self.ld_peak_tracking = True
|
||||
|
||||
self.log_gui.log(
|
||||
f"⚡ 开始独立瞬时峰值测试: {pattern_label},最长 {max_duration:.1f}s",
|
||||
f"开始瞬时峰值测试: {pattern_label},{duration_text},回落阈值 {drop_percent:.1f}%",
|
||||
level="info",
|
||||
)
|
||||
_set_current_ld_pattern(self, "瞬时峰值亮度", pattern_label, window_percentage)
|
||||
|
||||
_set_current_ld_pattern(
|
||||
self,
|
||||
"瞬时峰值亮度",
|
||||
pattern_label,
|
||||
window_percentage,
|
||||
)
|
||||
|
||||
if hasattr(self, "ld_peak_start_btn"):
|
||||
self.ld_peak_start_btn.configure(state="disabled")
|
||||
|
||||
if hasattr(self, "ld_peak_stop_btn"):
|
||||
self.ld_peak_stop_btn.configure(state="normal")
|
||||
|
||||
def run():
|
||||
|
||||
peak_lv = None
|
||||
peak_time = None
|
||||
drop_time = None
|
||||
@@ -645,6 +569,7 @@ def start_ld_instant_peak_tracking(self: "PQAutomationApp"):
|
||||
return
|
||||
|
||||
width, height = self.signal_service.current_resolution()
|
||||
|
||||
black_image = _ensure_solid_image(width, height, (0, 0, 0), "black")
|
||||
peak_image = _ensure_window_image(
|
||||
width,
|
||||
@@ -653,75 +578,97 @@ def start_ld_instant_peak_tracking(self: "PQAutomationApp"):
|
||||
window_level,
|
||||
)
|
||||
|
||||
# 黑场预置
|
||||
self.signal_service.send_image(black_image)
|
||||
time.sleep(INSTANT_PEAK_CAPTURE_DELAY)
|
||||
|
||||
# 切窗口
|
||||
self.signal_service.send_image(peak_image)
|
||||
|
||||
started = time.time()
|
||||
|
||||
while self.ld_peak_tracking:
|
||||
|
||||
elapsed = time.time() - started
|
||||
if elapsed > max_duration:
|
||||
|
||||
# 固定时长模式
|
||||
if max_duration is not None:
|
||||
if elapsed > max_duration:
|
||||
break
|
||||
|
||||
# 安全保护(30分钟)
|
||||
if elapsed > 1800:
|
||||
self._dispatch_ui(
|
||||
self.log_gui.log,
|
||||
"安全超时停止(30分钟)",
|
||||
"warning",
|
||||
)
|
||||
break
|
||||
|
||||
x, y, lv, _X, _Y, _Z = self.read_ca_xyLv()
|
||||
|
||||
if lv is None:
|
||||
time.sleep(sample_interval)
|
||||
continue
|
||||
|
||||
lv = float(lv)
|
||||
|
||||
# 更新峰值
|
||||
if peak_lv is None or lv > peak_lv:
|
||||
peak_lv = float(lv)
|
||||
peak_lv = lv
|
||||
peak_time = elapsed
|
||||
|
||||
# 曲线记录
|
||||
if record_curve:
|
||||
curve_count += 1
|
||||
self._dispatch_ui(
|
||||
self.ld_tree.insert,
|
||||
"",
|
||||
tk.END,
|
||||
self._insert_ld_tree_item,
|
||||
values=(
|
||||
"瞬时峰值曲线",
|
||||
f"{window_percentage}%窗口@{window_luminance_percent:.0f}% t={elapsed:.2f}s",
|
||||
f"{float(lv):.4f}",
|
||||
f"{lv:.4f}",
|
||||
f"{x:.4f}",
|
||||
f"{y:.4f}",
|
||||
datetime.datetime.now().strftime("%H:%M:%S"),
|
||||
),
|
||||
)
|
||||
|
||||
# 回落检测
|
||||
if peak_lv is not None:
|
||||
drop_threshold = max(
|
||||
peak_lv * INSTANT_PEAK_DROP_RATIO,
|
||||
peak_lv - INSTANT_PEAK_MIN_DROP_NITS,
|
||||
)
|
||||
|
||||
drop_threshold = peak_lv * (1 - drop_percent / 100.0)
|
||||
|
||||
if lv < drop_threshold and elapsed > (peak_time or 0):
|
||||
drop_time = elapsed
|
||||
break
|
||||
|
||||
self._dispatch_ui(
|
||||
self.ld_result_label.config,
|
||||
text=(
|
||||
f"亮度: {lv:.2f} cd/m² | 峰值: {(peak_lv or lv):.2f} cd/m²"
|
||||
f" | t: {elapsed:.2f}s"
|
||||
),
|
||||
text=f"亮度:{lv:.2f} cd/m² | 峰值:{(peak_lv or lv):.2f} cd/m² | t:{elapsed:.2f}s",
|
||||
)
|
||||
|
||||
time.sleep(sample_interval)
|
||||
|
||||
if peak_lv is None:
|
||||
self._dispatch_ui(self.log_gui.log, "瞬时峰值测试未采到有效亮度", "warning")
|
||||
self._dispatch_ui(
|
||||
self.log_gui.log,
|
||||
"瞬时峰值测试未采到有效亮度",
|
||||
"warning",
|
||||
)
|
||||
return
|
||||
|
||||
end_time = drop_time if drop_time is not None else (time.time() - started)
|
||||
sustain_time = max(0.0, end_time - (peak_time or 0.0))
|
||||
|
||||
sustain_time = max(0.0, end_time - (peak_time or 0))
|
||||
|
||||
result_label = (
|
||||
f"峰值={peak_lv:.2f} cd/m², 持续={sustain_time:.2f}s"
|
||||
if drop_time is not None
|
||||
else f"峰值={peak_lv:.2f} cd/m², 持续>{sustain_time:.2f}s(未检测到回落)"
|
||||
else f"峰值={peak_lv:.2f} cd/m², 持续>{sustain_time:.2f}s"
|
||||
)
|
||||
|
||||
self._dispatch_ui(
|
||||
self.ld_tree.insert,
|
||||
"",
|
||||
tk.END,
|
||||
self._insert_ld_tree_item,
|
||||
values=(
|
||||
"瞬时峰值亮度",
|
||||
pattern_label,
|
||||
@@ -731,30 +678,54 @@ def start_ld_instant_peak_tracking(self: "PQAutomationApp"):
|
||||
datetime.datetime.now().strftime("%H:%M:%S"),
|
||||
),
|
||||
)
|
||||
|
||||
self._dispatch_ui(
|
||||
self.log_gui.log,
|
||||
f"瞬时峰值测试完成: {result_label},曲线点 {curve_count} 个",
|
||||
"success",
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
self._dispatch_ui(self.log_gui.log, f"瞬时峰值测试异常: {e}", "error")
|
||||
self._dispatch_ui(
|
||||
self.log_gui.log,
|
||||
f"瞬时峰值测试异常: {e}",
|
||||
"error",
|
||||
)
|
||||
|
||||
finally:
|
||||
self.ld_peak_tracking = False
|
||||
|
||||
if hasattr(self, "ld_peak_start_btn"):
|
||||
self._dispatch_ui(self.ld_peak_start_btn.configure, state="normal")
|
||||
self._dispatch_ui(
|
||||
self.ld_peak_start_btn.configure,
|
||||
state="normal",
|
||||
)
|
||||
|
||||
if hasattr(self, "ld_peak_stop_btn"):
|
||||
self._dispatch_ui(self.ld_peak_stop_btn.configure, state="disabled")
|
||||
self._dispatch_ui(
|
||||
self.ld_peak_stop_btn.configure,
|
||||
state="disabled",
|
||||
)
|
||||
|
||||
threading.Thread(target=run, daemon=True).start()
|
||||
|
||||
|
||||
def stop_ld_instant_peak_tracking(self: "PQAutomationApp"):
|
||||
"""停止独立瞬时峰值连续采样。"""
|
||||
"""停止独立瞬时峰值连续采样"""
|
||||
if getattr(self, "ld_peak_tracking", False):
|
||||
self.ld_peak_tracking = False
|
||||
self.log_gui.log("已请求停止瞬时峰值测试", level="info")
|
||||
|
||||
|
||||
def _insert_ld_tree_item(self, parent="", index=tk.END, **kwargs):
|
||||
item = self.ld_tree.insert(parent, index, **kwargs)
|
||||
try:
|
||||
self.ld_tree.see(item)
|
||||
except Exception:
|
||||
pass
|
||||
return item
|
||||
|
||||
|
||||
def measure_ld_luminance(self: "PQAutomationApp"):
|
||||
"""测量当前显示的亮度并追加一行到 Treeview。"""
|
||||
if not self.ca:
|
||||
@@ -764,8 +735,6 @@ def measure_ld_luminance(self: "PQAutomationApp"):
|
||||
messagebox.showinfo("提示", "请先发送一个窗口图案")
|
||||
return
|
||||
|
||||
self.log_gui.log("📏 正在采集亮度...", level="info")
|
||||
|
||||
def measure():
|
||||
try:
|
||||
x, y, lv, _X, _Y, _Z = self.read_ca_xyLv()
|
||||
@@ -781,7 +750,7 @@ def measure_ld_luminance(self: "PQAutomationApp"):
|
||||
text=f"亮度: {lv:.2f} cd/m² | x: {x:.4f} | y: {y:.4f}",
|
||||
)
|
||||
self._dispatch_ui(
|
||||
self.ld_tree.insert, "", tk.END,
|
||||
self._insert_ld_tree_item,
|
||||
values=(
|
||||
getattr(self, "current_ld_test_item", "手动采集"),
|
||||
self.current_ld_pattern_label,
|
||||
@@ -840,48 +809,82 @@ def save_local_dimming_results(self: "PQAutomationApp"):
|
||||
|
||||
|
||||
def plot_ld_instant_peak_curve(self: "PQAutomationApp"):
|
||||
"""从测试表格提取瞬时峰值曲线点并生成亮度-时间曲线图。"""
|
||||
curve_points = []
|
||||
pattern = re.compile(r"t\s*=\s*([0-9]+(?:\.[0-9]+)?)s")
|
||||
"""绘制最近一次瞬时峰值测试的亮度-时间曲线"""
|
||||
|
||||
for item in self.ld_tree.get_children():
|
||||
pattern = re.compile(r"t\s*=\s*([0-9]+(?:\.[0-9]+)?)s")
|
||||
curve_points = []
|
||||
|
||||
# 从表格底部向上找最近一次曲线
|
||||
items = list(self.ld_tree.get_children())[::-1]
|
||||
|
||||
collecting = False
|
||||
|
||||
for item in items:
|
||||
values = self.ld_tree.item(item, "values")
|
||||
|
||||
if len(values) < 3:
|
||||
continue
|
||||
|
||||
test_item = str(values[0])
|
||||
pattern_text = str(values[1])
|
||||
lv_text = str(values[2])
|
||||
if test_item != "瞬时峰值曲线":
|
||||
|
||||
if test_item == "瞬时峰值曲线":
|
||||
collecting = True
|
||||
else:
|
||||
if collecting:
|
||||
break
|
||||
continue
|
||||
|
||||
match = pattern.search(pattern_text)
|
||||
if not match:
|
||||
continue
|
||||
|
||||
try:
|
||||
t_sec = float(match.group(1))
|
||||
lv = float(lv_text)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
curve_points.append((t_sec, lv))
|
||||
|
||||
if not curve_points:
|
||||
messagebox.showinfo("提示", "没有可绘制的瞬时峰值曲线数据")
|
||||
return
|
||||
|
||||
# 时间排序
|
||||
curve_points.sort(key=lambda x: x[0])
|
||||
|
||||
t_data = [p[0] for p in curve_points]
|
||||
lv_data = [p[1] for p in curve_points]
|
||||
|
||||
fig = plt.figure(figsize=(8.6, 4.6))
|
||||
ax = fig.add_subplot(111)
|
||||
ax.plot(t_data, lv_data, "-o", linewidth=1.8, markersize=3.5, color="#2a9d8f")
|
||||
|
||||
ax.plot(
|
||||
t_data,
|
||||
lv_data,
|
||||
"-o",
|
||||
linewidth=1.8,
|
||||
markersize=3.5,
|
||||
color="#2a9d8f",
|
||||
)
|
||||
|
||||
ax.set_title("Instant Peak Luminance Curve")
|
||||
ax.set_xlabel("Time (s)")
|
||||
ax.set_ylabel("Luminance (cd/m²)")
|
||||
ax.grid(True, linestyle="--", alpha=0.35)
|
||||
|
||||
# 标记峰值
|
||||
peak_idx = int(np.argmax(lv_data))
|
||||
ax.scatter([t_data[peak_idx]], [lv_data[peak_idx]], color="#e76f51", zorder=3)
|
||||
|
||||
ax.scatter(
|
||||
[t_data[peak_idx]],
|
||||
[lv_data[peak_idx]],
|
||||
color="#e76f51",
|
||||
zorder=3,
|
||||
)
|
||||
|
||||
ax.annotate(
|
||||
f"Peak: {lv_data[peak_idx]:.2f} cd/m² @ {t_data[peak_idx]:.2f}s",
|
||||
(t_data[peak_idx], lv_data[peak_idx]),
|
||||
@@ -893,24 +896,23 @@ def plot_ld_instant_peak_curve(self: "PQAutomationApp"):
|
||||
|
||||
fig.tight_layout()
|
||||
plt.show(block=False)
|
||||
self.log_gui.log("已生成瞬时峰值曲线图", level="success")
|
||||
|
||||
self.log_gui.log("已生成本次瞬时峰值曲线图", level="success")
|
||||
|
||||
|
||||
class LocalDimmingMixin:
|
||||
"""由 tools/refactor_to_mixins.py 自动生成。
|
||||
把本模块的自由函数挂到 PQAutomationApp 上,便于 F12 跳转与类型推断。
|
||||
"""
|
||||
start_local_dimming_test = start_local_dimming_test
|
||||
update_ld_results = update_ld_results
|
||||
stop_local_dimming_test = stop_local_dimming_test
|
||||
send_ld_window = send_ld_window
|
||||
send_ld_manual_window = send_ld_manual_window
|
||||
send_ld_checkerboard = send_ld_checkerboard
|
||||
send_ld_black_pattern = send_ld_black_pattern
|
||||
send_ld_instant_peak = send_ld_instant_peak
|
||||
start_ld_instant_peak_tracking = start_ld_instant_peak_tracking
|
||||
stop_ld_instant_peak_tracking = stop_ld_instant_peak_tracking
|
||||
measure_ld_luminance = measure_ld_luminance
|
||||
clear_ld_records = clear_ld_records
|
||||
save_local_dimming_results = save_local_dimming_results
|
||||
plot_ld_instant_peak_curve = plot_ld_instant_peak_curve
|
||||
|
||||
_insert_ld_tree_item = _insert_ld_tree_item
|
||||
|
||||
@@ -774,7 +774,6 @@ def create_test_type_frame(self: "PQAutomationApp"):
|
||||
).pack(fill=tk.X, padx=16, pady=(16, 6), anchor="w")
|
||||
|
||||
panel_buttons = [
|
||||
("log_btn", "测试日志", self.toggle_log_panel),
|
||||
("ai_image_btn", "AI 图片", self.toggle_ai_image_panel),
|
||||
("pantone_baseline_btn", "Pantone 摸底", self.toggle_pantone_baseline_panel),
|
||||
("gamma_pattern_btn", "Gamma Pattern编辑", self.toggle_gamma_pattern_panel),
|
||||
@@ -801,6 +800,17 @@ def create_test_type_frame(self: "PQAutomationApp"):
|
||||
)
|
||||
beta_lbl.pack(fill=tk.X, side=tk.BOTTOM, padx=4, pady=(6, 4))
|
||||
|
||||
|
||||
# ---------- 测试日志(底部固定) ----------
|
||||
self.log_btn = ttk.Button(
|
||||
self.sidebar_frame,
|
||||
text="测试日志",
|
||||
style="Sidebar.TButton",
|
||||
command=self.toggle_log_panel,
|
||||
takefocus=False,
|
||||
)
|
||||
self.log_btn.pack(fill=tk.X, padx=0, pady=(0, 2), side=tk.BOTTOM)
|
||||
|
||||
# ---------- 主题切换(底部固定) ----------
|
||||
self.theme_toggle_btn = ttk.Button(
|
||||
self.sidebar_frame,
|
||||
@@ -814,8 +824,6 @@ def create_test_type_frame(self: "PQAutomationApp"):
|
||||
|
||||
# 注册面板按钮
|
||||
if hasattr(self, "panels"):
|
||||
if "log" in self.panels:
|
||||
self.panels["log"]["button"] = self.log_btn
|
||||
if "ai_image" in self.panels:
|
||||
self.panels["ai_image"]["button"] = self.ai_image_btn
|
||||
if "single_step" in self.panels:
|
||||
@@ -1020,10 +1028,10 @@ def on_screen_module_timing_changed(self: "PQAutomationApp", event=None):
|
||||
|
||||
# 根据分辨率给出提示
|
||||
if width >= 3840: # 4K及以上
|
||||
self.log_gui.log(" ℹ️ 检测到4K分辨率", level="info")
|
||||
self.log_gui.log("检测到4K分辨率", level="info")
|
||||
|
||||
if refresh_rate >= 120:
|
||||
self.log_gui.log(" ℹ️ 检测到高刷新率", level="info")
|
||||
self.log_gui.log("检测到高刷新率", level="info")
|
||||
|
||||
# 更新屏模组配置(独立于 current_test_type)
|
||||
self.config.current_test_types.setdefault("screen_module", {})[
|
||||
|
||||
@@ -42,13 +42,13 @@ def create_local_dimming_panel(self: "PQAutomationApp"):
|
||||
|
||||
ttk.Label(
|
||||
title_frame,
|
||||
text="🔆 Local Dimming 窗口测试",
|
||||
text="Local Dimming 窗口测试",
|
||||
font=("微软雅黑", 14, "bold"),
|
||||
).pack(side=tk.LEFT)
|
||||
|
||||
# ==================== 2. 窗口百分比按钮 ====================
|
||||
window_frame = ttk.LabelFrame(
|
||||
main_container, text="🔆 窗口百分比(点击发送)", padding=10
|
||||
main_container, text="窗口百分比", padding=10
|
||||
)
|
||||
window_frame.pack(fill=tk.X, pady=(0, 10))
|
||||
|
||||
@@ -133,16 +133,9 @@ def create_local_dimming_panel(self: "PQAutomationApp"):
|
||||
).pack(side=tk.LEFT, padx=3)
|
||||
|
||||
# ==================== 3. 其他手动图案 ====================
|
||||
pattern_frame = ttk.LabelFrame(main_container, text="🧩 其他测试图案", padding=10)
|
||||
pattern_frame = ttk.LabelFrame(main_container, text="其他测试图案", padding=10)
|
||||
pattern_frame.pack(fill=tk.X, pady=(0, 10))
|
||||
|
||||
ttk.Label(
|
||||
pattern_frame,
|
||||
text="手动发送棋盘格、瞬时峰值、黑场图案,再点击采集当前亮度",
|
||||
font=("", 9),
|
||||
style="SuccessState.TLabel",
|
||||
).pack(pady=(0, 8))
|
||||
|
||||
pattern_row = ttk.Frame(pattern_frame)
|
||||
pattern_row.pack(fill=tk.X)
|
||||
|
||||
@@ -171,21 +164,16 @@ def create_local_dimming_panel(self: "PQAutomationApp"):
|
||||
).pack(side=tk.LEFT, padx=3)
|
||||
|
||||
# ==================== 4. 独立瞬时峰值连续测试 ====================
|
||||
peak_frame = ttk.LabelFrame(main_container, text="⚡ 瞬时峰值独立测试", padding=10)
|
||||
peak_frame = ttk.LabelFrame(main_container, text="瞬时峰值独立测试", padding=10)
|
||||
peak_frame.pack(fill=tk.X, pady=(0, 10))
|
||||
|
||||
ttk.Label(
|
||||
peak_frame,
|
||||
text="先黑场切窗口后连续测亮度,直到回落或到达最长测量时间",
|
||||
font=("", 9),
|
||||
style="WarningState.TLabel",
|
||||
).grid(row=0, column=0, columnspan=8, sticky=tk.W, pady=(0, 8))
|
||||
|
||||
self.ld_peak_window_size_var = tk.StringVar(value="10")
|
||||
self.ld_peak_window_luminance_var = tk.StringVar(value="100")
|
||||
self.ld_peak_duration_var = tk.StringVar(value="20")
|
||||
self.ld_peak_sample_interval_var = tk.StringVar(value="0.3")
|
||||
self.ld_peak_record_curve_var = tk.BooleanVar(value=True)
|
||||
self.ld_peak_no_limit_var = tk.BooleanVar(value=False)
|
||||
self.ld_peak_drop_percent_var = tk.StringVar(value="3")
|
||||
|
||||
ttk.Label(peak_frame, text="窗口(%):").grid(row=1, column=0, sticky=tk.W, padx=(0, 4))
|
||||
ttk.Entry(peak_frame, textvariable=self.ld_peak_window_size_var, width=8).grid(
|
||||
@@ -242,8 +230,25 @@ def create_local_dimming_panel(self: "PQAutomationApp"):
|
||||
)
|
||||
self.ld_peak_stop_btn.pack(side=tk.LEFT)
|
||||
|
||||
ttk.Label(peak_frame, text="亮度回落(%):").grid(
|
||||
row=2, column=0, sticky=tk.W, padx=(0, 4), pady=(6, 0)
|
||||
)
|
||||
|
||||
ttk.Entry(
|
||||
peak_frame,
|
||||
textvariable=self.ld_peak_drop_percent_var,
|
||||
width=8
|
||||
).grid(row=2, column=1, sticky=tk.W, pady=(6, 0))
|
||||
|
||||
ttk.Checkbutton(
|
||||
peak_frame,
|
||||
text="不固定测试时间",
|
||||
variable=self.ld_peak_no_limit_var,
|
||||
bootstyle="round-toggle",
|
||||
).grid(row=2, column=2, columnspan=3, sticky=tk.W, pady=(6, 0))
|
||||
|
||||
# ==================== 5. CA410 采集按钮 ====================
|
||||
measure_frame = ttk.LabelFrame(main_container, text="📊 CA410 测量", padding=10)
|
||||
measure_frame = ttk.LabelFrame(main_container, text="CA410 测量", padding=10)
|
||||
measure_frame.pack(fill=tk.X, pady=(0, 10))
|
||||
|
||||
measure_btn_frame = ttk.Frame(measure_frame)
|
||||
@@ -251,7 +256,7 @@ def create_local_dimming_panel(self: "PQAutomationApp"):
|
||||
|
||||
self.ld_measure_btn = ttk.Button(
|
||||
measure_btn_frame,
|
||||
text="📏 采集当前亮度",
|
||||
text="采集当前亮度",
|
||||
command=self.measure_ld_luminance,
|
||||
bootstyle="primary",
|
||||
width=15,
|
||||
@@ -268,7 +273,7 @@ def create_local_dimming_panel(self: "PQAutomationApp"):
|
||||
self.ld_result_label.pack(side=tk.LEFT, padx=(10, 0))
|
||||
|
||||
# ==================== 6. 测试结果表格 ====================
|
||||
result_frame = ttk.LabelFrame(main_container, text="📋 测试记录", padding=10)
|
||||
result_frame = ttk.LabelFrame(main_container, text="测试记录", padding=10)
|
||||
result_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
|
||||
|
||||
# Treeview
|
||||
@@ -314,7 +319,7 @@ def create_local_dimming_panel(self: "PQAutomationApp"):
|
||||
|
||||
self.ld_save_btn = ttk.Button(
|
||||
bottom_frame,
|
||||
text="💾 保存结果",
|
||||
text="保存结果",
|
||||
command=self.save_local_dimming_results,
|
||||
bootstyle="info",
|
||||
width=12,
|
||||
@@ -323,7 +328,7 @@ def create_local_dimming_panel(self: "PQAutomationApp"):
|
||||
|
||||
self.ld_plot_btn = ttk.Button(
|
||||
bottom_frame,
|
||||
text="📈 生成峰值曲线",
|
||||
text="生成峰值曲线",
|
||||
command=self.plot_ld_instant_peak_curve,
|
||||
bootstyle="warning-outline",
|
||||
width=14,
|
||||
|
||||
Reference in New Issue
Block a user