From 9ad9cf9aa02cbc9629eca7f52a1bdf70e0f44e51 Mon Sep 17 00:00:00 2001 From: "xinzhu.yin" Date: Mon, 8 Jun 2026 11:39:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=89=8B=E5=8A=A8=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E7=AA=97=E5=8F=A3=E4=BA=AE=E5=BA=A6=E3=80=81=E6=9B=B2?= =?UTF-8?q?=E7=BA=BF=E5=9B=BE=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/tests/local_dimming.py | 111 ++++++++++++++++++++++++++++++-- app/views/panels/side_panels.py | 61 +++++++++++++++--- settings/pq_config.json | 2 +- 3 files changed, 160 insertions(+), 14 deletions(-) diff --git a/app/tests/local_dimming.py b/app/tests/local_dimming.py index 2f0bf9b..b04009d 100644 --- a/app/tests/local_dimming.py +++ b/app/tests/local_dimming.py @@ -8,6 +8,7 @@ import atexit import csv import datetime import os +import re import shutil import sys import threading @@ -15,6 +16,7 @@ import time import tkinter as tk from tkinter import filedialog, messagebox +import matplotlib.pyplot as plt import numpy as np from PIL import Image @@ -398,15 +400,42 @@ def send_ld_window(self: "PQAutomationApp", percentage): messagebox.showwarning("警告", "请先连接 UCD323 设备") return - self.log_gui.log(f"🔆 发送 {percentage}% 窗口...", level="info") - _set_current_ld_pattern(self, "峰值亮度", f"{percentage}%窗口", percentage) + 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) + image_path = _ensure_window_image( + width, + height, + percentage, + window_level, + ) except Exception as e: self._dispatch_ui(self.log_gui.log, f"图像生成失败: {e}") return @@ -416,14 +445,27 @@ def send_ld_window(self: "PQAutomationApp", percentage): except Exception: ok = False msg = ( - f"{percentage}% 窗口已发送" if ok - else f"{percentage}% 窗口发送失败" + f"{percentage}% 窗口({luminance_percent:.0f}%亮度)已发送" if ok + else f"{percentage}% 窗口({luminance_percent:.0f}%亮度)发送失败" ) self._dispatch_ui(self.log_gui.log, msg) threading.Thread(target=send, daemon=True).start() +def send_ld_manual_window(self: "PQAutomationApp"): + """按手动输入的窗口百分比和亮度直接发送窗口图案。""" + try: + percentage = int(float(self.ld_window_percentage_var.get())) + if percentage < 1 or percentage > 100: + raise ValueError("窗口范围应为 1-100") + except Exception as e: + messagebox.showwarning("参数错误", f"窗口百分比无效: {e}") + return + + self.send_ld_window(percentage) + + def send_ld_checkerboard(self: "PQAutomationApp", center_white): """发送棋盘格图案(手动模式)。""" if not self.signal_service.is_connected: @@ -797,6 +839,63 @@ def save_local_dimming_results(self: "PQAutomationApp"): messagebox.showerror("错误", f"保存失败: {str(e)}") +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(): + 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 != "瞬时峰值曲线": + 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.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.annotate( + f"Peak: {lv_data[peak_idx]:.2f} cd/m² @ {t_data[peak_idx]:.2f}s", + (t_data[peak_idx], lv_data[peak_idx]), + xytext=(8, 10), + textcoords="offset points", + fontsize=9, + color="#333333", + ) + + fig.tight_layout() + plt.show(block=False) + self.log_gui.log("已生成瞬时峰值曲线图", level="success") + + class LocalDimmingMixin: """由 tools/refactor_to_mixins.py 自动生成。 把本模块的自由函数挂到 PQAutomationApp 上,便于 F12 跳转与类型推断。 @@ -805,6 +904,7 @@ class LocalDimmingMixin: 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 @@ -813,3 +913,4 @@ class LocalDimmingMixin: 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 diff --git a/app/views/panels/side_panels.py b/app/views/panels/side_panels.py index 4055751..71b7a97 100644 --- a/app/views/panels/side_panels.py +++ b/app/views/panels/side_panels.py @@ -60,6 +60,50 @@ def create_local_dimming_panel(self: "PQAutomationApp"): style="SuccessState.TLabel", ).pack(pady=(0, 8)) + window_level_row = ttk.Frame(window_frame) + window_level_row.pack(fill=tk.X, pady=(0, 8)) + + ttk.Label(window_level_row, text="窗口(%):").pack(side=tk.LEFT) + self.ld_window_percentage_var = tk.StringVar(value="10") + ld_window_percentage_entry = ttk.Entry( + window_level_row, + textvariable=self.ld_window_percentage_var, + width=8, + ) + ld_window_percentage_entry.pack(side=tk.LEFT, padx=(6, 10)) + + ttk.Label(window_level_row, text="窗口亮度(%):").pack(side=tk.LEFT) + self.ld_window_luminance_var = tk.StringVar(value="100") + ld_window_luminance_entry = ttk.Entry( + window_level_row, + textvariable=self.ld_window_luminance_var, + width=8, + ) + ld_window_luminance_entry.pack(side=tk.LEFT, padx=(6, 10)) + + ttk.Button( + window_level_row, + text="生成窗口", + command=self.send_ld_manual_window, + bootstyle="success-outline", + width=12, + ).pack(side=tk.LEFT) + + ld_window_percentage_entry.bind( + "", + lambda _event: self.send_ld_manual_window(), + ) + ld_window_luminance_entry.bind( + "", + lambda _event: self.send_ld_manual_window(), + ) + + ttk.Label( + window_level_row, + text="输入后可直接点生成或回车", + style="InfoState.TLabel", + ).pack(side=tk.LEFT, padx=(8, 0)) + # 第一行:1%, 2%, 5%, 10%, 18% row1 = ttk.Frame(window_frame) row1.pack(fill=tk.X, pady=(0, 5)) @@ -118,14 +162,6 @@ def create_local_dimming_panel(self: "PQAutomationApp"): width=14, ).pack(side=tk.LEFT, padx=3) - ttk.Button( - pattern_row, - text="瞬时峰值", - command=self.send_ld_instant_peak, - bootstyle="warning", - width=12, - ).pack(side=tk.LEFT, padx=3) - ttk.Button( pattern_row, text="全黑画面", @@ -285,6 +321,15 @@ def create_local_dimming_panel(self: "PQAutomationApp"): ) self.ld_save_btn.pack(side=tk.LEFT) + self.ld_plot_btn = ttk.Button( + bottom_frame, + text="📈 生成峰值曲线", + command=self.plot_ld_instant_peak_curve, + bootstyle="warning-outline", + width=14, + ) + self.ld_plot_btn.pack(side=tk.LEFT, padx=(5, 0)) + # 默认隐藏 self.local_dimming_visible = False diff --git a/settings/pq_config.json b/settings/pq_config.json index f7ba9fa..c03d6d4 100644 --- a/settings/pq_config.json +++ b/settings/pq_config.json @@ -1,5 +1,5 @@ { - "current_test_type": "screen_module", + "current_test_type": "local_dimming", "test_types": { "screen_module": { "name": "屏模组性能测试",