From 7d7d8949b94e3cb87e36dd8f3bed5824be8cf447 Mon Sep 17 00:00:00 2001 From: "xinzhu.yin" Date: Mon, 20 Apr 2026 10:16:31 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E6=8B=86=E5=88=86plot?= =?UTF-8?q?=E5=9B=BE=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/plots/plot_accuracy.py | 2 +- app/plots/plot_cct.py | 2 +- app/plots/plot_contrast.py | 2 +- app/plots/plot_gamma.py | 2 +- app/plots/plot_gamut.py | 2 +- app/views/__init__.py | 0 app/views/chart_frame.py | 824 ++++++++++++++++++++++++++++++++++++ pqAutomationApp.py | 836 ++----------------------------------- 8 files changed, 864 insertions(+), 806 deletions(-) create mode 100644 app/views/__init__.py create mode 100644 app/views/chart_frame.py diff --git a/app/plots/plot_accuracy.py b/app/plots/plot_accuracy.py index b36434a..44fdbb7 100644 --- a/app/plots/plot_accuracy.py +++ b/app/plots/plot_accuracy.py @@ -315,4 +315,4 @@ def plot_accuracy(app, accuracy_data, test_type): ) self.accuracy_canvas.draw() - self.chart_notebook.select(4) + self.chart_notebook.select(self.accuracy_chart_frame) diff --git a/app/plots/plot_cct.py b/app/plots/plot_cct.py index 3d50719..e5860e4 100644 --- a/app/plots/plot_cct.py +++ b/app/plots/plot_cct.py @@ -320,6 +320,6 @@ def plot_cct(app, test_type): ) self.cct_canvas.draw() - self.chart_notebook.select(2) + self.chart_notebook.select(self.cct_chart_frame) self.log_gui.log("✓ xy 色度坐标图绘制完成") diff --git a/app/plots/plot_contrast.py b/app/plots/plot_contrast.py index 3917e31..092e437 100644 --- a/app/plots/plot_contrast.py +++ b/app/plots/plot_contrast.py @@ -165,4 +165,4 @@ def plot_contrast(app, contrast_data, test_type): ) self.contrast_canvas.draw() - self.chart_notebook.select(3) + self.chart_notebook.select(self.contrast_chart_frame) diff --git a/app/plots/plot_gamma.py b/app/plots/plot_gamma.py index fd28a86..df1c25b 100644 --- a/app/plots/plot_gamma.py +++ b/app/plots/plot_gamma.py @@ -138,6 +138,6 @@ def plot_gamma(app, L_bar, results_with_gamma_list, target_gamma, test_type): # ========== 4. 绘制到画布 ========== self.gamma_canvas.draw() - self.chart_notebook.select(1) + self.chart_notebook.select(self.gamma_chart_frame) self.log_gui.log("Gamma曲线 + 数据表格绘制完成") diff --git a/app/plots/plot_gamut.py b/app/plots/plot_gamut.py index 3001d33..13b01fe 100644 --- a/app/plots/plot_gamut.py +++ b/app/plots/plot_gamut.py @@ -534,6 +534,6 @@ def plot_gamut(app, results, coverage, test_type): ) self.gamut_canvas.draw() - self.chart_notebook.select(0) + self.chart_notebook.select(self.gamut_chart_frame) self.log_gui.log("色域图绘制完成") diff --git a/app/views/__init__.py b/app/views/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/app/views/chart_frame.py b/app/views/chart_frame.py new file mode 100644 index 0000000..b3013fd --- /dev/null +++ b/app/views/chart_frame.py @@ -0,0 +1,824 @@ +"""图表框架相关逻辑(Step 3 重构)。 + +从 pqAutomationApp.PQAutomationApp 中搬迁而来。每个函数第一行 `self = app` +以保留原有 `self.xxx` 属性访问不变。 +""" + +import tkinter as tk +import ttkbootstrap as ttk +import matplotlib.pyplot as plt +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg +from views.pq_debug_panel import PQDebugPanel + +def init_gamut_chart(app): + """初始化色域图表 - 手动设置subplot位置,完全避免重叠""" + self = app + container = ttk.Frame(self.gamut_chart_frame) + container.pack(expand=True, fill=tk.BOTH) + + self.gamut_fig = plt.Figure(figsize=(14, 6), dpi=100) + self.gamut_canvas = FigureCanvasTkAgg(self.gamut_fig, master=container) + + canvas_widget = self.gamut_canvas.get_tk_widget() + canvas_widget.pack(expand=True, fill=tk.BOTH) + + # ✅ 恢复原来的大尺寸:0.84 高度 + self.gamut_ax_xy = self.gamut_fig.add_axes( + [0.02, 0.08, 0.46, 0.84] + ) # ← 改回 0.84 + self.gamut_ax_uv = self.gamut_fig.add_axes( + [0.52, 0.08, 0.46, 0.84] + ) # ← 改回 0.84 + + # 初始化XY图 + self.gamut_ax_xy.set_xlim(0, 600) + self.gamut_ax_xy.set_ylim(600, 0) + self.gamut_ax_xy.axis("off") + self.gamut_ax_xy.set_clip_on(False) + + # 初始化UV图 + self.gamut_ax_uv.set_xlim(0, 600) + self.gamut_ax_uv.set_ylim(600, 0) + self.gamut_ax_uv.axis("off") + self.gamut_ax_uv.set_clip_on(False) + + # 调整标题位置:y=0.98 + self.gamut_fig.suptitle("色域测试", fontsize=12, y=0.98) + + self.gamut_canvas.draw() + +def init_gamma_chart(app): + """初始化Gamma曲线图表 - 左侧曲线 + 右侧表格(✅ 4列 + 通用说明)""" + self = app + container = ttk.Frame(self.gamma_chart_frame) + container.pack(expand=True, fill=tk.BOTH) + + self.gamma_fig = plt.Figure(figsize=(12, 6), dpi=100, constrained_layout=False) + self.gamma_canvas = FigureCanvasTkAgg(self.gamma_fig, master=container) + + canvas_widget = self.gamma_canvas.get_tk_widget() + canvas_widget.pack(expand=True, fill=tk.BOTH) + + # 左侧:Gamma 曲线 + self.gamma_ax = self.gamma_fig.add_axes([0.08, 0.12, 0.50, 0.78]) + self.gamma_ax.set_xlabel("灰阶 (%)", fontsize=10) + self.gamma_ax.set_ylabel("L_bar", fontsize=10) + self.gamma_ax.set_xlim(0, 105) + self.gamma_ax.set_ylim(0, 1.1) + self.gamma_ax.grid(True, linestyle="--", alpha=0.3) + self.gamma_ax.tick_params(labelsize=9) + + # 左侧提示(通用说明,不显示具体 Gamma 值) + self.gamma_ax.text( + 0.5, + 0.5, + "等待测试数据...\n\n" + "将显示:\n" + "• 实测曲线 (蓝色)\n" + "• 理想 Gamma 曲线 (红色)\n\n" + "Gamma 值由测试配置决定", + ha="center", + va="center", + fontsize=10, + color="gray", + transform=self.gamma_ax.transAxes, + bbox=dict( + boxstyle="round,pad=1", facecolor="white", edgecolor="gray", alpha=0.8 + ), + ) + + # 右侧:数据表格 + self.gamma_table_ax = self.gamma_fig.add_axes([0.62, 0.12, 0.35, 0.78]) + self.gamma_table_ax.axis("off") + + # 4列表格数据 + table_data = [ + ["灰阶", "实测亮度\n(cd/m²)", "L_bar\n(计算)", "Gamma"], + ["0%", "--", "--", "--"], + ["10%", "--", "--", "--"], + ["20%", "--", "--", "--"], + ["30%", "--", "--", "--"], + ["40%", "--", "--", "--"], + ["50%", "--", "--", "--"], + ["60%", "--", "--", "--"], + ["70%", "--", "--", "--"], + ["80%", "--", "--", "--"], + ["90%", "--", "--", "--"], + ["100%", "--", "--", "--"], + ] + + table = self.gamma_table_ax.table( + cellText=table_data, + cellLoc="center", + loc="center", + colWidths=[0.18, 0.28, 0.27, 0.27], # ← 4列宽度 + ) + + table.auto_set_font_size(False) + table.set_fontsize(7.5) + table.scale(1, 1.5) + + # 表头样式 + for i in range(4): + cell = table[(0, i)] + cell.set_facecolor("#4472C4") + cell.set_text_props(weight="bold", color="white", fontsize=7) + + # 数据行交替颜色 + for i in range(1, len(table_data)): + for j in range(4): + cell = table[(i, j)] + if i % 2 == 0: + cell.set_facecolor("#E7E6E6") + else: + cell.set_facecolor("#FFFFFF") + + # 底部说明 + self.gamma_table_ax.text( + 0.5, + 0.02, + "表格说明:\n" + "• 实测亮度: 色度计测量值 (cd/m²)\n" + "• L_bar: 归一化亮度 (0-1)\n" + "• Gamma: 实际 Gamma 值", + ha="center", + va="bottom", + fontsize=7, + color="gray", + transform=self.gamma_table_ax.transAxes, + bbox=dict( + boxstyle="round,pad=0.5", + facecolor="lightyellow", + edgecolor="gray", + alpha=0.8, + ), + ) + + self.gamma_fig.suptitle("Gamma曲线 + 数据表格", fontsize=12, y=0.98) + self.gamma_canvas.draw() + +def init_eotf_chart(app): + """初始化 EOTF 曲线图表(HDR 专用)- 左侧曲线 + 右侧表格(✅ 4列)""" + self = app + container = ttk.Frame(self.eotf_chart_frame) + container.pack(expand=True, fill=tk.BOTH) + + self.eotf_fig = plt.Figure(figsize=(12, 6), dpi=100, constrained_layout=False) + self.eotf_canvas = FigureCanvasTkAgg(self.eotf_fig, master=container) + + canvas_widget = self.eotf_canvas.get_tk_widget() + canvas_widget.pack(expand=True, fill=tk.BOTH) + + # 左侧:EOTF 曲线 + self.eotf_ax = self.eotf_fig.add_axes([0.08, 0.12, 0.50, 0.78]) + self.eotf_ax.set_xlabel("灰阶 (%)", fontsize=10) + self.eotf_ax.set_ylabel("L_bar (归一化亮度)", fontsize=10) + self.eotf_ax.set_xlim(0, 105) + self.eotf_ax.set_ylim(0, 1.1) + self.eotf_ax.grid(True, linestyle="--", alpha=0.3) + self.eotf_ax.tick_params(labelsize=9) + + # 左侧提示 + self.eotf_ax.text( + 0.5, + 0.5, + "等待测试数据...\n\n将显示:\n• 实测 EOTF 曲线 (蓝色)\n• 理想 PQ 曲线 (红色)", + ha="center", + va="center", + fontsize=11, + color="gray", + transform=self.eotf_ax.transAxes, + bbox=dict( + boxstyle="round,pad=1", facecolor="white", edgecolor="gray", alpha=0.8 + ), + ) + + # 右侧:数据表格 + self.eotf_table_ax = self.eotf_fig.add_axes([0.62, 0.12, 0.35, 0.78]) + self.eotf_table_ax.axis("off") + + # 4列表格数据 + table_data = [ + ["灰阶", "实测亮度\n(cd/m²)", "L_bar\n(计算)", "EOTF γ"], + ["0%", "--", "--", "--"], + ["10%", "--", "--", "--"], + ["20%", "--", "--", "--"], + ["30%", "--", "--", "--"], + ["40%", "--", "--", "--"], + ["50%", "--", "--", "--"], + ["60%", "--", "--", "--"], + ["70%", "--", "--", "--"], + ["80%", "--", "--", "--"], + ["90%", "--", "--", "--"], + ["100%", "--", "--", "--"], + ] + + table = self.eotf_table_ax.table( + cellText=table_data, + cellLoc="center", + loc="center", + colWidths=[0.18, 0.28, 0.27, 0.27], + ) + + table.auto_set_font_size(False) + table.set_fontsize(7.5) + table.scale(1, 1.5) + + # 表头样式 + for i in range(4): + cell = table[(0, i)] + cell.set_facecolor("#4472C4") + cell.set_text_props(weight="bold", color="white", fontsize=7) + + # 数据行交替颜色 + for i in range(1, len(table_data)): + for j in range(4): + cell = table[(i, j)] + if i % 2 == 0: + cell.set_facecolor("#E7E6E6") + else: + cell.set_facecolor("#FFFFFF") + + # 底部说明 + self.eotf_table_ax.text( + 0.5, + 0.02, + "表格说明:\n" + "• 实测亮度: 色度计测量值 (cd/m²)\n" + "• L_bar: 归一化亮度 (0-1)\n" + "• EOTF γ: HDR 实际 Gamma 值", + ha="center", + va="bottom", + fontsize=7, + color="gray", + transform=self.eotf_table_ax.transAxes, + bbox=dict( + boxstyle="round,pad=0.5", + facecolor="lightyellow", + edgecolor="gray", + alpha=0.8, + ), + ) + + self.eotf_fig.suptitle("EOTF 曲线 + 数据表格", fontsize=12, y=0.98) + self.eotf_canvas.draw() + +def init_cct_chart(app): + """初始化色度坐标图表 - 正向横坐标,标题居中最上方""" + self = app + container = ttk.Frame(self.cct_chart_frame) + container.pack(expand=True) + + self.cct_fig = plt.Figure(figsize=(8, 6), dpi=100, tight_layout=False) + self.cct_canvas = FigureCanvasTkAgg(self.cct_fig, master=container) + + canvas_widget = self.cct_canvas.get_tk_widget() + canvas_widget.pack() + canvas_widget.config(width=800, height=600) + canvas_widget.pack_propagate(False) + + self.cct_ax1 = self.cct_fig.add_subplot(211) + self.cct_ax2 = self.cct_fig.add_subplot(212) + + # 上图:x coordinates + self.cct_ax1.set_xlabel("灰阶 (%)", fontsize=9) + self.cct_ax1.set_ylabel("CIE x", fontsize=9) + self.cct_ax1.set_xlim(0, 105) + self.cct_ax1.set_ylim(0.25, 0.35) + self.cct_ax1.grid(True, linestyle="--", alpha=0.3) + self.cct_ax1.tick_params(labelsize=8) + + # 下图:y coordinates + self.cct_ax2.set_xlabel("灰阶 (%)", fontsize=9) + self.cct_ax2.set_ylabel("CIE y", fontsize=9) + self.cct_ax2.set_xlim(0, 105) + self.cct_ax2.set_ylim(0.25, 0.35) + self.cct_ax2.grid(True, linestyle="--", alpha=0.3) + self.cct_ax2.tick_params(labelsize=8) + + # 调整标题位置:y=0.985(比色域/Gamma略高) + self.cct_fig.suptitle("色度一致性测试", fontsize=12, y=0.985) + + self.cct_fig.subplots_adjust( + left=0.12, + right=0.88, + top=0.90, + bottom=0.08, + hspace=0.25, + ) + + self.cct_canvas.draw() + +def init_contrast_chart(app): + """初始化对比度图表 - 固定大小,居中显示""" + self = app + container = ttk.Frame(self.contrast_chart_frame) + container.pack(expand=True) + + self.contrast_fig = plt.Figure( + figsize=(6, 6), + dpi=100, + tight_layout=False, + ) + self.contrast_canvas = FigureCanvasTkAgg(self.contrast_fig, master=container) + + canvas_widget = self.contrast_canvas.get_tk_widget() + canvas_widget.pack() + + canvas_widget.config(width=600, height=600) + canvas_widget.pack_propagate(False) + + self.contrast_ax = self.contrast_fig.add_subplot(111) + self.contrast_ax.set_xlim(0, 1) + self.contrast_ax.set_ylim(0, 1) + self.contrast_ax.axis("off") + + # 调整标题位置:y=0.985 + self.contrast_fig.suptitle("对比度测试", fontsize=12, y=0.985) + + self.contrast_fig.subplots_adjust( + left=0.02, + right=0.98, + top=0.90, + bottom=0.02, + ) + + self.contrast_canvas.draw() + +def init_accuracy_chart(app): + """初始化色准图表 - 固定大小,居中显示""" + self = app + container = ttk.Frame(self.accuracy_chart_frame) + container.pack(expand=True) + + self.accuracy_fig = plt.Figure( + figsize=(10, 6), + dpi=100, + tight_layout=False, + ) + self.accuracy_canvas = FigureCanvasTkAgg(self.accuracy_fig, master=container) + + canvas_widget = self.accuracy_canvas.get_tk_widget() + canvas_widget.pack() + + canvas_widget.config(width=1000, height=600) + canvas_widget.pack_propagate(False) + + self.accuracy_ax = self.accuracy_fig.add_subplot(111) + self.accuracy_ax.set_xlim(0, 1) + self.accuracy_ax.set_ylim(0, 1) + self.accuracy_ax.axis("off") + + # 调整标题位置 + self.accuracy_fig.suptitle("色准测试", fontsize=12, y=0.985) + + self.accuracy_fig.subplots_adjust( + left=0.05, + right=0.95, + top=0.90, + bottom=0.05, + ) + + self.accuracy_canvas.draw() + +def clear_chart(app): + """清空所有图表""" + self = app + + # ========== 1. 清空色域图表 ========== + if hasattr(self, "gamut_ax_xy") and hasattr(self, "gamut_ax_uv"): + # 清空XY图 + self.gamut_ax_xy.clear() + self.gamut_ax_xy.set_xlim(0, 600) + self.gamut_ax_xy.set_ylim(600, 0) + self.gamut_ax_xy.axis("off") + self.gamut_ax_xy.set_clip_on(False) + + # 清空UV图 + self.gamut_ax_uv.clear() + self.gamut_ax_uv.set_xlim(0, 600) + self.gamut_ax_uv.set_ylim(600, 0) + self.gamut_ax_uv.axis("off") + self.gamut_ax_uv.set_clip_on(False) + + self.gamut_fig.suptitle("色域测试", fontsize=12, y=0.98) + self.gamut_canvas.draw() + + # ========== 2. 清空Gamma图表(4列 + 通用说明)========== + if hasattr(self, "gamma_ax") and hasattr(self, "gamma_table_ax"): + # 清空左侧曲线 + self.gamma_ax.clear() + self.gamma_ax.set_xlim(0, 105) + self.gamma_ax.set_ylim(0, 1.1) + self.gamma_ax.set_xlabel("灰阶 (%)", fontsize=10) + self.gamma_ax.set_ylabel("L_bar", fontsize=10) + self.gamma_ax.grid(True, linestyle="--", alpha=0.3) + self.gamma_ax.tick_params(labelsize=9) + + # 左侧提示 + self.gamma_ax.text( + 0.5, + 0.5, + "等待测试数据...\n\n" + "将显示:\n" + "• 实测曲线 (蓝色)\n" + "• 理想 Gamma 曲线 (红色)\n\n" + "Gamma 值由测试配置决定", + ha="center", + va="center", + fontsize=10, + color="gray", + transform=self.gamma_ax.transAxes, + bbox=dict( + boxstyle="round,pad=1", + facecolor="white", + edgecolor="gray", + alpha=0.8, + ), + ) + + # 清空右侧表格 + self.gamma_table_ax.clear() + self.gamma_table_ax.axis("off") + + # 4列表格 + table_data = [ + ["灰阶", "实测亮度\n(cd/m²)", "L_bar\n(计算)", "Gamma"], + ["0%", "--", "--", "--"], + ["10%", "--", "--", "--"], + ["20%", "--", "--", "--"], + ["30%", "--", "--", "--"], + ["40%", "--", "--", "--"], + ["50%", "--", "--", "--"], + ["60%", "--", "--", "--"], + ["70%", "--", "--", "--"], + ["80%", "--", "--", "--"], + ["90%", "--", "--", "--"], + ["100%", "--", "--", "--"], + ] + + table = self.gamma_table_ax.table( + cellText=table_data, + cellLoc="center", + loc="center", + colWidths=[0.18, 0.28, 0.27, 0.27], + ) + + table.auto_set_font_size(False) + table.set_fontsize(7.5) + table.scale(1, 1.5) + + # 表头样式 + for i in range(4): + cell = table[(0, i)] + cell.set_facecolor("#4472C4") + cell.set_text_props(weight="bold", color="white", fontsize=7) + + # 数据行交替颜色 + for i in range(1, len(table_data)): + for j in range(4): + cell = table[(i, j)] + if i % 2 == 0: + cell.set_facecolor("#E7E6E6") + else: + cell.set_facecolor("#FFFFFF") + + # 底部说明 + self.gamma_table_ax.text( + 0.5, + 0.02, + "表格说明:\n" + "• 实测亮度: 色度计测量值 (cd/m²)\n" + "• L_bar: 归一化亮度 (0-1)\n" + "• Gamma: 实际 Gamma 值", + ha="center", + va="bottom", + fontsize=7, + color="gray", + transform=self.gamma_table_ax.transAxes, + bbox=dict( + boxstyle="round,pad=0.5", + facecolor="lightyellow", + edgecolor="gray", + alpha=0.8, + ), + ) + + self.gamma_fig.suptitle("Gamma曲线 + 数据表格", fontsize=12, y=0.98) + self.gamma_canvas.draw() + + # ========== 3. 清空EOTF图表(4列)========== + if hasattr(self, "eotf_ax") and hasattr(self, "eotf_table_ax"): + # 清空左侧曲线 + self.eotf_ax.clear() + self.eotf_ax.set_xlim(0, 105) + self.eotf_ax.set_ylim(0, 1.1) + self.eotf_ax.set_xlabel("灰阶 (%)", fontsize=10) + self.eotf_ax.set_ylabel("L_bar (归一化亮度)", fontsize=10) + self.eotf_ax.grid(True, linestyle="--", alpha=0.3) + self.eotf_ax.tick_params(labelsize=9) + + # 左侧提示 + self.eotf_ax.text( + 0.5, + 0.5, + "等待测试数据...\n\n将显示:\n• 实测 EOTF 曲线 (蓝色)\n• 理想 PQ 曲线 (红色)", + ha="center", + va="center", + fontsize=11, + color="gray", + transform=self.eotf_ax.transAxes, + bbox=dict( + boxstyle="round,pad=1", + facecolor="white", + edgecolor="gray", + alpha=0.8, + ), + ) + + # 清空右侧表格 + self.eotf_table_ax.clear() + self.eotf_table_ax.axis("off") + + # 4列表格 + table_data = [ + ["灰阶", "实测亮度\n(cd/m²)", "L_bar\n(计算)", "EOTF γ"], + ["0%", "--", "--", "--"], + ["10%", "--", "--", "--"], + ["20%", "--", "--", "--"], + ["30%", "--", "--", "--"], + ["40%", "--", "--", "--"], + ["50%", "--", "--", "--"], + ["60%", "--", "--", "--"], + ["70%", "--", "--", "--"], + ["80%", "--", "--", "--"], + ["90%", "--", "--", "--"], + ["100%", "--", "--", "--"], + ] + + table = self.eotf_table_ax.table( + cellText=table_data, + cellLoc="center", + loc="center", + colWidths=[0.18, 0.28, 0.27, 0.27], + ) + + table.auto_set_font_size(False) + table.set_fontsize(7.5) + table.scale(1, 1.5) + + # 表头样式 + for i in range(4): + cell = table[(0, i)] + cell.set_facecolor("#4472C4") + cell.set_text_props(weight="bold", color="white", fontsize=7) + + # 数据行交替颜色 + for i in range(1, len(table_data)): + for j in range(4): + cell = table[(i, j)] + if i % 2 == 0: + cell.set_facecolor("#E7E6E6") + else: + cell.set_facecolor("#FFFFFF") + + # 底部说明 + self.eotf_table_ax.text( + 0.5, + 0.02, + "表格说明:\n" + "• 实测亮度: 色度计测量值 (cd/m²)\n" + "• L_bar: 归一化亮度 (0-1)\n" + "• EOTF γ: HDR 实际 Gamma 值", + ha="center", + va="bottom", + fontsize=7, + color="gray", + transform=self.eotf_table_ax.transAxes, + bbox=dict( + boxstyle="round,pad=0.5", + facecolor="lightyellow", + edgecolor="gray", + alpha=0.8, + ), + ) + + self.eotf_fig.suptitle("EOTF 曲线 + 数据表格", fontsize=12, y=0.98) + self.eotf_canvas.draw() + + # ========== 4. 清空色度图表 ========== + if hasattr(self, "cct_ax1") and hasattr(self, "cct_ax2"): + # 上图:x coordinates + self.cct_ax1.clear() + self.cct_ax1.set_xlabel("灰阶 (%)", fontsize=9) + self.cct_ax1.set_ylabel("CIE x", fontsize=9) + self.cct_ax1.set_xlim(0, 105) + self.cct_ax1.set_ylim(0.25, 0.35) + self.cct_ax1.grid(True, linestyle="--", alpha=0.3) + self.cct_ax1.tick_params(labelsize=8) + + # 下图:y coordinates + self.cct_ax2.clear() + self.cct_ax2.set_xlabel("灰阶 (%)", fontsize=9) + self.cct_ax2.set_ylabel("CIE y", fontsize=9) + self.cct_ax2.set_xlim(0, 105) + self.cct_ax2.set_ylim(0.25, 0.35) + self.cct_ax2.grid(True, linestyle="--", alpha=0.3) + self.cct_ax2.tick_params(labelsize=8) + + self.cct_fig.suptitle("色度一致性测试", fontsize=12, y=0.985) + + # 重置布局 + self.cct_fig.subplots_adjust( + left=0.12, + right=0.88, + top=0.90, + bottom=0.08, + hspace=0.25, + ) + + self.cct_canvas.draw() + + # ========== 5. 清空对比度图表 ========== + if hasattr(self, "contrast_ax"): + self.contrast_ax.clear() + self.contrast_ax.set_xlim(0, 1) + self.contrast_ax.set_ylim(0, 1) + self.contrast_ax.axis("off") + + self.contrast_fig.suptitle("对比度测试", fontsize=12, y=0.985) + + # 重置布局 + self.contrast_fig.subplots_adjust( + left=0.02, + right=0.98, + top=0.90, + bottom=0.02, + ) + + self.contrast_canvas.draw() + + # ========== 6. 清空色准图表 ========== + if hasattr(self, "accuracy_ax"): + self.accuracy_ax.clear() + self.accuracy_ax.set_xlim(0, 1) + self.accuracy_ax.set_ylim(0, 1) + self.accuracy_ax.axis("off") + + # 标题 + self.accuracy_fig.suptitle("色准测试", fontsize=12, y=0.985) + + # 重置布局 + self.accuracy_fig.subplots_adjust( + left=0.05, + right=0.95, + top=0.90, + bottom=0.05, + ) + + self.accuracy_canvas.draw() + +def update_chart_tabs_state(app): + """根据测试项目复选框状态动态增删图表 Tab(保持规范顺序)。 + + - 色域 / Gamma 或 EOTF / 色度一致性 / 对比度 / 色准 全部走动态 add/forget + - Gamma 与 EOTF 二选一,由 current_test_type 决定 + - 屏模组测试强制隐藏色准 Tab + - 客户模板 Tab 由 change_test_type 独立管理,这里不动 + """ + self = app + if not hasattr(self, "chart_notebook"): + return + + selected_items = self.get_selected_test_items() + current_test_type = self.config.current_test_type + + # 根据测试类型决定 gamma/eotf 显示哪一个 + if current_test_type == "hdr_movie": + gamma_like_frame = self.eotf_chart_frame + gamma_like_text = "EOTF 曲线" + gamma_like_other = self.gamma_chart_frame + else: + gamma_like_frame = self.gamma_chart_frame + gamma_like_text = "Gamma 曲线" + gamma_like_other = self.eotf_chart_frame + + want_gamut = "gamut" in selected_items + want_gamma_like = "gamma" in selected_items or "eotf" in selected_items + want_cct = "cct" in selected_items + want_contrast = "contrast" in selected_items + want_accuracy = ( + "accuracy" in selected_items and current_test_type != "screen_module" + ) + + # 规范顺序:色域 → Gamma/EOTF → 色度一致性 → 对比度 → 色准 + spec = [ + (want_gamut, self.gamut_chart_frame, "色域图"), + (want_gamma_like, gamma_like_frame, gamma_like_text), + (want_cct, self.cct_chart_frame, "色度一致性"), + (want_contrast, self.contrast_chart_frame, "对比度"), + (want_accuracy, self.accuracy_chart_frame, "色准"), + ] + + try: + # 始终先把"另一个" gamma/eotf frame 从 Notebook 移除,保持互斥 + current_ids = list(self.chart_notebook.tabs()) + if str(gamma_like_other) in current_ids: + self.chart_notebook.forget(gamma_like_other) + + # 按规范顺序处理 add/forget + for idx_in_spec, (want, frame, text) in enumerate(spec): + fid = str(frame) + present = fid in self.chart_notebook.tabs() + + if want and not present: + # 统计该 frame 在 spec 中前面、当前实际存在的 tab 数 → 插入位置 + current_ids = list(self.chart_notebook.tabs()) + pos = sum( + 1 for pre_want, pre_frame, _ in spec[:idx_in_spec] + if str(pre_frame) in current_ids + ) + try: + self.chart_notebook.insert(pos, frame, text=text) + except Exception: + # fallback:尾部 add + self.chart_notebook.add(frame, text=text) + + elif not want and present: + self.chart_notebook.forget(frame) + + except Exception as e: + if hasattr(self, "log_gui"): + self.log_gui.log(f"更新Tab状态失败: {str(e)}") + +def create_result_chart_frame(app): + """创建结果图表区域 - 6个独立Tab(Gamma 和 EOTF 分离)""" + self = app + # 创建Notebook用于图表切换 + self.chart_notebook = ttk.Notebook(self.result_frame) + self.chart_notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) + + # ========== 创建6个独立的Tab页面 ========== + # 1. 色域图页面 + self.gamut_chart_frame = ttk.Frame(self.chart_notebook) + + # 2. Gamma图页面(SDR/屏模组使用) + self.gamma_chart_frame = ttk.Frame(self.chart_notebook) + + # 3. EOTF图页面(HDR专用) + self.eotf_chart_frame = ttk.Frame(self.chart_notebook) + + # 4. 色度一致性页面 + self.cct_chart_frame = ttk.Frame(self.chart_notebook) + + # 5. 对比度页面 + self.contrast_chart_frame = ttk.Frame(self.chart_notebook) + + # 6. 色准页面 + self.accuracy_chart_frame = ttk.Frame(self.chart_notebook) + + # 7. 客户模板结果页面 + self.custom_template_tab_frame = ttk.Frame(self.chart_notebook) + + # ========== 添加到Notebook(初始只添加前5个)========== + self.chart_notebook.add(self.gamut_chart_frame, text="色域图") + self.chart_notebook.add(self.gamma_chart_frame, text="Gamma曲线") + # ← EOTF 不添加,由 change_test_type() 动态控制 + self.chart_notebook.add(self.cct_chart_frame, text="色度一致性") + self.chart_notebook.add(self.contrast_chart_frame, text="对比度") + self.chart_notebook.add(self.accuracy_chart_frame, text="色准") + + # 初始化六个独立的图表 + self.init_gamut_chart() + self.init_gamma_chart() + self.init_eotf_chart() + self.init_cct_chart() + self.init_contrast_chart() + self.init_accuracy_chart() + + # 绑定Tab切换事件 + self.chart_notebook.bind("<>", self.on_chart_tab_changed) + + # ==================== ✅ 在图表下方创建单步调试面板 ==================== + self.debug_container = ttk.LabelFrame( + self.result_frame, # ← 放在 result_frame 内,图表正下方 + text="🔧 单步调试", + padding=10, + ) + # 默认不显示 + + # 创建单步调试面板实例 + self.debug_panel = PQDebugPanel(self.debug_container, self) + + self.log_gui.log("✓ 单步调试面板已创建(放在测试结果图表下方)") + +def on_chart_tab_changed(app, event): + """Tab切换时的事件处理""" + self = app + try: + self._last_tab_index = self.chart_notebook.index( + self.chart_notebook.select() + ) + except Exception as e: + self.log_gui.log(f"Tab切换事件处理失败: {str(e)}") + diff --git a/pqAutomationApp.py b/pqAutomationApp.py index ceb51f1..9ebcfc0 100644 --- a/pqAutomationApp.py +++ b/pqAutomationApp.py @@ -53,6 +53,18 @@ from app.plots.plot_contrast import plot_contrast as _plot_contrast from app.plots.plot_eotf import plot_eotf as _plot_eotf from app.plots.plot_gamma import plot_gamma as _plot_gamma from app.plots.plot_gamut import plot_gamut as _plot_gamut +from app.views.chart_frame import ( + clear_chart as _cf_clear_chart, + create_result_chart_frame as _cf_create_result_chart_frame, + init_accuracy_chart as _cf_init_accuracy_chart, + init_cct_chart as _cf_init_cct_chart, + init_contrast_chart as _cf_init_contrast_chart, + init_eotf_chart as _cf_init_eotf_chart, + init_gamma_chart as _cf_init_gamma_chart, + init_gamut_chart as _cf_init_gamut_chart, + on_chart_tab_changed as _cf_on_chart_tab_changed, + update_chart_tabs_state as _cf_update_chart_tabs_state, +) plt.rcParams["font.family"] = ["sans-serif"] plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"] @@ -210,666 +222,26 @@ class PQAutomationApp: self.log_gui.log(f"初始化默认测试类型失败: {str(e)}") def init_gamut_chart(self): - """初始化色域图表 - 手动设置subplot位置,完全避免重叠""" - container = ttk.Frame(self.gamut_chart_frame) - container.pack(expand=True, fill=tk.BOTH) - - self.gamut_fig = plt.Figure(figsize=(14, 6), dpi=100) - self.gamut_canvas = FigureCanvasTkAgg(self.gamut_fig, master=container) - - canvas_widget = self.gamut_canvas.get_tk_widget() - canvas_widget.pack(expand=True, fill=tk.BOTH) - - # ✅ 恢复原来的大尺寸:0.84 高度 - self.gamut_ax_xy = self.gamut_fig.add_axes( - [0.02, 0.08, 0.46, 0.84] - ) # ← 改回 0.84 - self.gamut_ax_uv = self.gamut_fig.add_axes( - [0.52, 0.08, 0.46, 0.84] - ) # ← 改回 0.84 - - # 初始化XY图 - self.gamut_ax_xy.set_xlim(0, 600) - self.gamut_ax_xy.set_ylim(600, 0) - self.gamut_ax_xy.axis("off") - self.gamut_ax_xy.set_clip_on(False) - - # 初始化UV图 - self.gamut_ax_uv.set_xlim(0, 600) - self.gamut_ax_uv.set_ylim(600, 0) - self.gamut_ax_uv.axis("off") - self.gamut_ax_uv.set_clip_on(False) - - # 调整标题位置:y=0.98 - self.gamut_fig.suptitle("色域测试", fontsize=12, y=0.98) - - self.gamut_canvas.draw() - + """转发到 app.views.chart_frame.init_gamut_chart(Step 3 重构)""" + return _cf_init_gamut_chart(self) def init_gamma_chart(self): - """初始化Gamma曲线图表 - 左侧曲线 + 右侧表格(✅ 4列 + 通用说明)""" - container = ttk.Frame(self.gamma_chart_frame) - container.pack(expand=True, fill=tk.BOTH) - - self.gamma_fig = plt.Figure(figsize=(12, 6), dpi=100, constrained_layout=False) - self.gamma_canvas = FigureCanvasTkAgg(self.gamma_fig, master=container) - - canvas_widget = self.gamma_canvas.get_tk_widget() - canvas_widget.pack(expand=True, fill=tk.BOTH) - - # 左侧:Gamma 曲线 - self.gamma_ax = self.gamma_fig.add_axes([0.08, 0.12, 0.50, 0.78]) - self.gamma_ax.set_xlabel("灰阶 (%)", fontsize=10) - self.gamma_ax.set_ylabel("L_bar", fontsize=10) - self.gamma_ax.set_xlim(0, 105) - self.gamma_ax.set_ylim(0, 1.1) - self.gamma_ax.grid(True, linestyle="--", alpha=0.3) - self.gamma_ax.tick_params(labelsize=9) - - # 左侧提示(通用说明,不显示具体 Gamma 值) - self.gamma_ax.text( - 0.5, - 0.5, - "等待测试数据...\n\n" - "将显示:\n" - "• 实测曲线 (蓝色)\n" - "• 理想 Gamma 曲线 (红色)\n\n" - "Gamma 值由测试配置决定", - ha="center", - va="center", - fontsize=10, - color="gray", - transform=self.gamma_ax.transAxes, - bbox=dict( - boxstyle="round,pad=1", facecolor="white", edgecolor="gray", alpha=0.8 - ), - ) - - # 右侧:数据表格 - self.gamma_table_ax = self.gamma_fig.add_axes([0.62, 0.12, 0.35, 0.78]) - self.gamma_table_ax.axis("off") - - # 4列表格数据 - table_data = [ - ["灰阶", "实测亮度\n(cd/m²)", "L_bar\n(计算)", "Gamma"], - ["0%", "--", "--", "--"], - ["10%", "--", "--", "--"], - ["20%", "--", "--", "--"], - ["30%", "--", "--", "--"], - ["40%", "--", "--", "--"], - ["50%", "--", "--", "--"], - ["60%", "--", "--", "--"], - ["70%", "--", "--", "--"], - ["80%", "--", "--", "--"], - ["90%", "--", "--", "--"], - ["100%", "--", "--", "--"], - ] - - table = self.gamma_table_ax.table( - cellText=table_data, - cellLoc="center", - loc="center", - colWidths=[0.18, 0.28, 0.27, 0.27], # ← 4列宽度 - ) - - table.auto_set_font_size(False) - table.set_fontsize(7.5) - table.scale(1, 1.5) - - # 表头样式 - for i in range(4): - cell = table[(0, i)] - cell.set_facecolor("#4472C4") - cell.set_text_props(weight="bold", color="white", fontsize=7) - - # 数据行交替颜色 - for i in range(1, len(table_data)): - for j in range(4): - cell = table[(i, j)] - if i % 2 == 0: - cell.set_facecolor("#E7E6E6") - else: - cell.set_facecolor("#FFFFFF") - - # 底部说明 - self.gamma_table_ax.text( - 0.5, - 0.02, - "表格说明:\n" - "• 实测亮度: 色度计测量值 (cd/m²)\n" - "• L_bar: 归一化亮度 (0-1)\n" - "• Gamma: 实际 Gamma 值", - ha="center", - va="bottom", - fontsize=7, - color="gray", - transform=self.gamma_table_ax.transAxes, - bbox=dict( - boxstyle="round,pad=0.5", - facecolor="lightyellow", - edgecolor="gray", - alpha=0.8, - ), - ) - - self.gamma_fig.suptitle("Gamma曲线 + 数据表格", fontsize=12, y=0.98) - self.gamma_canvas.draw() - + """转发到 app.views.chart_frame.init_gamma_chart(Step 3 重构)""" + return _cf_init_gamma_chart(self) def init_eotf_chart(self): - """初始化 EOTF 曲线图表(HDR 专用)- 左侧曲线 + 右侧表格(✅ 4列)""" - container = ttk.Frame(self.eotf_chart_frame) - container.pack(expand=True, fill=tk.BOTH) - - self.eotf_fig = plt.Figure(figsize=(12, 6), dpi=100, constrained_layout=False) - self.eotf_canvas = FigureCanvasTkAgg(self.eotf_fig, master=container) - - canvas_widget = self.eotf_canvas.get_tk_widget() - canvas_widget.pack(expand=True, fill=tk.BOTH) - - # 左侧:EOTF 曲线 - self.eotf_ax = self.eotf_fig.add_axes([0.08, 0.12, 0.50, 0.78]) - self.eotf_ax.set_xlabel("灰阶 (%)", fontsize=10) - self.eotf_ax.set_ylabel("L_bar (归一化亮度)", fontsize=10) - self.eotf_ax.set_xlim(0, 105) - self.eotf_ax.set_ylim(0, 1.1) - self.eotf_ax.grid(True, linestyle="--", alpha=0.3) - self.eotf_ax.tick_params(labelsize=9) - - # 左侧提示 - self.eotf_ax.text( - 0.5, - 0.5, - "等待测试数据...\n\n将显示:\n• 实测 EOTF 曲线 (蓝色)\n• 理想 PQ 曲线 (红色)", - ha="center", - va="center", - fontsize=11, - color="gray", - transform=self.eotf_ax.transAxes, - bbox=dict( - boxstyle="round,pad=1", facecolor="white", edgecolor="gray", alpha=0.8 - ), - ) - - # 右侧:数据表格 - self.eotf_table_ax = self.eotf_fig.add_axes([0.62, 0.12, 0.35, 0.78]) - self.eotf_table_ax.axis("off") - - # 4列表格数据 - table_data = [ - ["灰阶", "实测亮度\n(cd/m²)", "L_bar\n(计算)", "EOTF γ"], - ["0%", "--", "--", "--"], - ["10%", "--", "--", "--"], - ["20%", "--", "--", "--"], - ["30%", "--", "--", "--"], - ["40%", "--", "--", "--"], - ["50%", "--", "--", "--"], - ["60%", "--", "--", "--"], - ["70%", "--", "--", "--"], - ["80%", "--", "--", "--"], - ["90%", "--", "--", "--"], - ["100%", "--", "--", "--"], - ] - - table = self.eotf_table_ax.table( - cellText=table_data, - cellLoc="center", - loc="center", - colWidths=[0.18, 0.28, 0.27, 0.27], - ) - - table.auto_set_font_size(False) - table.set_fontsize(7.5) - table.scale(1, 1.5) - - # 表头样式 - for i in range(4): - cell = table[(0, i)] - cell.set_facecolor("#4472C4") - cell.set_text_props(weight="bold", color="white", fontsize=7) - - # 数据行交替颜色 - for i in range(1, len(table_data)): - for j in range(4): - cell = table[(i, j)] - if i % 2 == 0: - cell.set_facecolor("#E7E6E6") - else: - cell.set_facecolor("#FFFFFF") - - # 底部说明 - self.eotf_table_ax.text( - 0.5, - 0.02, - "表格说明:\n" - "• 实测亮度: 色度计测量值 (cd/m²)\n" - "• L_bar: 归一化亮度 (0-1)\n" - "• EOTF γ: HDR 实际 Gamma 值", - ha="center", - va="bottom", - fontsize=7, - color="gray", - transform=self.eotf_table_ax.transAxes, - bbox=dict( - boxstyle="round,pad=0.5", - facecolor="lightyellow", - edgecolor="gray", - alpha=0.8, - ), - ) - - self.eotf_fig.suptitle("EOTF 曲线 + 数据表格", fontsize=12, y=0.98) - self.eotf_canvas.draw() - + """转发到 app.views.chart_frame.init_eotf_chart(Step 3 重构)""" + return _cf_init_eotf_chart(self) def init_cct_chart(self): - """初始化色度坐标图表 - 正向横坐标,标题居中最上方""" - container = ttk.Frame(self.cct_chart_frame) - container.pack(expand=True) - - self.cct_fig = plt.Figure(figsize=(8, 6), dpi=100, tight_layout=False) - self.cct_canvas = FigureCanvasTkAgg(self.cct_fig, master=container) - - canvas_widget = self.cct_canvas.get_tk_widget() - canvas_widget.pack() - canvas_widget.config(width=800, height=600) - canvas_widget.pack_propagate(False) - - self.cct_ax1 = self.cct_fig.add_subplot(211) - self.cct_ax2 = self.cct_fig.add_subplot(212) - - # 上图:x coordinates - self.cct_ax1.set_xlabel("灰阶 (%)", fontsize=9) - self.cct_ax1.set_ylabel("CIE x", fontsize=9) - self.cct_ax1.set_xlim(0, 105) - self.cct_ax1.set_ylim(0.25, 0.35) - self.cct_ax1.grid(True, linestyle="--", alpha=0.3) - self.cct_ax1.tick_params(labelsize=8) - - # 下图:y coordinates - self.cct_ax2.set_xlabel("灰阶 (%)", fontsize=9) - self.cct_ax2.set_ylabel("CIE y", fontsize=9) - self.cct_ax2.set_xlim(0, 105) - self.cct_ax2.set_ylim(0.25, 0.35) - self.cct_ax2.grid(True, linestyle="--", alpha=0.3) - self.cct_ax2.tick_params(labelsize=8) - - # 调整标题位置:y=0.985(比色域/Gamma略高) - self.cct_fig.suptitle("色度一致性测试", fontsize=12, y=0.985) - - self.cct_fig.subplots_adjust( - left=0.12, - right=0.88, - top=0.90, - bottom=0.08, - hspace=0.25, - ) - - self.cct_canvas.draw() - + """转发到 app.views.chart_frame.init_cct_chart(Step 3 重构)""" + return _cf_init_cct_chart(self) def init_contrast_chart(self): - """初始化对比度图表 - 固定大小,居中显示""" - container = ttk.Frame(self.contrast_chart_frame) - container.pack(expand=True) - - self.contrast_fig = plt.Figure( - figsize=(6, 6), - dpi=100, - tight_layout=False, - ) - self.contrast_canvas = FigureCanvasTkAgg(self.contrast_fig, master=container) - - canvas_widget = self.contrast_canvas.get_tk_widget() - canvas_widget.pack() - - canvas_widget.config(width=600, height=600) - canvas_widget.pack_propagate(False) - - self.contrast_ax = self.contrast_fig.add_subplot(111) - self.contrast_ax.set_xlim(0, 1) - self.contrast_ax.set_ylim(0, 1) - self.contrast_ax.axis("off") - - # 调整标题位置:y=0.985 - self.contrast_fig.suptitle("对比度测试", fontsize=12, y=0.985) - - self.contrast_fig.subplots_adjust( - left=0.02, - right=0.98, - top=0.90, - bottom=0.02, - ) - - self.contrast_canvas.draw() - + """转发到 app.views.chart_frame.init_contrast_chart(Step 3 重构)""" + return _cf_init_contrast_chart(self) def init_accuracy_chart(self): - """初始化色准图表 - 固定大小,居中显示""" - container = ttk.Frame(self.accuracy_chart_frame) - container.pack(expand=True) - - self.accuracy_fig = plt.Figure( - figsize=(10, 6), - dpi=100, - tight_layout=False, - ) - self.accuracy_canvas = FigureCanvasTkAgg(self.accuracy_fig, master=container) - - canvas_widget = self.accuracy_canvas.get_tk_widget() - canvas_widget.pack() - - canvas_widget.config(width=1000, height=600) - canvas_widget.pack_propagate(False) - - self.accuracy_ax = self.accuracy_fig.add_subplot(111) - self.accuracy_ax.set_xlim(0, 1) - self.accuracy_ax.set_ylim(0, 1) - self.accuracy_ax.axis("off") - - # 调整标题位置 - self.accuracy_fig.suptitle("色准测试", fontsize=12, y=0.985) - - self.accuracy_fig.subplots_adjust( - left=0.05, - right=0.95, - top=0.90, - bottom=0.05, - ) - - self.accuracy_canvas.draw() - + """转发到 app.views.chart_frame.init_accuracy_chart(Step 3 重构)""" + return _cf_init_accuracy_chart(self) def clear_chart(self): - """清空所有图表""" - - # ========== 1. 清空色域图表 ========== - if hasattr(self, "gamut_ax_xy") and hasattr(self, "gamut_ax_uv"): - # 清空XY图 - self.gamut_ax_xy.clear() - self.gamut_ax_xy.set_xlim(0, 600) - self.gamut_ax_xy.set_ylim(600, 0) - self.gamut_ax_xy.axis("off") - self.gamut_ax_xy.set_clip_on(False) - - # 清空UV图 - self.gamut_ax_uv.clear() - self.gamut_ax_uv.set_xlim(0, 600) - self.gamut_ax_uv.set_ylim(600, 0) - self.gamut_ax_uv.axis("off") - self.gamut_ax_uv.set_clip_on(False) - - self.gamut_fig.suptitle("色域测试", fontsize=12, y=0.98) - self.gamut_canvas.draw() - - # ========== 2. 清空Gamma图表(4列 + 通用说明)========== - if hasattr(self, "gamma_ax") and hasattr(self, "gamma_table_ax"): - # 清空左侧曲线 - self.gamma_ax.clear() - self.gamma_ax.set_xlim(0, 105) - self.gamma_ax.set_ylim(0, 1.1) - self.gamma_ax.set_xlabel("灰阶 (%)", fontsize=10) - self.gamma_ax.set_ylabel("L_bar", fontsize=10) - self.gamma_ax.grid(True, linestyle="--", alpha=0.3) - self.gamma_ax.tick_params(labelsize=9) - - # 左侧提示 - self.gamma_ax.text( - 0.5, - 0.5, - "等待测试数据...\n\n" - "将显示:\n" - "• 实测曲线 (蓝色)\n" - "• 理想 Gamma 曲线 (红色)\n\n" - "Gamma 值由测试配置决定", - ha="center", - va="center", - fontsize=10, - color="gray", - transform=self.gamma_ax.transAxes, - bbox=dict( - boxstyle="round,pad=1", - facecolor="white", - edgecolor="gray", - alpha=0.8, - ), - ) - - # 清空右侧表格 - self.gamma_table_ax.clear() - self.gamma_table_ax.axis("off") - - # 4列表格 - table_data = [ - ["灰阶", "实测亮度\n(cd/m²)", "L_bar\n(计算)", "Gamma"], - ["0%", "--", "--", "--"], - ["10%", "--", "--", "--"], - ["20%", "--", "--", "--"], - ["30%", "--", "--", "--"], - ["40%", "--", "--", "--"], - ["50%", "--", "--", "--"], - ["60%", "--", "--", "--"], - ["70%", "--", "--", "--"], - ["80%", "--", "--", "--"], - ["90%", "--", "--", "--"], - ["100%", "--", "--", "--"], - ] - - table = self.gamma_table_ax.table( - cellText=table_data, - cellLoc="center", - loc="center", - colWidths=[0.18, 0.28, 0.27, 0.27], - ) - - table.auto_set_font_size(False) - table.set_fontsize(7.5) - table.scale(1, 1.5) - - # 表头样式 - for i in range(4): - cell = table[(0, i)] - cell.set_facecolor("#4472C4") - cell.set_text_props(weight="bold", color="white", fontsize=7) - - # 数据行交替颜色 - for i in range(1, len(table_data)): - for j in range(4): - cell = table[(i, j)] - if i % 2 == 0: - cell.set_facecolor("#E7E6E6") - else: - cell.set_facecolor("#FFFFFF") - - # 底部说明 - self.gamma_table_ax.text( - 0.5, - 0.02, - "表格说明:\n" - "• 实测亮度: 色度计测量值 (cd/m²)\n" - "• L_bar: 归一化亮度 (0-1)\n" - "• Gamma: 实际 Gamma 值", - ha="center", - va="bottom", - fontsize=7, - color="gray", - transform=self.gamma_table_ax.transAxes, - bbox=dict( - boxstyle="round,pad=0.5", - facecolor="lightyellow", - edgecolor="gray", - alpha=0.8, - ), - ) - - self.gamma_fig.suptitle("Gamma曲线 + 数据表格", fontsize=12, y=0.98) - self.gamma_canvas.draw() - - # ========== 3. 清空EOTF图表(4列)========== - if hasattr(self, "eotf_ax") and hasattr(self, "eotf_table_ax"): - # 清空左侧曲线 - self.eotf_ax.clear() - self.eotf_ax.set_xlim(0, 105) - self.eotf_ax.set_ylim(0, 1.1) - self.eotf_ax.set_xlabel("灰阶 (%)", fontsize=10) - self.eotf_ax.set_ylabel("L_bar (归一化亮度)", fontsize=10) - self.eotf_ax.grid(True, linestyle="--", alpha=0.3) - self.eotf_ax.tick_params(labelsize=9) - - # 左侧提示 - self.eotf_ax.text( - 0.5, - 0.5, - "等待测试数据...\n\n将显示:\n• 实测 EOTF 曲线 (蓝色)\n• 理想 PQ 曲线 (红色)", - ha="center", - va="center", - fontsize=11, - color="gray", - transform=self.eotf_ax.transAxes, - bbox=dict( - boxstyle="round,pad=1", - facecolor="white", - edgecolor="gray", - alpha=0.8, - ), - ) - - # 清空右侧表格 - self.eotf_table_ax.clear() - self.eotf_table_ax.axis("off") - - # 4列表格 - table_data = [ - ["灰阶", "实测亮度\n(cd/m²)", "L_bar\n(计算)", "EOTF γ"], - ["0%", "--", "--", "--"], - ["10%", "--", "--", "--"], - ["20%", "--", "--", "--"], - ["30%", "--", "--", "--"], - ["40%", "--", "--", "--"], - ["50%", "--", "--", "--"], - ["60%", "--", "--", "--"], - ["70%", "--", "--", "--"], - ["80%", "--", "--", "--"], - ["90%", "--", "--", "--"], - ["100%", "--", "--", "--"], - ] - - table = self.eotf_table_ax.table( - cellText=table_data, - cellLoc="center", - loc="center", - colWidths=[0.18, 0.28, 0.27, 0.27], - ) - - table.auto_set_font_size(False) - table.set_fontsize(7.5) - table.scale(1, 1.5) - - # 表头样式 - for i in range(4): - cell = table[(0, i)] - cell.set_facecolor("#4472C4") - cell.set_text_props(weight="bold", color="white", fontsize=7) - - # 数据行交替颜色 - for i in range(1, len(table_data)): - for j in range(4): - cell = table[(i, j)] - if i % 2 == 0: - cell.set_facecolor("#E7E6E6") - else: - cell.set_facecolor("#FFFFFF") - - # 底部说明 - self.eotf_table_ax.text( - 0.5, - 0.02, - "表格说明:\n" - "• 实测亮度: 色度计测量值 (cd/m²)\n" - "• L_bar: 归一化亮度 (0-1)\n" - "• EOTF γ: HDR 实际 Gamma 值", - ha="center", - va="bottom", - fontsize=7, - color="gray", - transform=self.eotf_table_ax.transAxes, - bbox=dict( - boxstyle="round,pad=0.5", - facecolor="lightyellow", - edgecolor="gray", - alpha=0.8, - ), - ) - - self.eotf_fig.suptitle("EOTF 曲线 + 数据表格", fontsize=12, y=0.98) - self.eotf_canvas.draw() - - # ========== 4. 清空色度图表 ========== - if hasattr(self, "cct_ax1") and hasattr(self, "cct_ax2"): - # 上图:x coordinates - self.cct_ax1.clear() - self.cct_ax1.set_xlabel("灰阶 (%)", fontsize=9) - self.cct_ax1.set_ylabel("CIE x", fontsize=9) - self.cct_ax1.set_xlim(0, 105) - self.cct_ax1.set_ylim(0.25, 0.35) - self.cct_ax1.grid(True, linestyle="--", alpha=0.3) - self.cct_ax1.tick_params(labelsize=8) - - # 下图:y coordinates - self.cct_ax2.clear() - self.cct_ax2.set_xlabel("灰阶 (%)", fontsize=9) - self.cct_ax2.set_ylabel("CIE y", fontsize=9) - self.cct_ax2.set_xlim(0, 105) - self.cct_ax2.set_ylim(0.25, 0.35) - self.cct_ax2.grid(True, linestyle="--", alpha=0.3) - self.cct_ax2.tick_params(labelsize=8) - - self.cct_fig.suptitle("色度一致性测试", fontsize=12, y=0.985) - - # 重置布局 - self.cct_fig.subplots_adjust( - left=0.12, - right=0.88, - top=0.90, - bottom=0.08, - hspace=0.25, - ) - - self.cct_canvas.draw() - - # ========== 5. 清空对比度图表 ========== - if hasattr(self, "contrast_ax"): - self.contrast_ax.clear() - self.contrast_ax.set_xlim(0, 1) - self.contrast_ax.set_ylim(0, 1) - self.contrast_ax.axis("off") - - self.contrast_fig.suptitle("对比度测试", fontsize=12, y=0.985) - - # 重置布局 - self.contrast_fig.subplots_adjust( - left=0.02, - right=0.98, - top=0.90, - bottom=0.02, - ) - - self.contrast_canvas.draw() - - # ========== 6. 清空色准图表 ========== - if hasattr(self, "accuracy_ax"): - self.accuracy_ax.clear() - self.accuracy_ax.set_xlim(0, 1) - self.accuracy_ax.set_ylim(0, 1) - self.accuracy_ax.axis("off") - - # 标题 - self.accuracy_fig.suptitle("色准测试", fontsize=12, y=0.985) - - # 重置布局 - self.accuracy_fig.subplots_adjust( - left=0.05, - right=0.95, - top=0.90, - bottom=0.05, - ) - - self.accuracy_canvas.draw() - + """转发到 app.views.chart_frame.clear_chart(Step 3 重构)""" + return _cf_clear_chart(self) def create_floating_config_panel(self): """创建右上角悬浮配置框""" cf = CollapsingFrame(self.control_frame_top) @@ -1477,7 +849,7 @@ class PQAutomationApp: pass # 3. 跳转到色度图Tab - self.chart_notebook.select(2) # ← 色度图是第3个Tab(索引2) + self.chart_notebook.select(self.cct_chart_frame) self.root.update_idletasks() # 4. 检查是否有数据 @@ -1538,7 +910,7 @@ class PQAutomationApp: pass # 2. 跳转到色域图Tab - self.chart_notebook.select(0) # 色域图是第1个Tab + self.chart_notebook.select(self.gamut_chart_frame) self.root.update_idletasks() # 3. 检查是否有数据 @@ -1854,83 +1226,8 @@ class PQAutomationApp: self.update_chart_tabs_state() def update_chart_tabs_state(self): - """根据测试项目复选框状态更新图表Tab的启用/禁用""" - if not hasattr(self, "chart_notebook"): - return - - selected_items = self.get_selected_test_items() - current_test_type = self.config.current_test_type - - # 检查5个独立图表的选中状态 - gamut_selected = "gamut" in selected_items - gamma_selected = "gamma" in selected_items or "eotf" in selected_items - cct_selected = "cct" in selected_items - contrast_selected = "contrast" in selected_items - accuracy_selected = "accuracy" in selected_items - - # 屏模组测试时,强制隐藏色准 Tab - if current_test_type == "screen_module": - accuracy_selected = False - - try: - # 获取当前所有 Tab - current_tabs = self.chart_notebook.tabs() - current_tab_set = set(current_tabs) - - gamut_tab_id = str(self.gamut_chart_frame) - gamma_tab_id = str(self.gamma_chart_frame) - eotf_tab_id = str(self.eotf_chart_frame) - cct_tab_id = str(self.cct_chart_frame) - contrast_tab_id = str(self.contrast_chart_frame) - accuracy_tab_id = str(self.accuracy_chart_frame) - - # ========== 控制前4个固定 Tab ========== - if gamut_tab_id in current_tab_set: - self.chart_notebook.tab( - gamut_tab_id, state="normal" if gamut_selected else "disabled" - ) - - if gamma_tab_id in current_tab_set: - self.chart_notebook.tab( - gamma_tab_id, state="normal" if gamma_selected else "disabled" - ) - elif eotf_tab_id in current_tab_set: - self.chart_notebook.tab( - eotf_tab_id, state="normal" if gamma_selected else "disabled" - ) - - if cct_tab_id in current_tab_set: - self.chart_notebook.tab( - cct_tab_id, state="normal" if cct_selected else "disabled" - ) - - if contrast_tab_id in current_tab_set: - self.chart_notebook.tab( - contrast_tab_id, - state="normal" if contrast_selected else "disabled", - ) - - # ========== 控制色准 Tab(动态添加/移除)========== - accuracy_tab_exists = accuracy_tab_id in current_tab_set - - if accuracy_selected and not accuracy_tab_exists: - # 需要显示色准,但当前没有 → 添加 - self.chart_notebook.add(self.accuracy_chart_frame, text="色准") - self.chart_notebook.tab(accuracy_tab_id, state="normal") - self.log_gui.log("✓ 色准 Tab 已显示") - - elif accuracy_selected and accuracy_tab_exists: - # 需要显示色准,且已存在 → 启用 - self.chart_notebook.tab(accuracy_tab_id, state="normal") - - elif not accuracy_selected and accuracy_tab_exists: - # 不需要显示色准,但存在 → 移除 - self.chart_notebook.forget(self.accuracy_chart_frame) - self.log_gui.log("✓ 色准 Tab 已隐藏") - - except Exception as e: - self.log_gui.log(f"更新Tab状态失败: {str(e)}") - + """转发到 app.views.chart_frame.update_chart_tabs_state(Step 3 重构)""" + return _cf_update_chart_tabs_state(self) def get_test_type_display_name(self, test_type): """获取测试类型的显示名称""" display_names = { @@ -3336,74 +2633,11 @@ class PQAutomationApp: self.show_panel("log") def create_result_chart_frame(self): - """创建结果图表区域 - 6个独立Tab(Gamma 和 EOTF 分离)""" - # 创建Notebook用于图表切换 - self.chart_notebook = ttk.Notebook(self.result_frame) - self.chart_notebook.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) - - # ========== 创建6个独立的Tab页面 ========== - # 1. 色域图页面 - self.gamut_chart_frame = ttk.Frame(self.chart_notebook) - - # 2. Gamma图页面(SDR/屏模组使用) - self.gamma_chart_frame = ttk.Frame(self.chart_notebook) - - # 3. EOTF图页面(HDR专用) - self.eotf_chart_frame = ttk.Frame(self.chart_notebook) - - # 4. 色度一致性页面 - self.cct_chart_frame = ttk.Frame(self.chart_notebook) - - # 5. 对比度页面 - self.contrast_chart_frame = ttk.Frame(self.chart_notebook) - - # 6. 色准页面 - self.accuracy_chart_frame = ttk.Frame(self.chart_notebook) - - # 7. 客户模板结果页面 - self.custom_template_tab_frame = ttk.Frame(self.chart_notebook) - - # ========== 添加到Notebook(初始只添加前5个)========== - self.chart_notebook.add(self.gamut_chart_frame, text="色域图") - self.chart_notebook.add(self.gamma_chart_frame, text="Gamma曲线") - # ← EOTF 不添加,由 change_test_type() 动态控制 - self.chart_notebook.add(self.cct_chart_frame, text="色度一致性") - self.chart_notebook.add(self.contrast_chart_frame, text="对比度") - self.chart_notebook.add(self.accuracy_chart_frame, text="色准") - - # 初始化六个独立的图表 - self.init_gamut_chart() - self.init_gamma_chart() - self.init_eotf_chart() - self.init_cct_chart() - self.init_contrast_chart() - self.init_accuracy_chart() - - # 绑定Tab切换事件 - self.chart_notebook.bind("<>", self.on_chart_tab_changed) - - # ==================== ✅ 在图表下方创建单步调试面板 ==================== - self.debug_container = ttk.LabelFrame( - self.result_frame, # ← 放在 result_frame 内,图表正下方 - text="🔧 单步调试", - padding=10, - ) - # 默认不显示 - - # 创建单步调试面板实例 - self.debug_panel = PQDebugPanel(self.debug_container, self) - - self.log_gui.log("✓ 单步调试面板已创建(放在测试结果图表下方)") - + """转发到 app.views.chart_frame.create_result_chart_frame(Step 3 重构)""" + return _cf_create_result_chart_frame(self) def on_chart_tab_changed(self, event): - """Tab切换时的事件处理""" - try: - self._last_tab_index = self.chart_notebook.index( - self.chart_notebook.select() - ) - except Exception as e: - self.log_gui.log(f"Tab切换事件处理失败: {str(e)}") - + """转发到 app.views.chart_frame.on_chart_tab_changed(Step 3 重构)""" + return _cf_on_chart_tab_changed(self, event) def change_test_type(self, test_type): """切换测试类型""" # 切换测试类型时,自动隐藏日志面板和 Local Dimming 面板 @@ -3810,7 +3044,7 @@ class PQAutomationApp: # ========== 5.2.5 跳转到色域图Tab(第一个Tab)========== try: if hasattr(self, "chart_notebook"): - self.chart_notebook.select(0) # ← 选中第一个Tab(色域图) + self.chart_notebook.select(self.gamut_chart_frame) self.root.update_idletasks() # ← 刷新界面 self.log_gui.log("✓ 已跳转到色域图界面") except Exception as e: