From febbb28a4c4ada5a12c226df83d99b944c024e69 Mon Sep 17 00:00:00 2001 From: "xinzhu.yin" Date: Mon, 8 Jun 2026 11:03:10 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=83=A8=E5=88=86UI=E3=80=81?= =?UTF-8?q?=E4=BF=AE=E6=94=B9module=E4=B8=AD=E5=BF=83=E7=82=B9=E8=AE=BE?= =?UTF-8?q?=E5=AE=9A=E3=80=81=E6=B7=BB=E5=8A=A0=E5=8D=95=E7=8B=AC=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/device/connection.py | 114 +++++++++++++++++++++++- app/plots/plot_cct.py | 27 +++++- app/runner/test_runner.py | 14 +++ app/views/panels/cct_panel.py | 4 + app/views/panels/gamma_pattern_panel.py | 17 ++-- app/views/panels/main_layout.py | 45 +++++++++- app/views/theme_manager.py | 2 +- settings/pq_config.json | 2 +- 8 files changed, 201 insertions(+), 24 deletions(-) diff --git a/app/device/connection.py b/app/device/connection.py index a7e5c82..742eb5d 100644 --- a/app/device/connection.py +++ b/app/device/connection.py @@ -160,8 +160,7 @@ class ConnectionController: def check_all_async(self) -> None: """异步并联检测 UCD + CA,通过 ``_dispatch_ui`` 回主线程更新 UI。""" app = self._app - app.check_button.configure(state="disabled") - app.refresh_button.configure(state="disabled") + self._set_connect_widgets_state("disabled") app.status_var.set("正在检测连接...") app.root.update() @@ -185,6 +184,58 @@ class ConnectionController: threading.Thread(target=worker, daemon=True).start() + def check_ucd_async(self) -> None: + """仅异步连接 UCD323。""" + app = self._app + self._set_connect_widgets_state("disabled") + app.status_var.set("正在连接 UCD323...") + app.root.update() + + def worker(): + try: + ucd_ok = self.connect_ucd(app.ucd_list_var.get()) + app._dispatch_ui( + app.update_connection_indicator, + app.ucd_status_indicator, + ucd_ok, + ) + app._dispatch_ui( + app.status_var.set, + "UCD323 连接成功" if ucd_ok else "UCD323 连接失败", + ) + app._dispatch_ui(self._enable_widgets) + except Exception as exc: # noqa: BLE001 + app._dispatch_ui(app.log_gui.log, f"UCD323 连接出错: {exc}") + app._dispatch_ui(self._enable_widgets) + + threading.Thread(target=worker, daemon=True).start() + + def check_ca_async(self) -> None: + """仅异步连接 CA410。""" + app = self._app + self._set_connect_widgets_state("disabled") + app.status_var.set("正在连接 CA410...") + app.root.update() + + def worker(): + try: + ca_ok = self.connect_ca() + app._dispatch_ui( + app.update_connection_indicator, + app.ca_status_indicator, + ca_ok, + ) + app._dispatch_ui( + app.status_var.set, + "CA410 连接成功" if ca_ok else "CA410 连接失败", + ) + app._dispatch_ui(self._enable_widgets) + except Exception as exc: # noqa: BLE001 + app._dispatch_ui(app.log_gui.log, f"CA410 连接出错: {exc}") + app._dispatch_ui(self._enable_widgets) + + threading.Thread(target=worker, daemon=True).start() + def disconnect_all(self) -> None: try: self.disconnect_ucd() @@ -196,6 +247,24 @@ class ConnectionController: self._log(f"断开连接时发生错误: {exc}", level="info") messagebox.showerror("错误", f"断开连接失败: {exc}") + def disconnect_ucd_only(self) -> None: + try: + self.disconnect_ucd() + self._app.refresh_connection_indicators() + self._app.status_var.set("UCD323 已断开") + except Exception as exc: # noqa: BLE001 + self._log(f"断开 UCD323 时发生错误: {exc}", level="info") + messagebox.showerror("错误", f"断开 UCD323 失败: {exc}") + + def disconnect_ca_only(self) -> None: + try: + self.disconnect_ca() + self._app.refresh_connection_indicators() + self._app.status_var.set("CA410 已断开") + except Exception as exc: # noqa: BLE001 + self._log(f"断开 CA410 时发生错误: {exc}", level="info") + messagebox.showerror("错误", f"断开 CA410 失败: {exc}") + def refresh_ports(self) -> None: """刷新 UCD + COM 端口下拉框;指示器复位。""" app = self._app @@ -219,8 +288,21 @@ class ConnectionController: # -- 内部 ---------------------------------------------------- def _enable_widgets(self) -> None: - self._app.check_button.configure(state="normal") - self._app.refresh_button.configure(state="normal") + self._set_connect_widgets_state("normal") + + def _set_connect_widgets_state(self, state: str) -> None: + for attr in ( + "check_button", + "ucd_connect_button", + "ca_connect_button", + "refresh_button", + ): + widget = getattr(self._app, attr, None) + if widget is not None: + try: + widget.configure(state=state) + except Exception: # noqa: BLE001 + pass def _log(self, msg: str, *, level: str = "info") -> None: log_gui = getattr(self._app, "log_gui", None) @@ -250,6 +332,14 @@ def check_com_connections(self: "PQAutomationApp"): self.connection.check_all_async() +def check_ucd_connection(self: "PQAutomationApp"): + self.connection.check_ucd_async() + + +def check_ca_connection(self: "PQAutomationApp"): + self.connection.check_ca_async() + + def update_connection_indicator(self: "PQAutomationApp", indicator, connected): _draw_connection_indicator(indicator, "green" if connected else "red") @@ -307,6 +397,14 @@ def disconnect_com_connections(self: "PQAutomationApp"): self.connection.disconnect_all() +def disconnect_ucd_connection(self: "PQAutomationApp"): + self.connection.disconnect_ucd_only() + + +def disconnect_ca_connection(self: "PQAutomationApp"): + self.connection.disconnect_ca_only() + + def _get_ca_measure_lock(self: "PQAutomationApp"): lock = getattr(self, "_ca_measure_lock", None) if lock is None: @@ -357,11 +455,15 @@ __all__ = [ "get_available_com_ports", "refresh_com_ports", "check_com_connections", + "check_ucd_connection", + "check_ca_connection", "update_connection_indicator", "refresh_connection_indicators", "check_port_connection", "enable_com_widgets", "disconnect_com_connections", + "disconnect_ucd_connection", + "disconnect_ca_connection", ] @@ -373,11 +475,15 @@ class DeviceConnectionMixin: get_available_com_ports = get_available_com_ports refresh_com_ports = refresh_com_ports check_com_connections = check_com_connections + check_ucd_connection = check_ucd_connection + check_ca_connection = check_ca_connection update_connection_indicator = update_connection_indicator refresh_connection_indicators = refresh_connection_indicators check_port_connection = check_port_connection enable_com_widgets = enable_com_widgets disconnect_com_connections = disconnect_com_connections + disconnect_ucd_connection = disconnect_ucd_connection + disconnect_ca_connection = disconnect_ca_connection _get_ca_measure_lock = _get_ca_measure_lock _read_ca_display = _read_ca_display read_ca_xyLv = read_ca_xyLv diff --git a/app/plots/plot_cct.py b/app/plots/plot_cct.py index ec10ebf..22fc912 100644 --- a/app/plots/plot_cct.py +++ b/app/plots/plot_cct.py @@ -83,6 +83,15 @@ def plot_cct(self: "PQAutomationApp", test_type): self.log_gui.log(f" y范围: {min(y_measured):.6f} - {max(y_measured):.6f}", level="info") # ========== 根据测试类型读取对应参数 ========== + # 屏模组中心坐标优先使用实测 100% 灰阶点(gray 数据第 1 个点) + screen_center_xy = None + if test_type == "screen_module": + try: + if gray_data and len(gray_data[0]) >= 2: + screen_center_xy = (float(gray_data[0][0]), float(gray_data[0][1])) + except Exception: + screen_center_xy = None + if test_type == "sdr_movie": try: x_ideal = float(self.sdr_cct_x_ideal_var.get()) @@ -111,11 +120,23 @@ def plot_cct(self: "PQAutomationApp", test_type): self.log_gui.log("HDR 参数读取失败,使用默认值", level="error") else: # screen_module try: - x_ideal = float(self.cct_x_ideal_var.get()) x_tolerance = float(self.cct_x_tolerance_var.get()) - y_ideal = float(self.cct_y_ideal_var.get()) y_tolerance = float(self.cct_y_tolerance_var.get()) - self.log_gui.log("使用屏模组色度参数", level="success") + if screen_center_xy is not None: + x_ideal, y_ideal = screen_center_xy + # 同步到输入框,避免界面显示和实际计算不一致 + try: + self.cct_x_ideal_var.set(f"{x_ideal:.6f}") + self.cct_y_ideal_var.set(f"{y_ideal:.6f}") + except Exception: + pass + self.log_gui.log( + f"屏模组中心使用实测100%坐标: x={x_ideal:.6f}, y={y_ideal:.6f}" + , level="success") + else: + x_ideal = float(self.cct_x_ideal_var.get()) + y_ideal = float(self.cct_y_ideal_var.get()) + self.log_gui.log("未取到实测100%点,回退到屏模组色度参数", level="error") except: x_ideal = 0.306 x_tolerance = 0.003 diff --git a/app/runner/test_runner.py b/app/runner/test_runner.py index 39dce29..c5b57c1 100644 --- a/app/runner/test_runner.py +++ b/app/runner/test_runner.py @@ -831,6 +831,20 @@ def test_cct(self: "PQAutomationApp", test_type, gray_data=None): self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行色度计算", level="info") + # 屏模组测试:中心坐标直接使用本次灰阶 100% 实测值(第 1 个点) + if test_type == "screen_module": + try: + if results and len(results[0]) >= 2: + x_100 = float(results[0][0]) + y_100 = float(results[0][1]) + self.cct_x_ideal_var.set(f"{x_100:.6f}") + self.cct_y_ideal_var.set(f"{y_100:.6f}") + self.log_gui.log( + f"屏模组 CCT 中心采用 100% 实测值: x={x_100:.6f}, y={y_100:.6f}" + , level="success") + except Exception as e: + self.log_gui.log(f"同步屏模组100%中心坐标失败: {str(e)}", level="error") + # 提取色度坐标 cct_values = pq_algorithm.calculate_cct_from_results(results) diff --git a/app/views/panels/cct_panel.py b/app/views/panels/cct_panel.py index 87907bb..fb1c629 100644 --- a/app/views/panels/cct_panel.py +++ b/app/views/panels/cct_panel.py @@ -74,6 +74,10 @@ def create_cct_params_frame(self: "PQAutomationApp"): entry = ttk.Entry(self.cct_params_frame, textvariable=var, width=15) entry.grid(row=i, column=1, sticky=tk.W, padx=5, pady=3) + # 屏模组中心由实测 100% 点自动决定,避免手动误改。 + if key in ("x_ideal", "y_ideal"): + entry.configure(state="readonly") + # 绑定失去焦点事件 default_val = screen_default_cct_params[key] entry.bind( diff --git a/app/views/panels/gamma_pattern_panel.py b/app/views/panels/gamma_pattern_panel.py index 5353fe1..ce12f03 100644 --- a/app/views/panels/gamma_pattern_panel.py +++ b/app/views/panels/gamma_pattern_panel.py @@ -274,7 +274,7 @@ def create_gamma_pattern_panel(self: "PQAutomationApp"): right = ttk.Frame(mid) right.grid(row=0, column=1, sticky=tk.NS) - edit_frame = ttk.LabelFrame(right, text="编辑选中点", padding=8) + edit_frame = ttk.LabelFrame(right, text="编辑选中点灯", padding=8) edit_frame.pack(fill=tk.X) self._gamma_edit_r_var = tk.StringVar() @@ -358,27 +358,22 @@ def create_gamma_pattern_panel(self: "PQAutomationApp"): command=lambda: _paste_from_clipboard(self), ).pack(fill=tk.X, pady=(6, 0)) - # ===== 底部 ===== - bottom = ttk.LabelFrame(root, text="校验与保存", padding=8) - bottom.pack(fill=tk.X, pady=(10, 0)) + # ---- 右侧:校验与保存(与编辑区放在一起) ---- + save_box = ttk.LabelFrame(right, text="校验与保存", padding=8) + save_box.pack(fill=tk.X, pady=(10, 0)) self._gamma_validate_label = ttk.Label( - bottom, text="", style="Muted.TLabel", justify=tk.LEFT + save_box, text="", style="Muted.TLabel", justify=tk.LEFT ) self._gamma_validate_label.pack(anchor=tk.W) - save_row = ttk.Frame(bottom) + save_row = ttk.Frame(save_box) save_row.pack(fill=tk.X, pady=(6, 0)) ttk.Button( save_row, text="保存改动到当前预设", bootstyle="primary", command=lambda: _save_to_current_preset(self), ).pack(side=tk.LEFT) - ttk.Button( - save_row, text="应用到运行时 (gray.json)", - bootstyle="success", - command=lambda: _apply_current_to_runtime(self), - ).pack(side=tk.LEFT, padx=(6, 0)) ttk.Button( save_row, text="另存为新预设...", bootstyle="info-outline", diff --git a/app/views/panels/main_layout.py b/app/views/panels/main_layout.py index f111739..c50256b 100644 --- a/app/views/panels/main_layout.py +++ b/app/views/panels/main_layout.py @@ -607,13 +607,14 @@ def create_connection_content(self: "PQAutomationApp"): button_frame.grid_columnconfigure(0, weight=1) button_frame.grid_columnconfigure(1, weight=1) button_frame.grid_columnconfigure(2, weight=1) + button_frame.grid_columnconfigure(3, weight=1) # connect_icon = load_icon("assets/connect-svgrepo-com.png") self.check_button = ttk.Button( button_frame, # image=connect_icon, # bootstyle="link", - text="连接", + text="全部连接", bootstyle="success", takefocus=False, command=self.check_com_connections, @@ -621,6 +622,42 @@ def create_connection_content(self: "PQAutomationApp"): # self.check_button.image = connect_icon self.check_button.grid(row=0, column=0, padx=(0, 4), pady=3, sticky="ew") + self.ucd_connect_button = ttk.Button( + button_frame, + text="连接UCD", + bootstyle="success-outline", + takefocus=False, + command=self.check_ucd_connection, + ) + self.ucd_connect_button.grid(row=0, column=1, padx=4, pady=3, sticky="ew") + + self.ca_connect_button = ttk.Button( + button_frame, + text="连接CA410", + bootstyle="success-outline", + takefocus=False, + command=self.check_ca_connection, + ) + self.ca_connect_button.grid(row=0, column=2, padx=4, pady=3, sticky="ew") + + self.ucd_disconnect_button = ttk.Button( + button_frame, + text="断开UCD", + bootstyle="danger-outline", + takefocus=False, + command=self.disconnect_ucd_connection, + ) + self.ucd_disconnect_button.grid(row=1, column=1, padx=4, pady=3, sticky="ew") + + self.ca_disconnect_button = ttk.Button( + button_frame, + text="断开CA410", + bootstyle="danger-outline", + takefocus=False, + command=self.disconnect_ca_connection, + ) + self.ca_disconnect_button.grid(row=1, column=2, padx=4, pady=3, sticky="ew") + # disconnect_icon = load_icon("assets/disconnect-svgrepo-com.png") # 断开连接按钮 self.disconnect_button = ttk.Button( @@ -633,7 +670,7 @@ def create_connection_content(self: "PQAutomationApp"): command=self.disconnect_com_connections, ) # self.disconnect_button.image = disconnect_icon # 防止图标被垃圾回收 - self.disconnect_button.grid(row=0, column=1, padx=4, pady=3, sticky="ew") + self.disconnect_button.grid(row=1, column=0, padx=(0, 4), pady=3, sticky="ew") # refresh_icon = load_icon("assets/refresh-svgrepo-com.png") self.refresh_button = ttk.Button( @@ -646,7 +683,7 @@ def create_connection_content(self: "PQAutomationApp"): command=self.refresh_com_ports, ) # self.refresh_button.image = refresh_icon # 防止图标被垃圾回收 - self.refresh_button.grid(row=0, column=2, padx=(4, 0), pady=3, sticky="ew") + self.refresh_button.grid(row=1, column=3, padx=(4, 0), pady=3, sticky="ew") # CA端口 ttk.Label(com_frame, text="CA端口:").grid( @@ -740,7 +777,7 @@ def create_test_type_frame(self: "PQAutomationApp"): ("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 图案", self.toggle_gamma_pattern_panel), + ("gamma_pattern_btn", "Gamma Pattern编辑", self.toggle_gamma_pattern_panel), ("calman_btn", "CALMAN 灰阶", self.toggle_calman_panel), ] for attr, text, cmd in panel_buttons: diff --git a/app/views/theme_manager.py b/app/views/theme_manager.py index c8820be..b161a5e 100644 --- a/app/views/theme_manager.py +++ b/app/views/theme_manager.py @@ -29,7 +29,7 @@ _LEGACY_LIGHT_THEMES = {"yeti"} _CALMAN_LIGHT_COLORS = { "primary": "#1755a6", - "secondary": "#2B6CB0", + "secondary": "#3572B4", "success": "#2F9E44", "info": "#247BA0", "warning": "#C98700", 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": "屏模组性能测试",