diff --git a/algorithm/pq_algorithm.py b/algorithm/pq_algorithm.py index e391ca7..035af91 100644 --- a/algorithm/pq_algorithm.py +++ b/algorithm/pq_algorithm.py @@ -40,7 +40,7 @@ def calculate_gamut_coverage(points1, points2): return intersection_area, coverage except Exception as e: - print(f"❌ 计算色域覆盖率失败: {e}") + print(f"[Error] 计算色域覆盖率失败: {e}") return 0.0, 0.0 @@ -227,7 +227,7 @@ def calculate_uv_gamut_coverage(uv_coords, reference="DCI-P3"): return coverage except Exception as e: - print(f"❌ 计算 u'v' 色域覆盖率失败: {str(e)}") + print(f"[Error] 计算 u'v' 色域覆盖率失败: {str(e)}") import traceback traceback.print_exc() @@ -451,7 +451,7 @@ def calculate_cct_from_results(results): """ 从测量结果提取色度坐标(xy 坐标) - ⚠️ 注意:方法名保持不变(历史原因),实际功能是提取色度坐标 + 注意:方法名保持不变(历史原因),实际功能是提取色度坐标 Args: results: [[x, y, lv, ...], ...] 测量结果列表 @@ -486,7 +486,7 @@ def calculate_cct_from_results(results): lv_values.append(0.0) except Exception as e: - print(f"⚠️ 提取色度坐标失败: {str(e)}") + print(f"提取色度坐标失败: {str(e)}") x_values.append(0.3127) y_values.append(0.3290) lv_values.append(0.0) @@ -555,5 +555,5 @@ if __name__ == "__main__": print(f" 点{i+1}: Gamma={result[3]:.3f}") print("\n" + "=" * 60) - print("✅ 测试完成!") + print("测试完成!") print("=" * 60) diff --git a/app/config_io.py b/app/config_io.py index 45aa3a5..bb7cd74 100644 --- a/app/config_io.py +++ b/app/config_io.py @@ -1,4 +1,4 @@ -"""配置文件 I/O(Step 4 重构)。 +"""配置文件 I/O(Step 4 重构)。 从 pqAutomationApp.PQAutomationApp 中搬迁。每个函数第一行 `self = app` 以保留原有 `self.xxx` 属性访问不变。 @@ -33,19 +33,19 @@ def get_config_path(self): def load_pq_config(self): """加载PQ配置(兼容打包后的程序)""" try: - # ✅ 使用 self.config_file(已经是动态路径) + # 使用 self.config_file(已经是动态路径) if os.path.exists(self.config_file): with open(self.config_file, "r", encoding="utf-8") as f: config_dict = json.load(f) self.config.from_dict(config_dict) if hasattr(self, "log_gui"): - self.log_gui.log("✓ 配置文件加载成功") + self.log_gui.log("配置文件加载成功", level="success") else: if hasattr(self, "log_gui"): - self.log_gui.log("⚠️ 配置文件不存在,使用默认配置") + self.log_gui.log("配置文件不存在,使用默认配置", level="error") except Exception as e: if hasattr(self, "log_gui"): - self.log_gui.log(f"⚠️ 加载配置文件失败: {str(e)},使用默认配置") + self.log_gui.log(f"加载配置文件失败: {str(e)},使用默认配置", level="error") def save_pq_config(self): @@ -58,7 +58,7 @@ def save_pq_config(self): self.config.save_to_file(self.config_file) except Exception as e: if hasattr(self, "log_gui"): - self.log_gui.log(f"保存配置文件失败: {str(e)}") + self.log_gui.log(f"保存配置文件失败: {str(e)}", level="error") def clear_config_file(self): @@ -71,14 +71,14 @@ def clear_config_file(self): if os.path.exists(config_file): os.remove(config_file) self.config_cleared = True - messagebox.showinfo("提示", "✓ 清理成功") - self.log_gui.log("✓ 配置文件清理成功") + messagebox.showinfo("提示", "清理成功") + self.log_gui.log("配置文件清理成功", level="success") else: messagebox.showinfo("提示", "配置文件不存在") - self.log_gui.log("⚠️ 配置文件不存在") + self.log_gui.log("配置文件不存在", level="error") except Exception as e: - messagebox.showerror("错误", "❌ 清理失败") - self.log_gui.log(f"❌ 配置文件清理失败: {str(e)}") + messagebox.showerror("错误", "[Error] 清理失败") + self.log_gui.log(f"[Error] 配置文件清理失败: {str(e)}", level="error") diff --git a/app/data_range_converter.py b/app/data_range_converter.py index 787da5c..9e8dbe8 100644 --- a/app/data_range_converter.py +++ b/app/data_range_converter.py @@ -73,7 +73,7 @@ class DataRangeConverter: # Full Range 不需要转换 if data_range == "Full": if self.verbose: - print("✓ 使用 Full Range (0-255),无需转换") + print("使用 Full Range (0-255),无需转换") return pattern_params # Limited Range 需要转换 @@ -157,7 +157,7 @@ class DataRangeConverter: def _print_footer(self, total_count): """打印转换尾部信息""" - print(f"✓ 转换完成,共 {total_count} 个图案") + print(f"转换完成,共 {total_count} 个图案") print("=" * 80) def get_info(self): diff --git a/app/device/connection.py b/app/device/connection.py index 709d448..9c94ca2 100644 --- a/app/device/connection.py +++ b/app/device/connection.py @@ -1,4 +1,4 @@ -"""设备连接(UCD323 / CA410)相关逻辑(Step 4 重构)。 +"""设备连接(UCD323 / CA410)相关逻辑(Step 4 重构)。 从 pqAutomationApp.PQAutomationApp 中搬迁。每个函数第一行 `self = app` 以保留原有 `self.xxx` 属性访问不变。 @@ -23,7 +23,7 @@ def get_available_com_ports(self): ports = serial.tools.list_ports.comports() return [port.device for port in ports] except Exception as e: - self.log_gui.log(f"获取COM端口列表出错: {e}") + self.log_gui.log(f"获取COM端口列表出错: {e}", level="error") return [] @@ -117,7 +117,7 @@ def check_port_connection(self, is_ucd=True): if not self.ucd.open(self.ucd_list_var.get()): self.log_gui.log( f"设备 {self.ucd_list_var.get()} 异常,UCD323连接失败" - ) + , level="error") return False else: return True @@ -147,12 +147,12 @@ def check_port_connection(self, is_ucd=True): else: self.log_gui.log( f"端口 {self.config.device_config["ca_com"]} 异常,色温仪连接失败" - ) + , level="error") self.ca.close() self.ca = None return False except Exception as e: - self.log_gui.log(f"端口连接失败: {e}") + self.log_gui.log(f"端口连接失败: {e}", level="error") return False @@ -173,7 +173,7 @@ def disconnect_com_connections(self): pass finally: self.ucd.status = False - self.log_gui.log("UCD连接已断开") + self.log_gui.log("UCD连接已断开", level="info") # 断开CA连接 if self.ca is not None: @@ -183,7 +183,7 @@ def disconnect_com_connections(self): pass finally: self.ca = None - self.log_gui.log("CA连接已断开") + self.log_gui.log("CA连接已断开", level="info") # 重新启用相关控件 self.enable_com_widgets() @@ -192,7 +192,7 @@ def disconnect_com_connections(self): self.status_var.set("串口连接已断开") except Exception as e: - self.log_gui.log(f"断开连接时发生错误: {str(e)}") + self.log_gui.log(f"断开连接时发生错误: {str(e)}", level="info") messagebox.showerror("错误", f"断开连接失败: {str(e)}") diff --git a/app/export/excel_exporter.py b/app/export/excel_exporter.py index 439313a..33fcb03 100644 --- a/app/export/excel_exporter.py +++ b/app/export/excel_exporter.py @@ -183,7 +183,7 @@ def _section_gamut(ws, styles, row, results, log): _apply_data_row(ws, row, ["A", "B", "C", "D"], styles) row += 1 - log(" ✓ 添加色域数据") + log(" 添加色域数据", level="info") return row + 1 @@ -274,7 +274,7 @@ def _section_curve(ws, styles, row, results, log, curve_type): _apply_data_row(ws, row, ["A", "B", "C", "D", "E", "F"], styles) row += 1 - log(f" ✓ 添加 {label_prefix} 数据") + log(f" 添加 {label_prefix} 数据", level="info") return row + 1 @@ -323,7 +323,7 @@ def _section_cct(ws, styles, row, results, log): _apply_data_row(ws, row, ["A", "B", "C", "D"], styles) row += 1 - log(" ✓ 添加色度一致性数据") + log(" 添加色度一致性数据", level="info") return row + 1 @@ -354,7 +354,7 @@ def _section_contrast(ws, styles, row, results, log): ws[f"B{row}"].border = styles["thin_border"] row += 1 - log(" ✓ 添加对比度数据") + log(" 添加对比度数据", level="info") return row + 1 @@ -440,7 +440,7 @@ def _section_accuracy(ws, styles, row, results, log): _apply_data_row(ws, row, ["A", "B", "C", "D", "E", "F", "G"], styles) row += 1 - log(" ✓ 添加色准数据(含 xy 坐标和亮度)") + log(" 添加色准数据(含 xy 坐标和亮度)") return row + 1 @@ -493,12 +493,12 @@ def export_excel_report(result_dir, current_test_type, selected_items, excel_path = os.path.join(result_dir, "测试数据.xlsx") wb.save(excel_path) - log("✓ 已保存: 测试数据.xlsx") - log("=" * 60) + log("已保存: 测试数据.xlsx", level="success") + log("=" * 60, level="seperator") except ImportError: - log("⚠️ 未安装 openpyxl 库,跳过 Excel 导出") + log("未安装 openpyxl 库,跳过 Excel 导出", level="error") log(" 安装方法: pip install openpyxl") except Exception as e: - log(f"⚠️ Excel 导出失败: {str(e)}") + log(f"Excel 导出失败: {str(e)}", level="error") log(traceback.format_exc()) diff --git a/app/export/image_exporter.py b/app/export/image_exporter.py index da1002c..9d5ec7d 100644 --- a/app/export/image_exporter.py +++ b/app/export/image_exporter.py @@ -35,4 +35,4 @@ def save_result_images(result_dir, current_test_type, selected_items, fig.savefig(path, dpi=300) else: fig.savefig(path, dpi=300, bbox_inches="tight") - log(f"✓ 已保存: {filename}") + log(f"已保存: {filename}") diff --git a/app/plots/plot_accuracy.py b/app/plots/plot_accuracy.py index 6e2e9ee..c5aa40f 100644 --- a/app/plots/plot_accuracy.py +++ b/app/plots/plot_accuracy.py @@ -287,7 +287,7 @@ def plot_accuracy(self, accuracy_data, test_type): grade_color = "orange" else: grade = "需要校准" - grade_icon = "✗" + grade_icon = "[Error]" grade_color = "red" self.accuracy_ax.text( diff --git a/app/plots/plot_cct.py b/app/plots/plot_cct.py index 282d560..72599f9 100644 --- a/app/plots/plot_cct.py +++ b/app/plots/plot_cct.py @@ -1,4 +1,4 @@ -"""CCT / 色度一致性绘制。 +"""CCT / 色度一致性绘制。 Step 2 重构:从 pqAutomationApp.PQAutomationApp.plot_cct 原样搬迁。 """ @@ -16,7 +16,7 @@ def plot_cct(self, test_type): gray_data = self.results.get_intermediate_data("cct", "gray") if not gray_data or len(gray_data) < 2: - self.log_gui.log("⚠️ 无 xy 数据可用") + self.log_gui.log("无 xy 数据可用", level="error") ax = self.cct_fig.add_subplot(111) ax.text( 0.5, @@ -46,9 +46,9 @@ def plot_cct(self, test_type): total_points = len(gray_data) grayscale = np.linspace(100 / total_points, 100, len(x_measured)) - self.log_gui.log(f"✓ 已移除第一个数据点,当前数据点数: {len(x_measured)}") - self.log_gui.log(f" x范围: {min(x_measured):.6f} - {max(x_measured):.6f}") - self.log_gui.log(f" y范围: {min(y_measured):.6f} - {max(y_measured):.6f}") + self.log_gui.log(f"已移除第一个数据点,当前数据点数: {len(x_measured)}", level="success") + self.log_gui.log(f" x范围: {min(x_measured):.6f} - {max(x_measured):.6f}", level="info") + self.log_gui.log(f" y范围: {min(y_measured):.6f} - {max(y_measured):.6f}", level="info") # ========== 根据测试类型读取对应参数 ========== if test_type == "sdr_movie": @@ -57,50 +57,50 @@ def plot_cct(self, test_type): x_tolerance = float(self.sdr_cct_x_tolerance_var.get()) y_ideal = float(self.sdr_cct_y_ideal_var.get()) y_tolerance = float(self.sdr_cct_y_tolerance_var.get()) - self.log_gui.log("✓ 使用 SDR 色度参数") + self.log_gui.log("使用 SDR 色度参数", level="success") except: x_ideal = 0.3127 x_tolerance = 0.003 y_ideal = 0.3290 y_tolerance = 0.003 - self.log_gui.log("⚠️ SDR 参数读取失败,使用默认值") + self.log_gui.log("SDR 参数读取失败,使用默认值", level="error") elif test_type == "hdr_movie": try: x_ideal = float(self.hdr_cct_x_ideal_var.get()) x_tolerance = float(self.hdr_cct_x_tolerance_var.get()) y_ideal = float(self.hdr_cct_y_ideal_var.get()) y_tolerance = float(self.hdr_cct_y_tolerance_var.get()) - self.log_gui.log("✓ 使用 HDR 色度参数") + self.log_gui.log("使用 HDR 色度参数", level="success") except: x_ideal = 0.3127 x_tolerance = 0.003 y_ideal = 0.3290 y_tolerance = 0.003 - self.log_gui.log("⚠️ HDR 参数读取失败,使用默认值") + 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("✓ 使用屏模组色度参数") + self.log_gui.log("使用屏模组色度参数", level="success") except: x_ideal = 0.306 x_tolerance = 0.003 y_ideal = 0.318 y_tolerance = 0.003 - self.log_gui.log("⚠️ 屏模组参数读取失败,使用默认值") + self.log_gui.log("屏模组参数读取失败,使用默认值", level="error") x_low = x_ideal - x_tolerance x_high = x_ideal + x_tolerance y_low = y_ideal - y_tolerance y_high = y_ideal + y_tolerance - self.log_gui.log(f"✓ 用户设置参数:") - self.log_gui.log(f" x-ideal={x_ideal:.4f}, tolerance={x_tolerance:.4f}") - self.log_gui.log(f" x范围: [{x_low:.4f}, {x_high:.4f}]") - self.log_gui.log(f" y-ideal={y_ideal:.4f}, tolerance={y_tolerance:.4f}") - self.log_gui.log(f" y范围: [{y_low:.4f}, {y_high:.4f}]") + self.log_gui.log(f"用户设置参数:", level="success") + self.log_gui.log(f" x-ideal={x_ideal:.4f}, tolerance={x_tolerance:.4f}", level="info") + self.log_gui.log(f" x范围: [{x_low:.4f}, {x_high:.4f}]", level="info") + self.log_gui.log(f" y-ideal={y_ideal:.4f}, tolerance={y_tolerance:.4f}", level="info") + self.log_gui.log(f" y范围: [{y_low:.4f}, {y_high:.4f}]", level="info") # 为所有测试类型创建子图 ax1 = self.cct_fig.add_subplot(211) @@ -180,7 +180,7 @@ def plot_cct(self, test_type): x_max_data = max(x_measured) data_range_x = x_max_data - x_min_data - self.log_gui.log(f" x数据波动: {data_range_x:.6f}") + self.log_gui.log(f" x数据波动: {data_range_x:.6f}", level="info") range_span = x_tolerance * 2 margin_ratio = 0.20 @@ -190,16 +190,16 @@ def plot_cct(self, test_type): final_y_max = max(x_max_data, x_high) + extra_margin if x_min_data >= x_low and x_max_data <= x_high: - self.log_gui.log(f" x数据在tolerance范围内,使用tolerance范围显示") + self.log_gui.log(f" x数据在tolerance范围内,使用tolerance范围显示", level="info") final_y_min = x_low - extra_margin final_y_max = x_high + extra_margin else: - self.log_gui.log(f" x数据超出tolerance范围,扩展显示范围") + self.log_gui.log(f" x数据超出tolerance范围,扩展显示范围", level="info") ax1.set_ylim(final_y_min, final_y_max) self.log_gui.log( f" x轴显示范围: {final_y_min:.6f} - {final_y_max:.6f} (跨度: {final_y_max - final_y_min:.6f})" - ) + , level="info") # ========== 下图:y coordinates ========== ax2.plot( @@ -273,7 +273,7 @@ def plot_cct(self, test_type): y_max_data = max(y_measured) data_range_y = y_max_data - y_min_data - self.log_gui.log(f" y数据波动: {data_range_y:.6f}") + self.log_gui.log(f" y数据波动: {data_range_y:.6f}", level="info") range_span = y_tolerance * 2 extra_margin = range_span * margin_ratio @@ -282,16 +282,16 @@ def plot_cct(self, test_type): final_y_max = max(y_max_data, y_high) + extra_margin if y_min_data >= y_low and y_max_data <= y_high: - self.log_gui.log(f" y数据在tolerance范围内,使用tolerance范围显示") + self.log_gui.log(f" y数据在tolerance范围内,使用tolerance范围显示", level="info") final_y_min = y_low - extra_margin final_y_max = y_high + extra_margin else: - self.log_gui.log(f" y数据超出tolerance范围,扩展显示范围") + self.log_gui.log(f" y数据超出tolerance范围,扩展显示范围", level="info") ax2.set_ylim(final_y_min, final_y_max) self.log_gui.log( f" y轴显示范围: {final_y_min:.6f} - {final_y_max:.6f} (跨度: {final_y_max - final_y_min:.6f})" - ) + , level="info") # ========== 总标题 - 统一格式(去掉统计信息)========== test_type_name = self.get_test_type_name(test_type) @@ -321,4 +321,4 @@ def plot_cct(self, test_type): self.cct_canvas.draw() self.chart_notebook.select(self.cct_chart_frame) - self.log_gui.log("✓ xy 色度坐标图绘制完成") + self.log_gui.log("xy 色度坐标图绘制完成", level="success") diff --git a/app/plots/plot_eotf.py b/app/plots/plot_eotf.py index c1ebed3..a966775 100644 --- a/app/plots/plot_eotf.py +++ b/app/plots/plot_eotf.py @@ -1,4 +1,4 @@ -"""EOTF 曲线绘制(HDR)。 +"""EOTF 曲线绘制(HDR)。 Step 2 重构:从 pqAutomationApp.PQAutomationApp.plot_eotf 原样搬迁。 """ @@ -145,4 +145,4 @@ def plot_eotf(self, L_bar, results_with_eotf_list, test_type): except: pass - self.log_gui.log("EOTF 曲线 + 数据表格绘制完成") + self.log_gui.log("EOTF 曲线 + 数据表格绘制完成", level="success") diff --git a/app/plots/plot_gamma.py b/app/plots/plot_gamma.py index 642b5be..0864fc4 100644 --- a/app/plots/plot_gamma.py +++ b/app/plots/plot_gamma.py @@ -1,4 +1,4 @@ -"""Gamma 曲线绘制。 +"""Gamma 曲线绘制。 Step 2 重构:从 pqAutomationApp.PQAutomationApp.plot_gamma 原样搬迁。 """ @@ -139,4 +139,4 @@ def plot_gamma(self, L_bar, results_with_gamma_list, target_gamma, test_type): self.gamma_canvas.draw() self.chart_notebook.select(self.gamma_chart_frame) - self.log_gui.log("Gamma曲线 + 数据表格绘制完成") + self.log_gui.log("Gamma曲线 + 数据表格绘制完成", level="success") diff --git a/app/plots/plot_gamut.py b/app/plots/plot_gamut.py index 07cbafe..a7c50ad 100644 --- a/app/plots/plot_gamut.py +++ b/app/plots/plot_gamut.py @@ -1,4 +1,4 @@ -"""色域图(Gamut)绘制。 +"""色域图(Gamut)绘制。 Step 2 重构:从 pqAutomationApp.PQAutomationApp.plot_gamut 整体搬迁, 实现与原方法完全一致;原方法仅保留为一行转发。 @@ -30,7 +30,7 @@ def plot_gamut(self, results, coverage, test_type): UV_PIXELS_PER_U = 615.7260 UV_PIXELS_PER_V = 599.8432 - # ========== ✅ 读取用户选择的参考标准 ========== + # ========== 读取用户选择的参考标准 ========== if test_type == "screen_module": current_ref = self.screen_gamut_ref_var.get() elif test_type == "sdr_movie": @@ -40,7 +40,7 @@ def plot_gamut(self, results, coverage, test_type): else: current_ref = "DCI-P3" - # ========== ✅✅✅ 根据参考标准重新计算覆盖率(XY 空间)========== + # ========== ✅✅根据参考标准重新计算覆盖率(XY 空间)========== xy_coverage = coverage # 默认使用传入的值 uv_coverage = 0.0 @@ -67,18 +67,18 @@ def plot_gamut(self, results, coverage, test_type): xy_points ) else: - self.log_gui.log(f"⚠️ 未知参考标准 '{current_ref}',使用 DCI-P3") + self.log_gui.log(f"未知参考标准 '{current_ref}',使用 DCI-P3", level="error") _, xy_coverage = pq_algorithm.calculate_gamut_coverage_DCIP3( xy_points ) current_ref = "DCI-P3" self.log_gui.log( - f"✓ XY 空间覆盖率({current_ref}): {xy_coverage:.1f}%" - ) + f"XY 空间覆盖率({current_ref}): {xy_coverage:.1f}%" + , level="success") except Exception as e: - self.log_gui.log(f"⚠️ 重新计算 XY 覆盖率失败: {str(e)}") + self.log_gui.log(f"重新计算 XY 覆盖率失败: {str(e)}", level="error") xy_coverage = coverage # 回退到传入值 # ================================================= @@ -87,7 +87,7 @@ def plot_gamut(self, results, coverage, test_type): img_xy = mpimg.imread(get_resource_path("assets/cie.png")) h_xy, w_xy = img_xy.shape[:2] - self.log_gui.log(f"加载 XY 色域图: {w_xy}x{h_xy}") + self.log_gui.log(f"加载 XY 色域图: {w_xy}x{h_xy}", level="info") self.gamut_ax_xy.imshow(img_xy, extent=[0, w_xy, h_xy, 0], aspect="equal") self.gamut_ax_xy.set_xlim(0, w_xy) @@ -109,7 +109,7 @@ def plot_gamut(self, results, coverage, test_type): self.log_gui.log( f"测量色域: R({red_x:.4f},{red_y:.4f}) " f"G({green_x:.4f},{green_y:.4f}) B({blue_x:.4f},{blue_y:.4f})" - ) + , level="info") # ========== 绘制测量三角形 ========== points = [ @@ -257,7 +257,7 @@ def plot_gamut(self, results, coverage, test_type): zorder=3, ) - # ========== ✅ XY 覆盖率标注(使用重新计算的值)========== + # ========== XY 覆盖率标注(使用重新计算的值)========== self.gamut_ax_xy.text( w_xy * 0.85, h_xy * 0.92, @@ -287,17 +287,17 @@ def plot_gamut(self, results, coverage, test_type): ) except Exception as e: - self.log_gui.log(f"XY 图绘制失败: {str(e)}") + self.log_gui.log(f"XY 图绘制失败: {str(e)}", level="error") import traceback - self.log_gui.log(traceback.format_exc()) + self.log_gui.log(traceback.format_exc(), level="error") # ========== 右图:CIE 1976 u'v' ========== try: img_uv = mpimg.imread(get_resource_path("assets/cie_uv.png")) h_uv, w_uv = img_uv.shape[:2] - self.log_gui.log(f"加载 UV 色域图: {w_uv}x{h_uv}") + self.log_gui.log(f"加载 UV 色域图: {w_uv}x{h_uv}", level="info") self.gamut_ax_uv.imshow(img_uv, extent=[0, w_uv, h_uv, 0], aspect="equal") self.gamut_ax_uv.set_xlim(0, w_uv) @@ -329,18 +329,18 @@ def plot_gamut(self, results, coverage, test_type): [u, v] for u, v in [xy_to_uv(r[0], r[1]) for r in rgb_results] ] - self.log_gui.log(f"UV 坐标: {uv_coords}") + self.log_gui.log(f"UV 坐标: {uv_coords}", level="info") - # ========== ✅✅✅ 计算 u'v' 覆盖率(使用参考标准)========== + # ========== ✅✅计算 u'v' 覆盖率(使用参考标准)========== try: uv_coverage = pq_algorithm.calculate_uv_gamut_coverage( uv_coords, reference=current_ref ) self.log_gui.log( - f"✓ UV 空间覆盖率({current_ref}): {uv_coverage:.1f}%" - ) + f"UV 空间覆盖率({current_ref}): {uv_coverage:.1f}%" + , level="success") except Exception as e: - self.log_gui.log(f"⚠️ 计算 UV 覆盖率失败: {str(e)}") + self.log_gui.log(f"计算 UV 覆盖率失败: {str(e)}", level="error") uv_coverage = 0.0 # ================================================= @@ -491,7 +491,7 @@ def plot_gamut(self, results, coverage, test_type): zorder=3, ) - # ========== ✅ UV 覆盖率标注(使用动态计算的值)========== + # ========== UV 覆盖率标注(使用动态计算的值)========== self.gamut_ax_uv.text( w_uv * 0.85, h_uv * 0.92, @@ -521,10 +521,10 @@ def plot_gamut(self, results, coverage, test_type): ) except Exception as e: - self.log_gui.log(f"UV 图绘制失败: {str(e)}") + self.log_gui.log(f"UV 图绘制失败: {str(e)}", level="error") import traceback - self.log_gui.log(traceback.format_exc()) + self.log_gui.log(traceback.format_exc(), level="error") # ========== 总标题 ========== test_type_name = self.get_test_type_name(test_type) @@ -535,4 +535,4 @@ def plot_gamut(self, results, coverage, test_type): self.gamut_canvas.draw() self.chart_notebook.select(self.gamut_chart_frame) - self.log_gui.log("色域图绘制完成") + self.log_gui.log("色域图绘制完成", level="success") diff --git a/app/pq/pq_config.py b/app/pq/pq_config.py index a911aad..82ef750 100644 --- a/app/pq/pq_config.py +++ b/app/pq/pq_config.py @@ -375,13 +375,13 @@ class PQConfig: "default_pattern_gray", self.default_pattern_gray ) - # ========== ✅ 强制使用新的 29色配置 ========== + # ========== 强制使用新的 29色配置 ========== loaded_accuracy = config_dict.get("default_pattern_accuracy", None) # 检查加载的配置是否是旧的 10色 if loaded_accuracy and len(loaded_accuracy.get("pattern_params", [])) != 29: print( - f"⚠️ 检测到旧的配置({len(loaded_accuracy.get('pattern_params', []))}色),强制使用新的 29色配置" + f"检测到旧的配置({len(loaded_accuracy.get('pattern_params', []))}色),强制使用新的 29色配置" ) # 使用 __init__ 中定义的新配置 self.default_pattern_accuracy = self.default_pattern_accuracy @@ -431,7 +431,7 @@ class PQConfig: self.current_pattern = self.default_pattern_rgb elif mode == "gray": self.current_pattern = self.default_pattern_gray - elif mode == "accuracy": # ✅ 色准模式(SDR 和 HDR 通用 29色) + elif mode == "accuracy": # 色准模式(SDR 和 HDR 通用 29色) self.current_pattern = self.default_pattern_accuracy elif mode == "custom": # self.current_pattern = self.custom_pattern @@ -459,7 +459,7 @@ class PQConfig: self.custom_pattern["measurement_max_value"] = len(pattern_params) - 1 return True - # ========== ✅ 获取 29色名称列表 ========== + # ========== 获取 29色名称列表 ========== def get_accuracy_color_names(self): """ 获取色准测试的 29个颜色名称(SDR 和 HDR 通用) @@ -502,7 +502,7 @@ class PQConfig: "100% Yellow", ] - # ========== ✅ 获取 29色的 RGB 值 ========== + # ========== 获取 29色的 RGB 值 ========== def get_accuracy_color_rgb(self): """ 获取色准测试的 RGB 值(用于标准值计算) @@ -579,7 +579,7 @@ class PQConfig: return config_info -# ========== ✅ 验证代码(测试完成后可删除)========== +# ========== 验证代码(测试完成后可删除)========== if __name__ == "__main__": print("=" * 60) print("验证 pq_config.py 配置") @@ -596,7 +596,7 @@ if __name__ == "__main__": print(f" measurement_max_value: {max_value}") if pattern_count == 29 and max_value == 28: - print("\n✅ 配置正确(29色)") + print("\n配置正确(29色)") # 显示前 5 个图案 print("\n前5个图案:") @@ -619,16 +619,16 @@ if __name__ == "__main__": print(f" current_pattern 图案数量: {current_count}") if current_count == 29: - print(" ✅ set_current_pattern 工作正常") + print(" set_current_pattern 工作正常") else: - print(f" ❌ set_current_pattern 失败!只有 {current_count} 个图案") + print(f" [Error] set_current_pattern 失败!只有 {current_count} 个图案") else: - print(f"\n❌ 配置错误!") + print(f"\n[Error] 配置错误!") print(f" 期望: 29 个图案, measurement_max_value=28") print(f" 实际: {pattern_count} 个图案, measurement_max_value={max_value}") - print("\n❌ 请检查 default_pattern_accuracy 定义!") + print("\n[Error] 请检查 default_pattern_accuracy 定义!") print(" 应该包含:") print(" - 5个灰阶") print(" - 18个 ColorChecker 色块") diff --git a/app/runner/test_runner.py b/app/runner/test_runner.py index 839b336..c798c9f 100644 --- a/app/runner/test_runner.py +++ b/app/runner/test_runner.py @@ -1,4 +1,4 @@ -"""测试执行(runner)相关逻辑(Step 5 重构)。 +"""测试执行(runner)相关逻辑(Step 5 重构)。 从 pqAutomationApp.PQAutomationApp 中搬迁。每个函数第一行 `self = app` 以保留原有 `self.xxx` 属性访问不变。 @@ -6,7 +6,6 @@ import datetime import time -import traceback from tkinter import messagebox import tkinter as tk @@ -40,10 +39,10 @@ def new_pq_results(self, test_type, test_name): def run_test(self, test_type, test_items): """执行测试""" try: - self.log_gui.log(f"开始执行{self.get_test_type_name(test_type)}测试") + self.log_gui.log(f"开始执行{self.get_test_type_name(test_type)}测试", level="info") self.log_gui.log( f"测试项目: {', '.join(self.config.get_test_item_chinese_names(test_items))}" - ) + , level="info") # 根据测试类型执行不同的测试流程 if test_type == "screen_module": @@ -57,21 +56,21 @@ def run_test(self, test_type, test_items): if self.testing: # 如果没有被中途停止 self._dispatch_ui(self.on_test_completed) except Exception as e: - self.log_gui.log(f"测试过程中发生错误: {str(e)}") + self.log_gui.log(f"测试过程中发生错误: {str(e)}", level="info") import traceback - self.log_gui.log(traceback.format_exc()) + self.log_gui.log(traceback.format_exc(), level="error") self._dispatch_ui(self.on_test_error) def run_screen_module_test(self, test_items): """执行屏模组性能测试 - 优化版""" - self.log_gui.log("执行屏模组性能测试...") + self.log_gui.log("执行屏模组性能测试...", level="info") if test_items: self.new_pq_results("screen_module", "屏模组性能测试") else: - self.log_gui.log("未选择任何测试项目") + self.log_gui.log("未选择任何测试项目", level="info") return # 判断是否需要灰阶数据 @@ -102,19 +101,19 @@ def run_screen_module_test(self, test_items): and shared_gray_data is None and needs_gray_data ): - self.log_gui.log("=" * 50) - self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)") - self.log_gui.log("=" * 50) + self.log_gui.log("=" * 50, level="separator") + self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)", level="info") + self.log_gui.log("=" * 50, level="separator") shared_gray_data = self.send_fix_pattern("gray") if not shared_gray_data or len(shared_gray_data) < 2: - self.log_gui.log("灰阶数据采集失败或数据不足,跳过相关测试") + self.log_gui.log("灰阶数据采集失败或数据不足,跳过相关测试", level="error") return self.log_gui.log( - f"✓ 灰阶数据采集完成,共 {len(shared_gray_data)} 个数据点" - ) + f"灰阶数据采集完成,共 {len(shared_gray_data)} 个数据点" + , level="success") # 保存到 results 对象,供所有灰阶测试使用 self.results.add_intermediate_data("shared", "gray", shared_gray_data) @@ -129,7 +128,7 @@ def run_screen_module_test(self, test_items): # ==================== 后续灰阶测试(复用数据) ==================== elif item in ["gamma", "cct", "contrast"] and shared_gray_data is not None: - self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试") + self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试", level="info") if item == "gamma": self.test_gamma("screen_module", shared_gray_data) @@ -141,16 +140,16 @@ def run_screen_module_test(self, test_items): def run_custom_sdr_test(self, test_items): """执行客户定制 SDR 测试 - 升级版""" - self.log_gui.log("执行客户定制 SDR 测试...") + self.log_gui.log("执行客户定制 SDR 测试...", level="info") # 获取信号格式设置 color_space = self.sdr_color_space_var.get() # BT.709/BT.601/BT.2020 gamma_type = self.sdr_gamma_type_var.get() # 2.2/2.4/2.6 data_range = self.sdr_data_range_var.get() # Full/Limited bit_depth = self.sdr_bit_depth_var.get() # 8bit/10bit/12bit - self.log_gui.log(f"信号格式: 色彩空间={color_space}, Gamma={gamma_type}") - self.log_gui.log(f" 数据范围={data_range}, 编码位深={bit_depth}") - self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)") + self.log_gui.log(f"信号格式: 色彩空间={color_space}, Gamma={gamma_type}", level="info") + self.log_gui.log(f" 数据范围={data_range}, 编码位深={bit_depth}", level="info") + self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)", level="info") self.test_custom_sdr() if self.testing: @@ -159,12 +158,12 @@ def run_custom_sdr_test(self, test_items): def run_sdr_movie_test(self, test_items): """执行SDR Movie测试""" - self.log_gui.log("执行SDR Movie测试...") + self.log_gui.log("执行SDR Movie测试...", level="info") if test_items: self.new_pq_results("sdr_movie", "SDR Movie测试") else: - self.log_gui.log("未选择任何测试项目") + self.log_gui.log("未选择任何测试项目", level="info") return # 获取信号格式设置 @@ -173,8 +172,8 @@ def run_sdr_movie_test(self, test_items): data_range = self.sdr_data_range_var.get() # Full/Limited bit_depth = self.sdr_bit_depth_var.get() # 8bit/10bit/12bit - self.log_gui.log(f"信号格式: 色彩空间={color_space}, Gamma={gamma_type}") - self.log_gui.log(f" 数据范围={data_range}, 编码位深={bit_depth}") + self.log_gui.log(f"信号格式: 色彩空间={color_space}, Gamma={gamma_type}", level="info") + self.log_gui.log(f" 数据范围={data_range}, 编码位深={bit_depth}", level="info") # 判断是否需要灰阶数据 needs_gray_data = any( @@ -201,11 +200,11 @@ def run_sdr_movie_test(self, test_items): and shared_gray_data is None and needs_gray_data ): - self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)") + self.log_gui.log("开始统一采集灰阶数据(用于 Gamma/CCT/对比度测试)", level="info") shared_gray_data = self.send_fix_pattern("gray") if not shared_gray_data or len(shared_gray_data) < 2: - self.log_gui.log("灰阶数据采集失败或数据不足") + self.log_gui.log("灰阶数据采集失败或数据不足", level="error") return self.results.add_intermediate_data("shared", "gray", shared_gray_data) @@ -218,7 +217,7 @@ def run_sdr_movie_test(self, test_items): self.test_contrast("sdr_movie", shared_gray_data) elif item in ["gamma", "cct", "contrast"] and shared_gray_data is not None: - self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试") + self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试", level="info") if item == "gamma": self.test_gamma("sdr_movie", shared_gray_data) elif item == "cct": @@ -232,12 +231,12 @@ def run_sdr_movie_test(self, test_items): def run_hdr_movie_test(self, test_items): """执行HDR Movie测试""" - self.log_gui.log("执行HDR Movie测试...") + self.log_gui.log("执行HDR Movie测试...", level="info") if test_items: self.new_pq_results("hdr_movie", "HDR Movie测试") else: - self.log_gui.log("未选择任何测试项目") + self.log_gui.log("未选择任何测试项目", level="info") return # 获取信号格式设置 @@ -247,9 +246,9 @@ def run_hdr_movie_test(self, test_items): data_range = self.hdr_data_range_var.get() bit_depth = self.hdr_bit_depth_var.get() - self.log_gui.log(f"信号格式: 色彩空间={color_space}") - self.log_gui.log(f" MaxCLL={max_cll}, MaxFALL={max_fall}") - self.log_gui.log(f" 数据范围={data_range}, 编码位深={bit_depth}") + self.log_gui.log(f"信号格式: 色彩空间={color_space}", level="info") + self.log_gui.log(f" MaxCLL={max_cll}, MaxFALL={max_fall}", level="info") + self.log_gui.log(f" 数据范围={data_range}, 编码位深={bit_depth}", level="info") # 判断是否需要灰阶数据 needs_gray_data = any( @@ -276,11 +275,11 @@ def run_hdr_movie_test(self, test_items): and shared_gray_data is None and needs_gray_data ): - self.log_gui.log("开始统一采集灰阶数据(用于 EOTF/CCT/对比度测试)") + self.log_gui.log("开始统一采集灰阶数据(用于 EOTF/CCT/对比度测试)", level="info") shared_gray_data = self.send_fix_pattern("gray") if not shared_gray_data or len(shared_gray_data) < 2: - self.log_gui.log("灰阶数据采集失败或数据不足") + self.log_gui.log("灰阶数据采集失败或数据不足", level="error") return self.results.add_intermediate_data("shared", "gray", shared_gray_data) @@ -293,7 +292,7 @@ def run_hdr_movie_test(self, test_items): self.test_contrast("hdr_movie", shared_gray_data) elif item in ["eotf", "cct", "contrast"] and shared_gray_data is not None: - self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试") + self.log_gui.log(f"复用已采集的灰阶数据进行 {item} 测试", level="info") if item == "eotf": self.test_eotf("hdr_movie", shared_gray_data) elif item == "cct": @@ -320,7 +319,7 @@ def send_fix_pattern(self, mode): elif mode == "custom": self.config.set_current_pattern("custom") else: - self.log_gui.log(f"❌ 未知的图案模式: {mode}") + self.log_gui.log(f"[Error] 未知的图案模式: {mode}", level="error") return None # 2. 获取当前测试类型 @@ -329,31 +328,31 @@ def send_fix_pattern(self, mode): # 3. 根据测试类型设置信号格式和图案 if test_type == "screen_module": # 屏模组测试:使用 Timing - self.log_gui.log("=" * 50) - self.log_gui.log("设置屏模组信号格式:") - self.log_gui.log("=" * 50) + self.log_gui.log("=" * 50, level="separator") + self.log_gui.log("设置屏模组信号格式:", level="info") + self.log_gui.log("=" * 50, level="separator") timing_str = self.config.current_test_types[test_type]["timing"] - self.log_gui.log(f" Timing: {timing_str}") + self.log_gui.log(f" Timing: {timing_str}", level="info") - # ✅ 屏模组测试:直接使用原始配置 + # 屏模组测试:直接使用原始配置 self.ucd.set_ucd_params(self.config) elif test_type == "sdr_movie": # SDR 测试:设置色彩空间、Gamma 等 - self.log_gui.log("=" * 50) - self.log_gui.log("设置 SDR 信号格式:") - self.log_gui.log("=" * 50) + self.log_gui.log("=" * 50, level="separator") + self.log_gui.log("设置 SDR 信号格式:", level="info") + self.log_gui.log("=" * 50, level="separator") color_space = self.sdr_color_space_var.get() gamma = self.sdr_gamma_type_var.get() data_range = self.sdr_data_range_var.get() bit_depth = self.sdr_bit_depth_var.get() - self.log_gui.log(f" 色彩空间: {color_space}") - self.log_gui.log(f" Gamma: {gamma}") - self.log_gui.log(f" 数据范围: {data_range}") - self.log_gui.log(f" 编码位深: {bit_depth}") + self.log_gui.log(f" 色彩空间: {color_space}", level="info") + self.log_gui.log(f" Gamma: {gamma}", level="info") + self.log_gui.log(f" 数据范围: {data_range}", level="info") + self.log_gui.log(f" 编码位深: {bit_depth}", level="info") success = self.ucd.set_sdr_format( color_space=color_space, @@ -363,17 +362,17 @@ def send_fix_pattern(self, mode): ) if success: - self.log_gui.log("✓ SDR 信号格式设置成功") + self.log_gui.log("SDR 信号格式设置成功", level="success") else: - self.log_gui.log("✗ SDR 信号格式设置失败") + self.log_gui.log("[Error] SDR 信号格式设置失败", level="error") # 设置图案参数 if mode == "accuracy": - self.log_gui.log(f"设置 SDR 29色色准测试图案...") + self.log_gui.log(f"设置 SDR 29色色准测试图案...", level="info") else: - self.log_gui.log(f"设置 SDR 测试图案({mode} 模式)...") + self.log_gui.log(f"设置 SDR 测试图案({mode} 模式)...", level="info") - # ========== ✅✅✅ 修改:使用临时配置对象 ========== + # ========== ✅✅修改:使用临时配置对象 ========== import copy # 从原始配置获取参数(每次都是干净的) @@ -394,9 +393,9 @@ def send_fix_pattern(self, mode): self.config.default_pattern_temp["pattern_params"] ) - self.log_gui.log(f"🔍 使用原始 RGB 参数(前 3 个):") + self.log_gui.log(f" 使用原始 RGB 参数(前 3 个):", level="info") for i in range(min(3, len(original_params))): - self.log_gui.log(f" [{i+1}] {original_params[i]}") + self.log_gui.log(f" [{i+1}] {original_params[i]}", level="info") # 根据 data_range 转换 converted_params = convert_pattern_params( @@ -404,30 +403,30 @@ def send_fix_pattern(self, mode): ) if data_range == "Limited": - self.log_gui.log("🔧 转换为 Limited Range (16-235):") + self.log_gui.log(" 转换为 Limited Range (16-235):", level="info") for i in range(min(3, len(converted_params))): self.log_gui.log( f" {original_params[i]} → {converted_params[i]}" - ) + , level="info") else: - self.log_gui.log("✓ Full Range,RGB 保持不变") + self.log_gui.log("Full Range,RGB 保持不变", level="success") - # ✅ 创建临时配置对象(不修改 self.config) + # 创建临时配置对象(不修改 self.config) temp_config = self.config.get_temp_config_with_converted_params( mode=mode, converted_params=converted_params ) - # ✅ 使用临时配置设置参数 + # 使用临时配置设置参数 self.ucd.set_ucd_params(temp_config) - self.log_gui.log(f"✓ 图案参数已设置,共 {len(converted_params)} 个图案") + self.log_gui.log(f"图案参数已设置,共 {len(converted_params)} 个图案", level="success") # ========== 修改结束 ========== elif test_type == "hdr_movie": # HDR 测试:设置色彩空间、Metadata 等 - self.log_gui.log("=" * 50) - self.log_gui.log("设置 HDR 信号格式:") - self.log_gui.log("=" * 50) + self.log_gui.log("=" * 50, level="separator") + self.log_gui.log("设置 HDR 信号格式:", level="info") + self.log_gui.log("=" * 50, level="separator") color_space = self.hdr_color_space_var.get() data_range = self.hdr_data_range_var.get() @@ -435,11 +434,11 @@ def send_fix_pattern(self, mode): max_cll = self.hdr_maxcll_var.get() max_fall = self.hdr_maxfall_var.get() - self.log_gui.log(f" 色彩空间: {color_space}") - self.log_gui.log(f" 数据范围: {data_range}") - self.log_gui.log(f" 编码位深: {bit_depth}") - self.log_gui.log(f" MaxCLL: {max_cll}") - self.log_gui.log(f" MaxFALL: {max_fall}") + self.log_gui.log(f" 色彩空间: {color_space}", level="info") + self.log_gui.log(f" 数据范围: {data_range}", level="info") + self.log_gui.log(f" 编码位深: {bit_depth}", level="info") + self.log_gui.log(f" MaxCLL: {max_cll}", level="info") + self.log_gui.log(f" MaxFALL: {max_fall}", level="info") success = self.ucd.set_hdr_format( color_space=color_space, @@ -450,17 +449,17 @@ def send_fix_pattern(self, mode): ) if success: - self.log_gui.log("✓ HDR 信号格式设置成功") + self.log_gui.log("HDR 信号格式设置成功", level="success") else: - self.log_gui.log("✗ HDR 信号格式设置失败") + self.log_gui.log("[Error] HDR 信号格式设置失败", level="error") # 设置图案参数 if mode == "accuracy": - self.log_gui.log(f"设置 HDR 29色色准测试图案...") + self.log_gui.log(f"设置 HDR 29色色准测试图案...", level="info") else: - self.log_gui.log(f"设置 HDR 测试图案({mode} 模式)...") + self.log_gui.log(f"设置 HDR 测试图案({mode} 模式)...", level="info") - # ========== ✅✅✅ 修改:使用临时配置对象 ========== + # ========== ✅✅修改:使用临时配置对象 ========== import copy # 从原始配置获取参数 @@ -477,9 +476,9 @@ def send_fix_pattern(self, mode): self.config.default_pattern_accuracy["pattern_params"] ) - self.log_gui.log(f"🔍 使用原始 RGB 参数(前 3 个):") + self.log_gui.log(f" 使用原始 RGB 参数(前 3 个):", level="info") for i in range(min(3, len(original_params))): - self.log_gui.log(f" [{i+1}] {original_params[i]}") + self.log_gui.log(f" [{i+1}] {original_params[i]}", level="info") # 根据 data_range 转换 converted_params = convert_pattern_params( @@ -487,36 +486,36 @@ def send_fix_pattern(self, mode): ) if data_range == "Limited": - self.log_gui.log("🔧 转换为 Limited Range (16-235):") + self.log_gui.log(" 转换为 Limited Range (16-235):", level="info") for i in range(min(3, len(converted_params))): self.log_gui.log( f" {original_params[i]} → {converted_params[i]}" - ) + , level="info") else: - self.log_gui.log("✓ Full Range,RGB 保持不变") + self.log_gui.log("Full Range,RGB 保持不变", level="success") - # ✅ 创建临时配置对象 + # 创建临时配置对象 temp_config = self.config.get_temp_config_with_converted_params( mode=mode, converted_params=converted_params ) self.ucd.set_ucd_params(temp_config) - self.log_gui.log(f"✓ 图案参数已设置,共 {len(converted_params)} 个图案") + self.log_gui.log(f"图案参数已设置,共 {len(converted_params)} 个图案", level="success") # ========== 修改结束 ========== - self.log_gui.log("=" * 50) + self.log_gui.log("=" * 50, level="separator") # 4. 循环发送图案并采集数据(使用原始配置的数量) total_patterns = len(self.config.current_pattern["pattern_params"]) - self.log_gui.log(f"开始采集数据,共 {total_patterns} 个图案") + self.log_gui.log(f"开始采集数据,共 {total_patterns} 个图案", level="info") settle_time = max(0.2, float(getattr(self, "pattern_settle_time", 1.0))) progress_step = max( 1, int(getattr(self, "pattern_progress_log_step", 5)) ) self.log_gui.log( f"采集等待时间: {settle_time:.2f}s(可通过 pattern_settle_time 调整)" - ) + , level="info") # 获取颜色名称列表(用于日志显示) color_names = None @@ -529,7 +528,7 @@ def send_fix_pattern(self, mode): for i in range(total_patterns): if not self.testing: - self.log_gui.log("⚠️ 测试已停止") + self.log_gui.log("测试已停止", level="error") return results should_log_detail = ( @@ -543,9 +542,9 @@ def send_fix_pattern(self, mode): if color_names and i < len(color_names): self.log_gui.log( f"发送第 {i+1}/{total_patterns} 个图案: {color_names[i]}..." - ) + , level="info") else: - self.log_gui.log(f"发送第 {i+1}/{total_patterns} 个图案...") + self.log_gui.log(f"发送第 {i+1}/{total_patterns} 个图案...", level="info") self.ucd.set_next_pattern() self.ucd.run() @@ -559,18 +558,18 @@ def send_fix_pattern(self, mode): if should_log_detail: self.log_gui.log( - f" ✓ 测量完成: TCP={tcp:.4f}, DUV={duv:.4f}, lv={lv:.2f}, " + f" 测量完成: TCP={tcp:.4f}, DUV={duv:.4f}, lv={lv:.2f}, " f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}" - ) + , level="success") self.ca.set_Display(8) lambda_, Pe, lv, X, Y, Z = self.ca.readAllDisplay() if should_log_detail: self.log_gui.log( - f" ✓ 测量完成: λ={lambda_:.4f}, Pe={Pe:.4f}, lv={lv:.2f}, " + f" 测量完成: λ={lambda_:.4f}, Pe={Pe:.4f}, lv={lv:.2f}, " f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}" - ) + , level="success") result = [tcp, duv, lv, lambda_, Pe, lv, X, Y, Z] results.append(result) @@ -606,7 +605,7 @@ def send_fix_pattern(self, mode): ), ) except Exception as e: - self.log_gui.log(f"⚠️ 第 {i+1} 行实时结果写入失败: {str(e)}") + self.log_gui.log(f"第 {i+1} 行实时结果写入失败: {str(e)}", level="error") else: self.ca.set_xyLv_Display() @@ -616,35 +615,35 @@ def send_fix_pattern(self, mode): if should_log_detail: self.log_gui.log( - f" ✓ 测量完成: x={x:.4f}, y={y:.4f}, lv={lv:.2f}, " + f" 测量完成: x={x:.4f}, y={y:.4f}, lv={lv:.2f}, " f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}" - ) + , level="success") - self.log_gui.log(f"✓ 数据采集完成,共 {len(results)} 组数据") + self.log_gui.log(f"数据采集完成,共 {len(results)} 组数据", level="success") return results except Exception as e: - self.log_gui.log(f"❌ 发送图案失败: {str(e)}") + self.log_gui.log(f"[Error] 发送图案失败: {str(e)}", level="error") import traceback - self.log_gui.log(traceback.format_exc()) + self.log_gui.log(traceback.format_exc(), level="error") return None def test_custom_sdr(self): """执行客户定制 SDR 测试 - 升级版""" - self.log_gui.log("执行客户定制 SDR 测试...") + self.log_gui.log("执行客户定制 SDR 测试...", level="info") results = self.send_fix_pattern("custom") if not results: - self.log_gui.log("客户模板SDR测试被中断") + self.log_gui.log("客户模板SDR测试被中断", level="info") return - self.log_gui.log(f"客户模板采集完成,共 {len(results)} 组数据") + self.log_gui.log(f"客户模板采集完成,共 {len(results)} 组数据", level="success") def test_gamut(self, test_type): """测试色域""" - self.log_gui.log("开始测试色域...") + self.log_gui.log("开始测试色域...", level="info") self.results.start_test_item("gamut") try: @@ -653,18 +652,18 @@ def test_gamut(self, test_type): # 检查结果是否为空 if not results: - self.log_gui.log("色域测试被中断") + self.log_gui.log("色域测试被中断", level="info") return self.results.add_intermediate_data("gamut", "rgb", results) # 计算色域覆盖率 - self.log_gui.log("计算色域覆盖率...") + self.log_gui.log("计算色域覆盖率...", level="info") # 提取 x, y 坐标用于计算色域 xy_points = [[result[0], result[1]] for result in results] - # ========== ✅ 测试时:使用色彩空间的值作为参考标准 ========== + # ========== 测试时:使用色彩空间的值作为参考标准 ========== reference_standard = None area = None coverage = None @@ -673,7 +672,7 @@ def test_gamut(self, test_type): # 屏模组测试:固定使用 DCI-P3(因为没有色彩空间设置) reference_standard = "DCI-P3" - # ✅ 同步更新到色域参考标准变量(供后续重绘使用) + # 同步更新到色域参考标准变量(供后续重绘使用) self.screen_gamut_ref_var.set(reference_standard) elif test_type == "sdr_movie": @@ -689,10 +688,10 @@ def test_gamut(self, test_type): else: reference_standard = "BT.709" self.log_gui.log( - f"⚠️ 未识别的色彩空间 '{color_space}',使用默认标准 BT.709" - ) + f"未识别的色彩空间 '{color_space}',使用默认标准 BT.709" + , level="error") - # ✅ 同步更新到色域参考标准变量 + # 同步更新到色域参考标准变量 self.sdr_gamut_ref_var.set(reference_standard) elif test_type == "hdr_movie": @@ -706,17 +705,17 @@ def test_gamut(self, test_type): else: reference_standard = "BT.2020" self.log_gui.log( - f"⚠️ 未识别的色彩空间 '{color_space}',使用默认标准 BT.2020" - ) + f"未识别的色彩空间 '{color_space}',使用默认标准 BT.2020" + , level="error") - # ✅ 同步更新到色域参考标准变量 + # 同步更新到色域参考标准变量 self.hdr_gamut_ref_var.set(reference_standard) else: # 未知测试类型,使用 DCI-P3 作为后备 reference_standard = "DCI-P3" self.log_gui.log( - f"⚠️ 未识别的测试类型 '{test_type}',使用默认标准 DCI-P3" - ) + f"未识别的测试类型 '{test_type}',使用默认标准 DCI-P3" + , level="error") # ========== 根据参考标准计算 XY 覆盖率 ========== if reference_standard == "BT.2020": @@ -732,10 +731,10 @@ def test_gamut(self, test_type): area, coverage = pq_algorithm.calculate_gamut_coverage_DCIP3(xy_points) reference_standard = "DCI-P3" self.log_gui.log( - f"⚠️ 未识别的参考标准 '{reference_standard}',使用默认标准 DCI-P3" - ) + f"未识别的参考标准 '{reference_standard}',使用默认标准 DCI-P3" + , level="error") - # ========== ✅✅✅ 新增:计算 UV 覆盖率 ========== + # ========== ✅✅新增:计算 UV 覆盖率 ========== uv_coverage = 0 try: # 将 XY 转换为 UV @@ -768,8 +767,8 @@ def test_gamut(self, test_type): ) self.log_gui.log( - f"✓ XY 覆盖率: {coverage:.1f}% | UV 覆盖率: {uv_coverage:.1f}%" - ) + f"XY 覆盖率: {coverage:.1f}% | UV 覆盖率: {uv_coverage:.1f}%" + , level="success") except: uv_coverage = 0 @@ -779,7 +778,7 @@ def test_gamut(self, test_type): { "area": area, "coverage": coverage, - "uv_coverage": uv_coverage, # ✅ 新增 UV 覆盖率 + "uv_coverage": uv_coverage, # 新增 UV 覆盖率 "reference": reference_standard, }, ) @@ -787,13 +786,13 @@ def test_gamut(self, test_type): # 传递完整的 results 用于绘图 self.plot_gamut(results, coverage, test_type) - self.log_gui.log("色域测试完成") + self.log_gui.log("色域测试完成", level="success") except Exception as e: - self.log_gui.log(f"色域测试失败: {str(e)}") + self.log_gui.log(f"色域测试失败: {str(e)}", level="error") import traceback - self.log_gui.log(traceback.format_exc()) + self.log_gui.log(traceback.format_exc(), level="error") raise @@ -803,28 +802,28 @@ def test_gamma(self, test_type, gray_data=None): Args: test_type: 测阶数据,如果提供则使用,否则重新采集 """ - self.log_gui.log("开始测试Gamma曲线...") + self.log_gui.log("开始测试Gamma曲线...", level="info") self.results.start_test_item("gamma") try: # 使用传入的灰阶数据或独立采集 if gray_data is not None: - self.log_gui.log("使用共享的灰阶数据") + self.log_gui.log("使用共享的灰阶数据", level="info") results = gray_data else: - self.log_gui.log("独立采集灰阶数据") + self.log_gui.log("独立采集灰阶数据", level="info") results = self.send_fix_pattern("gray") if not results or len(results) < 2: - self.log_gui.log("Gamma测试被中断或数据不足") + self.log_gui.log("Gamma测试被中断或数据不足", level="info") return self.results.add_intermediate_data("gamma", "gray", results) - self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行计算") - self.log_gui.log("计算Gamma值...") + self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行计算", level="info") + self.log_gui.log("计算Gamma值...", level="info") - # ========== ✅ 修复:正确获取 max_index_fix ========== + # ========== 修复:正确获取 max_index_fix ========== # 获取配置中的值 config_max_value = self.config.current_pattern.get( "measurement_max_value", 10 @@ -834,10 +833,10 @@ def test_gamma(self, test_type, gray_data=None): try: max_index_fix = int(config_max_value) except (ValueError, TypeError): - self.log_gui.log(f"警告: measurement_max_value 转换失败,使用默认值 10") + self.log_gui.log(f"警告: measurement_max_value 转换失败,使用默认值 10", level="warning") max_index_fix = 10 - self.log_gui.log(f"配置中的 max_index_fix = {max_index_fix}") + self.log_gui.log(f"配置中的 max_index_fix = {max_index_fix}", level="info") # 关键修复:验证并调整 max_index_fix # max_index_fix 应该是数据点的最大索引(从0开始,所以是 len - 1) @@ -845,12 +844,13 @@ def test_gamma(self, test_type, gray_data=None): if max_index_fix > actual_max_index: self.log_gui.log( - f"警告: 配置的 max_index_fix({max_index_fix}) > 实际最大索引({actual_max_index})" + f"警告: 配置的 max_index_fix({max_index_fix}) > 实际最大索引({actual_max_index})", + level="warning" ) - self.log_gui.log(f"自动调整为: {actual_max_index}") + self.log_gui.log(f"自动调整为: {actual_max_index}", level="info") max_index_fix = actual_max_index - self.log_gui.log(f"最终使用的 max_index_fix = {max_index_fix}") + self.log_gui.log(f"最终使用的 max_index_fix = {max_index_fix}", level="info") # ======================================================== # 获取灰阶 pattern 参数(用于22293 Gamma数据对齐) @@ -877,13 +877,13 @@ def test_gamma(self, test_type, gray_data=None): self.plot_gamma(L_bar, results_with_gamma_list, target_gamma, test_type) - self.log_gui.log("Gamma测试完成") + self.log_gui.log("Gamma测试完成", level="success") except Exception as e: - self.log_gui.log(f"Gamma测试失败: {str(e)}") + self.log_gui.log(f"Gamma测试失败: {str(e)}", level="error") import traceback - self.log_gui.log(traceback.format_exc()) + self.log_gui.log(traceback.format_exc(), level="error") raise @@ -893,26 +893,26 @@ def test_eotf(self, test_type, gray_data=None): Args: test_type: 测试类型阶数据,如果提供则使用,否则重新采集 """ - self.log_gui.log("开始测试 EOTF 曲线(HDR)...") + self.log_gui.log("开始测试 EOTF 曲线(HDR)...", level="info") self.results.start_test_item("eotf") try: # 使用传入的灰阶数据或独立采集 if gray_data is not None: - self.log_gui.log("使用共享的灰阶数据") + self.log_gui.log("使用共享的灰阶数据", level="info") results = gray_data else: - self.log_gui.log("独立采集灰阶数据") + self.log_gui.log("独立采集灰阶数据", level="info") results = self.send_fix_pattern("gray") if not results or len(results) < 2: - self.log_gui.log("EOTF 测试被中断或数据不足") + self.log_gui.log("EOTF 测试被中断或数据不足", level="info") return self.results.add_intermediate_data("eotf", "gray", results) - self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行计算") - self.log_gui.log("计算 EOTF 值...") + self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行计算", level="info") + self.log_gui.log("计算 EOTF 值...", level="info") # ========== 获取 max_index_fix ========== config_max_value = self.config.current_pattern.get( @@ -922,22 +922,23 @@ def test_eotf(self, test_type, gray_data=None): try: max_index_fix = int(config_max_value) except (ValueError, TypeError): - self.log_gui.log(f"警告: measurement_max_value 转换失败,使用默认值 10") + self.log_gui.log(f"警告: measurement_max_value 转换失败,使用默认值 10", level="warning") max_index_fix = 10 - self.log_gui.log(f"配置中的 max_index_fix = {max_index_fix}") + self.log_gui.log(f"配置中的 max_index_fix = {max_index_fix}", level="info") # 验证并调整 max_index_fix actual_max_index = len(results) - 1 if max_index_fix > actual_max_index: self.log_gui.log( - f"警告: 配置的 max_index_fix({max_index_fix}) > 实际最大索引({actual_max_index})" + f"警告: 配置的 max_index_fix({max_index_fix}) > 实际最大索引({actual_max_index})", + level="warning" ) - self.log_gui.log(f"自动调整为: {actual_max_index}") + self.log_gui.log(f"自动调整为: {actual_max_index}", level="info") max_index_fix = actual_max_index - self.log_gui.log(f"最终使用的 max_index_fix = {max_index_fix}") + self.log_gui.log(f"最终使用的 max_index_fix = {max_index_fix}", level="info") # 获取灰阶 pattern 参数(用于22293 Gamma数据对齐) pattern_params = self.config.default_pattern_gray.get( @@ -958,36 +959,36 @@ def test_eotf(self, test_type, gray_data=None): # HDR 使用 PQ 曲线,目标 gamma 设为 None(不使用传统 gamma) self.plot_eotf(L_bar, results_with_eotf_list, test_type) - self.log_gui.log("EOTF 测试完成") + self.log_gui.log("EOTF 测试完成", level="success") except Exception as e: - self.log_gui.log(f"EOTF 测试失败: {str(e)}") + self.log_gui.log(f"EOTF 测试失败: {str(e)}", level="error") import traceback - self.log_gui.log(traceback.format_exc()) + self.log_gui.log(traceback.format_exc(), level="error") raise def test_cct(self, test_type, gray_data=None): """测试色度一致性""" - self.log_gui.log("开始测试色度一致性...") + self.log_gui.log("开始测试色度一致性...", level="info") self.results.start_test_item("cct") try: if gray_data is not None: - self.log_gui.log("使用共享的灰阶数据") + self.log_gui.log("使用共享的灰阶数据", level="info") results = gray_data else: - self.log_gui.log("独立采集灰阶数据") + self.log_gui.log("独立采集灰阶数据", level="info") results = self.send_fix_pattern("gray") if not results: - self.log_gui.log("色度一致性测试被中断") + self.log_gui.log("色度一致性测试被中断", level="info") return self.results.add_intermediate_data("cct", "gray", results) - self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行色度计算") + self.log_gui.log(f"使用 {len(results)} 个灰阶数据点进行色度计算", level="info") # 提取色度坐标 cct_values = pq_algorithm.calculate_cct_from_results(results) @@ -998,12 +999,12 @@ def test_cct(self, test_type, gray_data=None): # 绘制图表 self.plot_cct(test_type) - self.log_gui.log("色度一致性测试完成") + self.log_gui.log("色度一致性测试完成", level="success") except Exception as e: - self.log_gui.log(f"色度一致性测试失败: {str(e)}") + self.log_gui.log(f"色度一致性测试失败: {str(e)}", level="error") import traceback - self.log_gui.log(traceback.format_exc()) + self.log_gui.log(traceback.format_exc(), level="error") raise @@ -1013,20 +1014,20 @@ def test_contrast(self, test_type, gray_data=None): Args: test_type: 阶数据,如果提供则使用,否则重新采集 """ - self.log_gui.log("开始测试对比度...") + self.log_gui.log("开始测试对比度...", level="info") self.results.start_test_item("contrast") try: - # ✅ 优先使用传入的灰阶数据 + # 优先使用传入的灰阶数据 if gray_data is not None: - self.log_gui.log("使用共享的灰阶数据") + self.log_gui.log("使用共享的灰阶数据", level="info") results = gray_data else: - self.log_gui.log("独立采集灰阶数据") + self.log_gui.log("独立采集灰阶数据", level="info") results = self.send_fix_pattern("gray") if not results: - self.log_gui.log("对比度测试被中断") + self.log_gui.log("对比度测试被中断", level="info") return self.results.add_intermediate_data("contrast", "gray", results) @@ -1054,19 +1055,19 @@ def test_contrast(self, test_type, gray_data=None): self.results.set_test_item_result("contrast", contrast_data) # 显示对比度结果到日志 - self.log_gui.log(f"最大亮度 (白场): {max_luminance:.2f} cd/m²") - self.log_gui.log(f"最小亮度 (黑场): {min_luminance:.4f} cd/m²") - self.log_gui.log(f"对比度: {contrast_ratio:.0f}:1") + self.log_gui.log(f"最大亮度 (白场): {max_luminance:.2f} cd/m²", level="info") + self.log_gui.log(f"最小亮度 (黑场): {min_luminance:.4f} cd/m²", level="info") + self.log_gui.log(f"对比度: {contrast_ratio:.0f}:1", level="info") # 绘制对比度图表 self.plot_contrast(contrast_data, test_type) - self.log_gui.log("对比度测试完成") + self.log_gui.log("对比度测试完成", level="success") except Exception as e: - self.log_gui.log(f"对比度测试失败: {str(e)}") + self.log_gui.log(f"对比度测试失败: {str(e)}", level="error") import traceback - self.log_gui.log(traceback.format_exc()) + self.log_gui.log(traceback.format_exc(), level="error") raise @@ -1080,58 +1081,58 @@ def test_color_accuracy(self, test_type): except (ValueError, AttributeError): target_gamma = 2.2 - self.log_gui.log("=" * 50) - self.log_gui.log(f"开始测试色准(SDR Movie 标准 - 29色)") - self.log_gui.log(f"✓ 使用 Gamma: {target_gamma}") # ← 新增 - self.log_gui.log("=" * 50) + self.log_gui.log("=" * 50, level="separator") + self.log_gui.log(f"开始测试色准(SDR Movie 标准 - 29色)", level="info") + self.log_gui.log(f"使用 Gamma: {target_gamma}", level="success") # ← 新增 + self.log_gui.log("=" * 50, level="separator") elif test_type == "hdr_movie": target_gamma = 2.4 # HDR 使用 PQ,但保留参考值 - self.log_gui.log("=" * 50) - self.log_gui.log(f"开始测试色准(HDR Movie 标准 - 29色)") - self.log_gui.log(f"✓ 使用 Gamma: PQ (参考γ={target_gamma})") # ← 新增 - self.log_gui.log("=" * 50) + self.log_gui.log("=" * 50, level="separator") + self.log_gui.log(f"开始测试色准(HDR Movie 标准 - 29色)", level="info") + self.log_gui.log(f"使用 Gamma: PQ (参考γ={target_gamma})", level="success") # ← 新增 + self.log_gui.log("=" * 50, level="separator") else: # screen_module target_gamma = 2.2 - self.log_gui.log("=" * 50) - self.log_gui.log(f"开始测试色准(屏模组 标准 - 29色)") - self.log_gui.log(f"✓ 使用 Gamma: {target_gamma}") - self.log_gui.log("=" * 50) + self.log_gui.log("=" * 50, level="separator") + self.log_gui.log(f"开始测试色准(屏模组 标准 - 29色)", level="info") + self.log_gui.log(f"使用 Gamma: {target_gamma}", level="success") + self.log_gui.log("=" * 50, level="separator") # 获取 29色名称 color_names = self.config.get_accuracy_color_names() - self.log_gui.log(f"✓ 将测试 {len(color_names)} 个色块") - self.log_gui.log(f" 色块分组:") - self.log_gui.log(f" 灰阶 (5个): {', '.join(color_names[:5])}") - self.log_gui.log(f" ColorChecker (18个): {', '.join(color_names[5:23])}") - self.log_gui.log(f" 饱和色 (6个): {', '.join(color_names[23:])}") + self.log_gui.log(f"将测试 {len(color_names)} 个色块", level="success") + self.log_gui.log(f" 色块分组:", level="info") + self.log_gui.log(f" 灰阶 (5个): {', '.join(color_names[:5])}", level="info") + self.log_gui.log(f" ColorChecker (18个): {', '.join(color_names[5:23])}", level="info") + self.log_gui.log(f" 饱和色 (6个): {', '.join(color_names[23:])}", level="info") - self.log_gui.log("=" * 50) - self.log_gui.log("开始发送色准图案并采集数据...") - self.log_gui.log("=" * 50) + self.log_gui.log("=" * 50, level="separator") + self.log_gui.log("开始发送色准图案并采集数据...", level="info") + self.log_gui.log("=" * 50, level="separator") # 发送 29色图案 measured_data_list = self.send_fix_pattern("accuracy") if measured_data_list is None or len(measured_data_list) != 29: - self.log_gui.log(f"❌ 数据数量不匹配") - self.log_gui.log(f" 期望: 29 个") + self.log_gui.log(f"[Error] 数据数量不匹配", level="error") + self.log_gui.log(f" 期望: 29 个", level="info") self.log_gui.log( f" 实际: {len(measured_data_list) if measured_data_list else 0} 个" - ) + , level="info") return # 保存原始测量数据供单步调试使用 self.results.add_intermediate_data("accuracy", "measured", measured_data_list) # ========== 计算 ΔE 2000(显示 Gamma)========== - self.log_gui.log("=" * 50) - self.log_gui.log(f"计算色准(ΔE 2000,Gamma {target_gamma})...") - self.log_gui.log("=" * 50) + self.log_gui.log("=" * 50, level="separator") + self.log_gui.log(f"计算色准(ΔE 2000,Gamma {target_gamma})...", level="info") + self.log_gui.log("=" * 50, level="separator") # 获取标准 xy 坐标 standards = self.get_accuracy_color_standards(test_type) @@ -1162,11 +1163,11 @@ def test_color_accuracy(self, test_type): elif delta_e < 5: grade, icon = "良好", "○" else: - grade, icon = "偏差", "✗" + grade, icon = "偏差", "[Error]" self.log_gui.log( f" [{i+1:2d}] {name:20s} ΔE={delta_e:5.2f} {icon} {grade}" - ) + , level="info") # ========== 统计 ========== avg_delta_e_all = sum(delta_e_values) / len(delta_e_values) @@ -1186,23 +1187,23 @@ def test_color_accuracy(self, test_type): delta_e_saturated = delta_e_values[23:29] avg_delta_e_saturated = sum(delta_e_saturated) / len(delta_e_saturated) - self.log_gui.log("=" * 50) - self.log_gui.log("色准统计(全 29色):") - self.log_gui.log("=" * 50) - self.log_gui.log(f" 平均 ΔE: {avg_delta_e_all:.2f}") - self.log_gui.log(f" 最大 ΔE: {max_delta_e_all:.2f}") - self.log_gui.log(f" 最小 ΔE: {min_delta_e_all:.2f}") - self.log_gui.log(f" 优秀 (ΔE<3): {excellent_count_all} 个") - self.log_gui.log(f" 良好 (3≤ΔE<5): {good_count_all} 个") - self.log_gui.log(f" 偏差 (ΔE≥5): {poor_count_all} 个") + self.log_gui.log("=" * 50, level="separator") + self.log_gui.log("色准统计(全 29色):", level="info") + self.log_gui.log("=" * 50, level="separator") + self.log_gui.log(f" 平均 ΔE: {avg_delta_e_all:.2f}", level="info") + self.log_gui.log(f" 最大 ΔE: {max_delta_e_all:.2f}", level="info") + self.log_gui.log(f" 最小 ΔE: {min_delta_e_all:.2f}", level="info") + self.log_gui.log(f" 优秀 (ΔE<3): {excellent_count_all} 个", level="info") + self.log_gui.log(f" 良好 (3≤ΔE<5): {good_count_all} 个", level="info") + self.log_gui.log(f" 偏差 (ΔE≥5): {poor_count_all} 个", level="info") - self.log_gui.log("") - self.log_gui.log("分组统计:") - self.log_gui.log(f" 灰阶 (5个): 平均 ΔE = {avg_delta_e_gray:.2f}") + self.log_gui.log("", level="blank") + self.log_gui.log("分组统计:", level="info") + self.log_gui.log(f" 灰阶 (5个): 平均 ΔE = {avg_delta_e_gray:.2f}", level="info") self.log_gui.log( f" ColorChecker (18个): 平均 ΔE = {avg_delta_e_colorchecker:.2f}" - ) - self.log_gui.log(f" 饱和色 (6个): 平均 ΔE = {avg_delta_e_saturated:.2f}") + , level="info") + self.log_gui.log(f" 饱和色 (6个): 平均 ΔE = {avg_delta_e_saturated:.2f}", level="info") # ========== 保存测试结果 ========== accuracy_data = { @@ -1226,7 +1227,7 @@ def test_color_accuracy(self, test_type): # ========== 绘制图表 ========== self.plot_accuracy(accuracy_data, test_type) - self.log_gui.log("色准测试完成") + self.log_gui.log("色准测试完成", level="success") def on_test_completed(self): @@ -1237,7 +1238,7 @@ def on_test_completed(self): self.save_btn.config(state=tk.NORMAL) self.clear_config_btn.config(state=tk.NORMAL) self.status_var.set("测试完成") - self.log_gui.log("测试完成") + self.log_gui.log("测试完成", level="success") # 恢复配置项按钮 if hasattr(self, "config_panel_frame"): @@ -1252,23 +1253,23 @@ def on_test_completed(self): if test_type == "screen_module" and hasattr(self, "screen_gamut_combo"): self.screen_gamut_combo.configure(state="readonly") - self.log_gui.log("✓ 屏模组色域参考标准已启用") + self.log_gui.log("屏模组色域参考标准已启用", level="success") elif test_type == "sdr_movie" and hasattr(self, "sdr_gamut_combo"): self.sdr_gamut_combo.configure(state="readonly") - self.log_gui.log("✓ SDR 色域参考标准已启用") + self.log_gui.log("SDR 色域参考标准已启用", level="success") elif test_type == "hdr_movie" and hasattr(self, "hdr_gamut_combo"): self.hdr_gamut_combo.configure(state="readonly") - self.log_gui.log("✓ HDR 色域参考标准已启用") + self.log_gui.log("HDR 色域参考标准已启用", level="success") except Exception as e: - self.log_gui.log(f"启用色域参考标准失败: {str(e)}") + self.log_gui.log(f"启用色域参考标准失败: {str(e)}", level="error") # 获取当前测试类型和选中的测试项 selected_items = self.get_selected_test_items() test_type = self.config.current_test_type - # ==================== ✅ 启用单步调试按钮 ==================== + # ==================== 启用单步调试按钮 ==================== if hasattr(self, "debug_panel"): try: # 屏模组:启用 Gamma 和 RGB 单步调试 @@ -1288,10 +1289,10 @@ def on_test_completed(self): "screen_module", "rgb", rgb_data ) - # ✅ 启用单步调试按钮 + # 启用单步调试按钮 if hasattr(self, "screen_debug_btn"): self.screen_debug_btn.config(state=tk.NORMAL) - self.log_gui.log("✓ 屏模组单步调试按钮已启用") + self.log_gui.log("屏模组单步调试按钮已启用", level="success") # SDR:启用 Gamma、色准和 RGB 单步调试 elif test_type == "sdr_movie": @@ -1317,10 +1318,10 @@ def on_test_completed(self): if rgb_data: self.debug_panel.enable_debug("sdr_movie", "rgb", rgb_data) - # ✅ 启用单步调试按钮 + # 启用单步调试按钮 if hasattr(self, "sdr_debug_btn"): self.sdr_debug_btn.config(state=tk.NORMAL) - self.log_gui.log("✓ SDR 单步调试按钮已启用") + self.log_gui.log("SDR 单步调试按钮已启用", level="success") # HDR:启用 EOTF、色准和 RGB 单步调试 elif test_type == "hdr_movie": @@ -1346,42 +1347,42 @@ def on_test_completed(self): if rgb_data: self.debug_panel.enable_debug("hdr_movie", "rgb", rgb_data) - # ✅ 启用单步调试按钮 + # 启用单步调试按钮 if hasattr(self, "hdr_debug_btn"): self.hdr_debug_btn.config(state=tk.NORMAL) - self.log_gui.log("✓ HDR 单步调试按钮已启用") + self.log_gui.log("HDR 单步调试按钮已启用", level="success") except Exception as e: - self.log_gui.log(f"启用单步调试失败: {str(e)}") + self.log_gui.log(f"启用单步调试失败: {str(e)}", level="error") # ==================== 显示色度/色域重新计算按钮 ==================== if "cct" in selected_items: try: if test_type == "screen_module" and hasattr(self, "recalc_cct_btn"): self.recalc_cct_btn.grid() - self.log_gui.log("✓ 屏模组色度参数调整按钮已启用") + self.log_gui.log("屏模组色度参数调整按钮已启用", level="success") elif test_type == "sdr_movie" and hasattr(self, "sdr_recalc_cct_btn"): self.sdr_recalc_cct_btn.grid() - self.log_gui.log("✓ SDR 色度参数调整按钮已启用") + self.log_gui.log("SDR 色度参数调整按钮已启用", level="success") elif test_type == "hdr_movie" and hasattr(self, "hdr_recalc_cct_btn"): self.hdr_recalc_cct_btn.grid() - self.log_gui.log("✓ HDR 色度参数调整按钮已启用") + self.log_gui.log("HDR 色度参数调整按钮已启用", level="success") except Exception as e: - self.log_gui.log(f"显示色度重新计算按钮失败: {str(e)}") + self.log_gui.log(f"显示色度重新计算按钮失败: {str(e)}", level="error") if "gamut" in selected_items: try: if test_type == "screen_module" and hasattr(self, "recalc_gamut_btn"): self.recalc_gamut_btn.grid() - self.log_gui.log("✓ 屏模组色域参考调整按钮已启用") + self.log_gui.log("屏模组色域参考调整按钮已启用", level="success") elif test_type == "sdr_movie" and hasattr(self, "sdr_recalc_gamut_btn"): self.sdr_recalc_gamut_btn.grid() - self.log_gui.log("✓ SDR 色域参考调整按钮已启用") + self.log_gui.log("SDR 色域参考调整按钮已启用", level="success") elif test_type == "hdr_movie" and hasattr(self, "hdr_recalc_gamut_btn"): self.hdr_recalc_gamut_btn.grid() - self.log_gui.log("✓ HDR 色域参考调整按钮已启用") + self.log_gui.log("HDR 色域参考调整按钮已启用", level="success") except Exception as e: - self.log_gui.log(f"显示色域重新计算按钮失败: {str(e)}") + self.log_gui.log(f"显示色域重新计算按钮失败: {str(e)}", level="error") messagebox.showinfo("完成", "测试已完成!") @@ -1403,7 +1404,7 @@ def on_custom_template_test_completed(self): except: pass - self.log_gui.log("客户模板测试完成") + self.log_gui.log("客户模板测试完成", level="success") messagebox.showinfo("完成", "客户模板测试已完成!") diff --git a/app/tests/local_dimming.py b/app/tests/local_dimming.py index af73463..1568eae 100644 --- a/app/tests/local_dimming.py +++ b/app/tests/local_dimming.py @@ -1,4 +1,4 @@ -"""Local Dimming 测试逻辑(应用层)。 +"""Local Dimming 测试逻辑(应用层)。 整合自原 drivers/local_dimming_test.py:窗口图片生成与测试主循环 直接落在本模块,UCD 通用操作下沉到 drivers.ucd_helpers。 @@ -107,51 +107,51 @@ def start_local_dimming_test(self): def worker(): log = self.log_gui.log - log("=" * 60) - log("开始 Local Dimming 测试") - log("=" * 60) + log("=" * 60, level="separator") + log("开始 Local Dimming 测试", level="info") + log("=" * 60, level="separator") width, height = get_current_resolution(self.ucd) total = len(DEFAULT_WINDOW_PERCENTAGES) - log(f" 分辨率: {width}x{height}") - log(f" 测试窗口: {DEFAULT_WINDOW_PERCENTAGES}") - log(f" 等待时间: {wait_time} 秒") + log(f" 分辨率: {width}x{height}", level="info") + log(f" 测试窗口: {DEFAULT_WINDOW_PERCENTAGES}", level="info") + log(f" 等待时间: {wait_time} 秒", level="info") results = [] for i, percentage in enumerate(DEFAULT_WINDOW_PERCENTAGES, 1): if stop_event.is_set(): - log("⚠️ 测试已停止") + log("测试已停止", level="error") break - log(f"[{i}/{total}] 测试 {percentage}% 窗口...") + log(f"[{i}/{total}] 测试 {percentage}% 窗口...", level="info") try: image_path = _ensure_window_image(width, height, percentage) except Exception as e: - log(f" ❌ 图像生成失败: {e}") + log(f" [Error] 图像生成失败: {e}", level="error") continue if not send_image_pattern(self.ucd, image_path): - log(f" ❌ {percentage}% 窗口发送失败,跳过") + log(f" [Error] {percentage}% 窗口发送失败,跳过", level="error") continue - log(f" ⏳ 等待 {wait_time} 秒...") + log(f"等待 {wait_time} 秒...", level="info") time.sleep(wait_time) try: x, y, lv, _X, _Y, _Z = self.ca.readAllDisplay() except Exception as e: - log(f" ❌ 采集亮度异常: {e}") + log(f" [Error] 采集亮度异常: {e}", level="error") continue if lv is None: - log(f" ❌ {percentage}% 窗口采集失败") + log(f" [Error] {percentage}% 窗口采集失败", level="error") continue - log(f" ✓ 采集亮度: {lv:.2f} cd/m²") + log(f"采集亮度: {lv:.2f} cd/m²", level="info") results.append((percentage, x, y, lv, _X, _Y, _Z)) - log("=" * 60) - log(f"✅ Local Dimming 测试完成 ({len(results)}/{total})") - log("=" * 60) + log("=" * 60, level="separator") + log(f"Local Dimming 测试完成 ({len(results)}/{total})", level="success") + log("=" * 60, level="separator") self.ld_test_results = results self._dispatch_ui(self.update_ld_results, results) @@ -184,7 +184,7 @@ def send_ld_window(self, percentage): messagebox.showwarning("警告", "请先连接 UCD323 设备") return - self.log_gui.log(f"🔆 发送 {percentage}% 窗口...") + self.log_gui.log(f"🔆 发送 {percentage}% 窗口...", level="info") self.current_ld_percentage = percentage def send(): @@ -192,12 +192,12 @@ def send_ld_window(self, percentage): try: image_path = _ensure_window_image(width, height, percentage) except Exception as e: - self._dispatch_ui(self.log_gui.log, f"❌ 图像生成失败: {e}") + self._dispatch_ui(self.log_gui.log, f"[Error] 图像生成失败: {e}") return ok = send_image_pattern(self.ucd, image_path) msg = ( - f"✅ {percentage}% 窗口已发送" if ok - else f"❌ {percentage}% 窗口发送失败" + f"{percentage}% 窗口已发送" if ok + else f"[Error] {percentage}% 窗口发送失败" ) self._dispatch_ui(self.log_gui.log, msg) @@ -213,16 +213,16 @@ def measure_ld_luminance(self): messagebox.showinfo("提示", "请先发送一个窗口图案") return - self.log_gui.log("📏 正在采集亮度...") + self.log_gui.log("📏 正在采集亮度...", level="info") def measure(): try: x, y, lv, _X, _Y, _Z = self.ca.readAllDisplay() except Exception as e: - self._dispatch_ui(self.log_gui.log, f"❌ 采集异常: {str(e)}") + self._dispatch_ui(self.log_gui.log, f"[Error] 采集异常: {str(e)}") return if lv is None: - self._dispatch_ui(self.log_gui.log, "❌ 采集失败") + self._dispatch_ui(self.log_gui.log, "[Error] 采集失败") return timestamp = datetime.datetime.now().strftime("%H:%M:%S") self._dispatch_ui( @@ -236,7 +236,7 @@ def measure_ld_luminance(self): f"{lv:.2f}", f"{x:.4f}", f"{y:.4f}", timestamp, ), ) - self._dispatch_ui(self.log_gui.log, f"✅ 采集完成: {lv:.2f} cd/m²") + self._dispatch_ui(self.log_gui.log, f"采集完成: {lv:.2f} cd/m²") threading.Thread(target=measure, daemon=True).start() @@ -247,7 +247,7 @@ def clear_ld_records(self): self.ld_tree.delete(item) self.ld_result_label.config(text="亮度: -- cd/m² | x: -- | y: --") self.current_ld_percentage = None - self.log_gui.log("🗑️ 测试记录已清空") + self.log_gui.log("测试记录已清空", level="info") def save_local_dimming_results(self): @@ -274,8 +274,8 @@ def save_local_dimming_results(self): writer.writerow(["窗口百分比", "亮度 (cd/m²)", "x", "y", "时间"]) for item in self.ld_tree.get_children(): writer.writerow(self.ld_tree.item(item, "values")) - self.log_gui.log(f"✓ 测试结果已保存: {save_path}") + self.log_gui.log(f"测试结果已保存: {save_path}", level="success") messagebox.showinfo("成功", f"测试结果已保存到:\n{save_path}") except Exception as e: - self.log_gui.log(f"❌ 保存失败: {str(e)}") + self.log_gui.log(f"[Error] 保存失败: {str(e)}", level="error") messagebox.showerror("错误", f"保存失败: {str(e)}") diff --git a/app/views/chart_frame.py b/app/views/chart_frame.py index d079e0c..d763bf4 100644 --- a/app/views/chart_frame.py +++ b/app/views/chart_frame.py @@ -1,4 +1,4 @@ -"""图表框架相关逻辑(Step 3 重构)。 +"""图表框架相关逻辑(Step 3 重构)。 从 pqAutomationApp.PQAutomationApp 中搬迁而来。每个函数第一行 `self = app` 以保留原有 `self.xxx` 属性访问不变。 @@ -21,7 +21,7 @@ def init_gamut_chart(self): canvas_widget = self.gamut_canvas.get_tk_widget() canvas_widget.pack(expand=True, fill=tk.BOTH) - # ✅ 恢复原来的大尺寸:0.84 高度 + # 恢复原来的大尺寸:0.84 高度 self.gamut_ax_xy = self.gamut_fig.add_axes( [0.02, 0.08, 0.46, 0.84] ) # ← 改回 0.84 @@ -47,7 +47,7 @@ def init_gamut_chart(self): self.gamut_canvas.draw() def init_gamma_chart(self): - """初始化Gamma曲线图表 - 左侧曲线 + 右侧表格(✅ 4列 + 通用说明)""" + """初始化Gamma曲线图表 - 左侧曲线 + 右侧表格(4列 + 通用说明)""" container = ttk.Frame(self.gamma_chart_frame) container.pack(expand=True, fill=tk.BOTH) @@ -156,7 +156,7 @@ def init_gamma_chart(self): self.gamma_canvas.draw() def init_eotf_chart(self): - """初始化 EOTF 曲线图表(HDR 专用)- 左侧曲线 + 右侧表格(✅ 4列)""" + """初始化 EOTF 曲线图表(HDR 专用)- 左侧曲线 + 右侧表格(4列)""" container = ttk.Frame(self.eotf_chart_frame) container.pack(expand=True, fill=tk.BOTH) @@ -741,7 +741,7 @@ def update_chart_tabs_state(self): except Exception as e: if hasattr(self, "log_gui"): - self.log_gui.log(f"更新Tab状态失败: {str(e)}") + self.log_gui.log(f"更新Tab状态失败: {str(e)}", level="error") def create_result_chart_frame(self): """创建结果图表区域 - 6个独立Tab(Gamma 和 EOTF 分离)""" @@ -790,10 +790,10 @@ def create_result_chart_frame(self): # 绑定Tab切换事件 self.chart_notebook.bind("<>", self.on_chart_tab_changed) - # ==================== ✅ 在图表下方创建单步调试面板 ==================== + # ==================== 在图表下方创建单步调试面板 ==================== self.debug_container = ttk.LabelFrame( self.result_frame, # ← 放在 result_frame 内,图表正下方 - text="🔧 单步调试", + text=" 单步调试", padding=10, ) # 默认不显示 @@ -801,7 +801,7 @@ def create_result_chart_frame(self): # 创建单步调试面板实例 self.debug_panel = PQDebugPanel(self.debug_container, self) - self.log_gui.log("✓ 单步调试面板已创建(放在测试结果图表下方)") + self.log_gui.log("单步调试面板已创建(放在测试结果图表下方)", level="success") def on_chart_tab_changed(self, event): """Tab切换时的事件处理""" @@ -810,5 +810,5 @@ def on_chart_tab_changed(self, event): self.chart_notebook.select() ) except Exception as e: - self.log_gui.log(f"Tab切换事件处理失败: {str(e)}") + self.log_gui.log(f"Tab切换事件处理失败: {str(e)}", level="error") diff --git a/app/views/panels/cct_panel.py b/app/views/panels/cct_panel.py index c0df096..e98883b 100644 --- a/app/views/panels/cct_panel.py +++ b/app/views/panels/cct_panel.py @@ -1,4 +1,4 @@ -"""CCT 参数面板及其处理函数(与主文件重构版本保持同步)。""" +"""CCT 参数面板及其处理函数(与主文件重构版本保持同步)。""" import time import traceback @@ -10,7 +10,7 @@ import algorithm.pq_algorithm as pq_algorithm def create_cct_params_frame(self): - """创建色度参数设置区域 - 屏模组、SDR、HDR 独立(✅ 增加色域参考标准选择 + 单步调试按钮)""" + """创建色度参数设置区域 - 屏模组、SDR、HDR 独立(增加色域参考标准选择 + 单步调试按钮)""" # ==================== 屏模组色度参数 Frame ==================== self.cct_params_frame = ttk.LabelFrame( @@ -84,7 +84,7 @@ def create_cct_params_frame(self): ) self.screen_gamut_combo = screen_gamut_combo - # ==================== ✅ 单步调试按钮(右侧第二行)==================== + # ==================== 单步调试按钮(右侧第二行)==================== ttk.Label(self.cct_params_frame, text="单步调试:").grid( row=1, column=2, sticky=tk.W, padx=(20, 5), pady=3 ) @@ -94,7 +94,7 @@ def create_cct_params_frame(self): text="打开调试面板", command=self.toggle_screen_debug_panel, bootstyle="info-outline", - state=tk.DISABLED, # 初始禁用 + # state=tk.DISABLED, # 初始禁用 width=15, ) self.screen_debug_btn.grid(row=1, column=3, sticky=tk.W, padx=5, pady=3) @@ -201,7 +201,7 @@ def create_cct_params_frame(self): sdr_gamut_combo.bind("<>", self.on_sdr_gamut_ref_changed) self.sdr_gamut_combo = sdr_gamut_combo - # ==================== ✅ SDR 单步调试按钮(右侧第二行)==================== + # ==================== SDR 单步调试按钮(右侧第二行)==================== ttk.Label(self.sdr_cct_params_frame, text="单步调试:").grid( row=1, column=2, sticky=tk.W, padx=(20, 5), pady=3 ) @@ -211,7 +211,7 @@ def create_cct_params_frame(self): text="打开调试面板", command=self.toggle_sdr_debug_panel, bootstyle="info-outline", - state=tk.DISABLED, # 初始禁用 + # state=tk.DISABLED, # 初始禁用 width=15, ) self.sdr_debug_btn.grid(row=1, column=3, sticky=tk.W, padx=5, pady=3) @@ -318,7 +318,7 @@ def create_cct_params_frame(self): hdr_gamut_combo.bind("<>", self.on_hdr_gamut_ref_changed) self.hdr_gamut_combo = hdr_gamut_combo - # ==================== ✅ HDR 单步调试按钮(右侧第二行)==================== + # ==================== HDR 单步调试按钮(右侧第二行)==================== ttk.Label(self.hdr_cct_params_frame, text="单步调试:").grid( row=1, column=2, sticky=tk.W, padx=(20, 5), pady=3 ) @@ -328,7 +328,7 @@ def create_cct_params_frame(self): text="打开调试面板", command=self.toggle_hdr_debug_panel, bootstyle="info-outline", - state=tk.DISABLED, # 初始禁用 + # state=tk.DISABLED, # 初始禁用 width=15, ) self.hdr_debug_btn.grid(row=1, column=3, sticky=tk.W, padx=5, pady=3) @@ -427,7 +427,7 @@ def _handle_cct_focus_out(self, var, default_value, save_func, label): if value == "": var.set(str(default_value)) if hasattr(self, "log_gui"): - self.log_gui.log(f"✓ {label} 参数为空,恢复默认值: {default_value}") + self.log_gui.log(f"{label} 参数为空,恢复默认值: {default_value}", level="success") else: try: float_val = float(value) @@ -435,19 +435,19 @@ def _handle_cct_focus_out(self, var, default_value, save_func, label): var.set(str(default_value)) if hasattr(self, "log_gui"): self.log_gui.log( - f"⚠️ {label} 参数超出范围,恢复默认值: {default_value}" - ) + f"{label} 参数超出范围,恢复默认值: {default_value}" + , level="error") except ValueError: var.set(str(default_value)) if hasattr(self, "log_gui"): self.log_gui.log( - f"⚠️ {label} 参数无效,恢复默认值: {default_value}" - ) + f"{label} 参数无效,恢复默认值: {default_value}" + , level="error") save_func() except Exception as e: if hasattr(self, "log_gui"): - self.log_gui.log(f"处理 {label} 参数失败: {str(e)}") + self.log_gui.log(f"处理 {label} 参数失败: {str(e)}", level="error") def on_sdr_cct_param_focus_out(self, var, default_value): @@ -475,7 +475,7 @@ def recalculate_cct(self): try: # 1. 保存新参数 self.save_cct_params() - self.log_gui.log("✓ 色度参数已更新") + self.log_gui.log("色度参数已更新", level="success") # 2. 收起配置项 if hasattr(self, "config_panel_frame"): @@ -493,7 +493,7 @@ def recalculate_cct(self): # 4. 检查是否有数据 if not hasattr(self, "results") or not self.results: - self.log_gui.log("⚠️ 没有测试数据,无法重新绘制") + self.log_gui.log("没有测试数据,无法重新绘制", level="error") messagebox.showwarning("警告", "请先完成测试后再重新计算") return @@ -503,14 +503,14 @@ def recalculate_cct(self): gray_data = self.results.get_intermediate_data("cct", "gray") if not gray_data or len(gray_data) < 2: - self.log_gui.log("⚠️ 没有可用的灰阶数据") + self.log_gui.log("没有可用的灰阶数据", level="error") messagebox.showwarning("警告", "没有找到色度测试数据") return # 6. 重新计算 CCT - self.log_gui.log("=" * 50) - self.log_gui.log("开始重新计算色度一致性...") - self.log_gui.log("=" * 50) + self.log_gui.log("=" * 50, level="separator") + self.log_gui.log("开始重新计算色度一致性...", level="info") + self.log_gui.log("=" * 50, level="separator") cct_values = pq_algorithm.calculate_cct_from_results(gray_data) @@ -521,14 +521,14 @@ def recalculate_cct(self): test_type = self.config.current_test_type self.plot_cct(test_type) - self.log_gui.log("✓ 色度图已重新绘制") - self.log_gui.log("=" * 50) + self.log_gui.log("色度图已重新绘制", level="success") + self.log_gui.log("=" * 50, level="separator") messagebox.showinfo("成功", "色度图已根据新参数重新绘制!") except Exception as e: - self.log_gui.log(f"❌ 重新计算失败: {str(e)}") - self.log_gui.log(traceback.format_exc()) + self.log_gui.log(f"[Error] 重新计算失败: {str(e)}", level="error") + self.log_gui.log(traceback.format_exc(), level="error") messagebox.showerror("错误", f"重新计算失败: {str(e)}") @@ -551,7 +551,7 @@ def recalculate_gamut(self): # 3. 检查是否有数据 if not hasattr(self, "results") or not self.results: - self.log_gui.log("⚠️ 没有测试数据,无法重新绘制") + self.log_gui.log("没有测试数据,无法重新绘制", level="error") messagebox.showwarning("警告", "请先完成测试后再重新计算") return @@ -559,7 +559,7 @@ def recalculate_gamut(self): rgb_data = self.results.get_intermediate_data("gamut", "rgb") if not rgb_data or len(rgb_data) < 3: - self.log_gui.log("⚠️ 没有可用的色域数据") + self.log_gui.log("没有可用的色域数据", level="error") messagebox.showwarning("警告", "没有找到色域测试数据") return @@ -576,9 +576,9 @@ def recalculate_gamut(self): else: reference_standard = "DCI-P3" - self.log_gui.log("=" * 50) - self.log_gui.log(f"开始重新计算色域(参考标准: {reference_standard})...") - self.log_gui.log("=" * 50) + self.log_gui.log("=" * 50, level="separator") + self.log_gui.log(f"开始重新计算色域(参考标准: {reference_standard})...", level="info") + self.log_gui.log("=" * 50, level="separator") # 7. 重新计算 XY 色域覆盖率 xy_points = [[result[0], result[1]] for result in rgb_data] @@ -602,10 +602,10 @@ def recalculate_gamut(self): ) reference_standard = "DCI-P3" - self.log_gui.log(f"✓ 参考标准: {reference_standard}") - self.log_gui.log(f"✓ XY 色域覆盖率: {coverage_xy:.1f}%") + self.log_gui.log(f"参考标准: {reference_standard}", level="success") + self.log_gui.log(f"XY 色域覆盖率: {coverage_xy:.1f}%", level="success") - # ========== ✅✅✅ 8. 重新计算 UV 色域覆盖率 ========== + # ========== ✅✅8. 重新计算 UV 色域覆盖率 ========== # 将 XY 坐标转换为 UV 坐标 uv_points = [] for x, y in xy_points: @@ -621,7 +621,7 @@ def recalculate_gamut(self): except ZeroDivisionError: continue - self.log_gui.log(f"✓ 转换后的 UV 点数量: {len(uv_points)}") + self.log_gui.log(f"转换后的 UV 点数量: {len(uv_points)}", level="success") # 根据参考标准计算 UV 覆盖率 if reference_standard == "BT.2020": @@ -641,10 +641,10 @@ def recalculate_gamut(self): uv_points ) - self.log_gui.log(f"✓ UV 色域覆盖率: {coverage_uv:.1f}%") + self.log_gui.log(f"UV 色域覆盖率: {coverage_uv:.1f}%", level="success") # ======================================================== - # 9. ✅ 更新结果(同时保存 XY 和 UV 覆盖率) + # 9. 更新结果(同时保存 XY 和 UV 覆盖率) self.results.set_test_item_result( "gamut", { @@ -659,13 +659,13 @@ def recalculate_gamut(self): }, ) - self.log_gui.log("✓ 测试结果已更新到 results 对象") + self.log_gui.log("测试结果已更新到 results 对象", level="success") # 10. 重新绘制色域图 self.plot_gamut(rgb_data, coverage_xy, test_type) - self.log_gui.log("✓ 色域图已重新绘制") - self.log_gui.log("=" * 50) + self.log_gui.log("色域图已重新绘制", level="success") + self.log_gui.log("=" * 50, level="separator") messagebox.showinfo( "成功", @@ -675,8 +675,8 @@ def recalculate_gamut(self): ) except Exception as e: - self.log_gui.log(f"❌ 重新计算失败: {str(e)}") - self.log_gui.log(traceback.format_exc()) + self.log_gui.log(f"[Error] 重新计算失败: {str(e)}", level="error") + self.log_gui.log(traceback.format_exc(), level="error") messagebox.showerror("错误", f"重新计算失败: {str(e)}") @@ -709,7 +709,7 @@ def reload_cct_params(self): except Exception as e: if hasattr(self, "log_gui"): - self.log_gui.log(f"重新加载色度参数失败: {str(e)}") + self.log_gui.log(f"重新加载色度参数失败: {str(e)}", level="error") def toggle_cct_params_frame(self): @@ -743,7 +743,7 @@ def toggle_cct_params_frame(self): self.hdr_cct_params_frame.pack(fill=tk.X, padx=5, pady=5) else: if hasattr(self, "log_gui"): - self.log_gui.log("[ERROR] HDR 色度参数框尚未创建") + self.log_gui.log("[ERROR] HDR 色度参数框尚未创建", level="error") # ---- gamut 参考标准改变回调(统一实现) ---- @@ -758,14 +758,14 @@ def _on_gamut_ref_changed(self, test_type, event=None): cfg = _GAMUT_REF_CONFIGS[test_type] try: new_ref = getattr(self, cfg["var_attr"]).get() - self.log_gui.log(f"✓ {cfg['label']} 色域参考标准已更改为: {new_ref}") + self.log_gui.log(f"{cfg['label']} 色域参考标准已更改为: {new_ref}", level="success") if test_type not in self.config.current_test_types: self.config.current_test_types[test_type] = {} self.config.current_test_types[test_type]["gamut_reference"] = new_ref self.save_pq_config() except Exception as e: - self.log_gui.log(f"保存 {cfg['label']} 色域参考标准失败: {str(e)}") + self.log_gui.log(f"保存 {cfg['label']} 色域参考标准失败: {str(e)}", level="error") def on_screen_gamut_ref_changed(self, event=None): diff --git a/app/views/panels/custom_template_panel.py b/app/views/panels/custom_template_panel.py index 8e1dba8..6cbafc9 100644 --- a/app/views/panels/custom_template_panel.py +++ b/app/views/panels/custom_template_panel.py @@ -1,4 +1,4 @@ -"""自定义模板结果面板(Step 6 重构)。""" +"""自定义模板结果面板(Step 6 重构)。""" import threading import time @@ -274,7 +274,7 @@ def _run_custom_row_single_step(self, item_id, row_no): """后台执行客户模板单步测试""" try: self._dispatch_ui(self.status_var.set, f"单步测试第 {row_no} 行...") - self.log_gui.log(f"开始单步测试第 {row_no} 行") + self.log_gui.log(f"开始单步测试第 {row_no} 行", level="info") self.config.set_current_pattern("custom") @@ -295,7 +295,7 @@ def _run_custom_row_single_step(self, item_id, row_no): ) if row_no > len(converted_params): - self.log_gui.log(f"❌ 行号超出 pattern 范围: {row_no}/{len(converted_params)}") + self.log_gui.log(f"[Error] 行号超出 pattern 范围: {row_no}/{len(converted_params)}", level="error") self._dispatch_ui(self.status_var.set, "单步测试失败:行号超范围") return @@ -335,11 +335,11 @@ def _run_custom_row_single_step(self, item_id, row_no): self._update_custom_result_row, item_id, row_no, row_data ) - self.log_gui.log(f"✓ 第 {row_no} 行单步测试完成并已覆盖") + self.log_gui.log(f"第 {row_no} 行单步测试完成并已覆盖", level="success") self._dispatch_ui(self.status_var.set, f"第 {row_no} 行单步测试完成") except Exception as e: - self.log_gui.log(f"❌ 单步测试失败: {str(e)}") + self.log_gui.log(f"[Error] 单步测试失败: {str(e)}", level="error") self._dispatch_ui(self.status_var.set, "单步测试失败") @@ -423,7 +423,7 @@ def copy_custom_result_table(self): if hasattr(self, "status_var"): self.status_var.set(f"已复制 {len(items)} 行客户模板数据到剪贴板") if hasattr(self, "log_gui"): - self.log_gui.log(f"✓ 已复制客户模板表格数据({len(items)} 行)") + self.log_gui.log(f"已复制客户模板表格数据({len(items)} 行)", level="success") def fill_custom_result_test_data(self): @@ -465,7 +465,7 @@ def fill_custom_result_test_data(self): if hasattr(self, "status_var"): self.status_var.set("已填充 147 行客户模板测试数据") if hasattr(self, "log_gui"): - self.log_gui.log("✓ 已填充 147 行客户模板测试数据") + self.log_gui.log("已填充 147 行客户模板测试数据", level="success") def clear_custom_template_results(self): @@ -511,7 +511,7 @@ def auto_expand_custom_result_view(self): self.root.update_idletasks() except Exception as e: if hasattr(self, "log_gui"): - self.log_gui.log(f"⚠️ 自动扩展客户模板窗口失败: {str(e)}") + self.log_gui.log(f"自动扩展客户模板窗口失败: {str(e)}", level="error") def append_custom_template_result(self, row_no, result_data): diff --git a/app/views/panels/main_layout.py b/app/views/panels/main_layout.py index cc7a388..e23317f 100644 --- a/app/views/panels/main_layout.py +++ b/app/views/panels/main_layout.py @@ -1,4 +1,4 @@ -"""主布局面板创建函数(Step 6 重构)。""" +"""主布局面板创建函数(Step 6 重构)。""" import re import tkinter as tk @@ -405,7 +405,7 @@ def create_test_type_frame(self): fill=tk.X, padx=10, pady=10 ) - # ✅ 只保留日志按钮 + # 只保留日志按钮 self.log_btn = ttk.Button( self.sidebar_frame, text="测试日志", @@ -515,7 +515,7 @@ def on_screen_module_timing_changed(self, event=None): selected_timing = self.screen_module_timing_var.get() # 记录日志 - self.log_gui.log(f"屏模组信号格式已更改为: {selected_timing}") + self.log_gui.log(f"屏模组信号格式已更改为: {selected_timing}", level="info") match = re.search(r"(\d+)x(\d+)\s*@\s*(\d+)", selected_timing) if match: @@ -523,28 +523,28 @@ def on_screen_module_timing_changed(self, event=None): height = int(match.group(2)) refresh_rate = int(match.group(3)) - self.log_gui.log(f" ├─ 分辨率: {width}x{height}") - self.log_gui.log(f" └─ 刷新率: {refresh_rate}Hz") + self.log_gui.log(f" ├─ 分辨率: {width}x{height}", level="info") + self.log_gui.log(f" └─ 刷新率: {refresh_rate}Hz", level="info") # 根据分辨率给出提示 if width >= 3840: # 4K及以上 - self.log_gui.log(" ℹ️ 检测到4K分辨率") + self.log_gui.log(" ℹ️ 检测到4K分辨率", level="info") if refresh_rate >= 120: - self.log_gui.log(" ℹ️ 检测到高刷新率") + self.log_gui.log(" ℹ️ 检测到高刷新率", level="info") # 更新配置 self.config.set_current_timing(selected_timing) # 如果正在测试,提示用户 if self.testing: - self.log_gui.log("⚠️ 警告: 测试进行中,信号格式更改将在下次测试时生效") + self.log_gui.log("警告: 测试进行中,信号格式更改将在下次测试时生效", level="error") # 保存配置 self.save_pq_config() except Exception as e: - self.log_gui.log(f"❌ 屏模组信号格式更改失败: {str(e)}") + self.log_gui.log(f"[Error] 屏模组信号格式更改失败: {str(e)}", level="error") def update_test_items(self): diff --git a/app/views/panels/side_panels.py b/app/views/panels/side_panels.py index 470713b..aec3740 100644 --- a/app/views/panels/side_panels.py +++ b/app/views/panels/side_panels.py @@ -1,4 +1,4 @@ -"""侧边面板(日志 / Local Dimming / 调试)(Step 6 重构)。""" +"""侧边面板(日志 / Local Dimming / 调试)(Step 6 重构)。""" import traceback import tkinter as tk @@ -141,7 +141,7 @@ def create_local_dimming_panel(self): self.ld_clear_btn = ttk.Button( bottom_frame, - text="🗑️ 清空记录", + text="清空记录", command=self.clear_ld_records, bootstyle="danger-outline", width=12, @@ -187,7 +187,7 @@ DEBUG_PANEL_CONFIGS = { "screen_module": { "window_attr": "debug_window", "btn_attr": "screen_debug_btn", - "title": "🔧 单步调试面板", + "title": " 单步调试面板", "window_log_prefix": "", "data_log_prefix": "屏模组", "failure_data_label": "调试数据", @@ -200,7 +200,7 @@ DEBUG_PANEL_CONFIGS = { "sdr_movie": { "window_attr": "sdr_debug_window", "btn_attr": "sdr_debug_btn", - "title": "🔧 SDR 单步调试面板", + "title": " SDR 单步调试面板", "window_log_prefix": "SDR ", "data_log_prefix": "SDR", "failure_data_label": "SDR 调试数据", @@ -213,7 +213,7 @@ DEBUG_PANEL_CONFIGS = { "hdr_movie": { "window_attr": "hdr_debug_window", "btn_attr": "hdr_debug_btn", - "title": "🔧 HDR 单步调试面板", + "title": " HDR 单步调试面板", "window_log_prefix": "HDR ", "data_log_prefix": "HDR", "failure_data_label": "HDR 调试数据", @@ -238,7 +238,6 @@ def _toggle_debug_panel(self, test_type): if existing is not None and existing.winfo_exists(): existing.destroy() btn.config(text="打开调试面板") - self.log_gui.log(f"✓ {wlp}单步调试面板已关闭") return # 创建新窗口 @@ -255,38 +254,40 @@ def _toggle_debug_panel(self, test_type): # 创建调试面板实例(不要对它调用 pack) debug_panel_instance = PQDebugPanel(debug_container, self) - self.log_gui.log(f"✓ {wlp}单步调试面板实例已创建") - # 重新启用调试(如果有数据) try: selected_items = self.get_selected_test_items() dlp = cfg["data_log_prefix"] - for item_key, debug_key, (cat, sub), data_label, enable_desc in cfg["data_items"]: - if item_key not in selected_items: - continue - data = self.results.get_intermediate_data(cat, sub) - if not data: - if test_type == "screen_module" and item_key == "gamma": - self.log_gui.log(" ✗ 没有可用的灰阶数据") - continue - self.log_gui.log(f" → 加载 {len(data)} 个{data_label}数据点") - debug_panel_instance.enable_debug(test_type, debug_key, data) - self.log_gui.log(f"✓ {dlp} {enable_desc}已重新启用") + results_obj = getattr(self, "results", None) + if results_obj is None: + self.log_gui.log(f"{dlp} 暂无可用测试结果,面板已打开", level="warning") + else: + for item_key, debug_key, (cat, sub), data_label, enable_desc in cfg["data_items"]: + if item_key not in selected_items: + continue + data = results_obj.get_intermediate_data(cat, sub) + if not data: + if test_type == "screen_module" and item_key == "gamma": + self.log_gui.log("[Error] 没有可用的灰阶数据", level="error") + continue + self.log_gui.log(f" → 加载 {len(data)} 个{data_label}数据点", level="info") + debug_panel_instance.enable_debug(test_type, debug_key, data) + self.log_gui.log(f"{dlp} {enable_desc}已重新启用", level="success") except Exception as e: - self.log_gui.log(f"⚠️ 加载{cfg['failure_data_label']}失败: {str(e)}") - self.log_gui.log(traceback.format_exc()) + self.log_gui.log(f"加载{cfg['failure_data_label']}失败: {str(e)}", level="error") + self.log_gui.log(traceback.format_exc(), level="error") btn.config(text="关闭调试面板") def on_closing(): btn.config(text="打开调试面板") getattr(self, win_attr).destroy() - self.log_gui.log(f"✓ {wlp}单步调试窗口已关闭") + self.log_gui.log(f"{wlp}单步调试窗口已关闭", level="success") win.protocol("WM_DELETE_WINDOW", on_closing) win.update_idletasks() - self.log_gui.log(f"✓ {wlp}单步调试面板已打开(独立窗口)") + self.log_gui.log(f"{wlp}单步调试面板已打开(独立窗口)", level="success") def toggle_screen_debug_panel(self): diff --git a/app/views/pq_debug_panel.py b/app/views/pq_debug_panel.py index 41d347c..80461fb 100644 --- a/app/views/pq_debug_panel.py +++ b/app/views/pq_debug_panel.py @@ -1,4 +1,4 @@ -""" +""" PQ 单步调试面板 支持屏模组、SDR、HDR 三种测试类型的单步调试功能 """ @@ -34,7 +34,7 @@ class PQDebugPanel: # 原始测试数据(用于对比) self.original_data = {} - # ==================== ✅ 创建主容器并自动 pack ==================== + # ==================== 创建主容器并自动 pack ==================== self.main_container = ttk.Frame(parent) self.main_container.pack(fill=tk.BOTH, expand=True) @@ -600,13 +600,13 @@ class PQDebugPanel: if test_type == "screen_module": self.screen_frame.pack(fill=tk.BOTH, expand=True) - self.app.log_gui.log("✓ 显示屏模组调试面板") + self.app.log_gui.log("显示屏模组调试面板", level="success") elif test_type == "sdr_movie": self.sdr_frame.pack(fill=tk.BOTH, expand=True) - self.app.log_gui.log("✓ 显示 SDR 调试面板") + self.app.log_gui.log("显示 SDR 调试面板", level="success") elif test_type == "hdr_movie": self.hdr_frame.pack(fill=tk.BOTH, expand=True) - self.app.log_gui.log("✓ 显示 HDR 调试面板") + self.app.log_gui.log("显示 HDR 调试面板", level="success") # ==================== 启用/禁用控制 ==================== @@ -631,39 +631,39 @@ class PQDebugPanel: if test_item == "gamma": self.screen_gray_combo.config(state="readonly") self.screen_test_btn.config(state=tk.NORMAL) - self.app.log_gui.log("✓ 屏模组 Gamma 单步调试已启用") + self.app.log_gui.log("屏模组 Gamma 单步调试已启用", level="success") elif test_item == "rgb": self.screen_rgb_combo.config(state="readonly") self.screen_rgb_test_btn.config(state=tk.NORMAL) - self.app.log_gui.log("✓ 屏模组 RGB 单步调试已启用") + self.app.log_gui.log("屏模组 RGB 单步调试已启用", level="success") elif test_type == "sdr_movie": if test_item == "gamma": self.sdr_gray_combo.config(state="readonly") self.sdr_gamma_test_btn.config(state=tk.NORMAL) - self.app.log_gui.log("✓ SDR Gamma 单步调试已启用") + self.app.log_gui.log("SDR Gamma 单步调试已启用", level="success") elif test_item == "accuracy": self.sdr_color_combo.config(state="readonly") self.sdr_accuracy_test_btn.config(state=tk.NORMAL) - self.app.log_gui.log("✓ SDR 色准单步调试已启用") + self.app.log_gui.log("SDR 色准单步调试已启用", level="success") elif test_item == "rgb": self.sdr_rgb_combo.config(state="readonly") self.sdr_rgb_test_btn.config(state=tk.NORMAL) - self.app.log_gui.log("✓ SDR RGB 单步调试已启用") + self.app.log_gui.log("SDR RGB 单步调试已启用", level="success") elif test_type == "hdr_movie": if test_item == "eotf": self.hdr_gray_combo.config(state="readonly") self.hdr_eotf_test_btn.config(state=tk.NORMAL) - self.app.log_gui.log("✓ HDR EOTF 单步调试已启用") + self.app.log_gui.log("HDR EOTF 单步调试已启用", level="success") elif test_item == "accuracy": self.hdr_color_combo.config(state="readonly") self.hdr_accuracy_test_btn.config(state=tk.NORMAL) - self.app.log_gui.log("✓ HDR 色准单步调试已启用") + self.app.log_gui.log("HDR 色准单步调试已启用", level="success") elif test_item == "rgb": self.hdr_rgb_combo.config(state="readonly") self.hdr_rgb_test_btn.config(state=tk.NORMAL) - self.app.log_gui.log("✓ HDR RGB 单步调试已启用") + self.app.log_gui.log("HDR RGB 单步调试已启用", level="success") def disable_all_debug(self): """禁用所有单步调试(新测试开始时调用)""" @@ -759,11 +759,11 @@ class PQDebugPanel: def _run_single_step_thread(self, test_type, test_item, selected): """单步测试线程""" try: - self.app.log_gui.log("=" * 50) + self.app.log_gui.log("=" * 50, level="info") self.app.log_gui.log( f"开始单步调试: {test_type} - {test_item} - {selected}" - ) - self.app.log_gui.log("=" * 50) + , level="info") + self.app.log_gui.log("=" * 50, level="info") # 禁用按钮 self._disable_test_button(test_type, test_item) @@ -796,9 +796,9 @@ class PQDebugPanel: x, y, lv, X, Y, Z = self.app.ca.readAllDisplay() self.app.log_gui.log( - f"✓ 测量完成: x={x:.4f}, y={y:.4f}, lv={lv:.2f}, " + f"测量完成: x={x:.4f}, y={y:.4f}, lv={lv:.2f}, " f"X={X:.4f}, Y={Y:.4f}, Z={Z:.4f}" - ) + , level="success") # 对比数据 self._compare_and_display( @@ -809,10 +809,10 @@ class PQDebugPanel: self._enable_test_button(test_type, test_item) except Exception as e: - self.app.log_gui.log(f"❌ 单步测试失败: {str(e)}") + self.app.log_gui.log(f"[Error] 单步测试失败: {str(e)}", level="error") import traceback - self.app.log_gui.log(traceback.format_exc()) + self.app.log_gui.log(traceback.format_exc(), level="error") self._enable_test_button(test_type, test_item) def _setup_signal_format(self, test_type): @@ -842,7 +842,7 @@ class PQDebugPanel: # 获取原始数据 key = f"{test_type}_{test_item}" if key not in self.original_data: - self.app.log_gui.log("⚠️ 未找到原始测试数据") + self.app.log_gui.log("未找到原始测试数据", level="error") return original_data_list = self.original_data[key] @@ -856,12 +856,12 @@ class PQDebugPanel: index = self.get_rgb_index(selected) if index >= len(original_data_list): - self.app.log_gui.log(f"⚠️ 索引超出范围: {index}") + self.app.log_gui.log(f"索引超出范围: {index}", level="error") return original_data = original_data_list[index] - # ==================== ✅ 构建对比数据 ==================== + # ==================== 构建对比数据 ==================== comparison = {} if test_item == "gamma": @@ -1140,57 +1140,57 @@ class PQDebugPanel: def _get_delta_e_from_results(self, test_type, color_name): """从原始测试结果中读取 ΔE 值""" try: - self.app.log_gui.log(f"[读取 ΔE] 开始:") - self.app.log_gui.log(f" test_type = {test_type}") - self.app.log_gui.log(f" color_name = {color_name}") + self.app.log_gui.log(f"[读取 ΔE] 开始:", level="info") + self.app.log_gui.log(f" test_type = {test_type}", level="info") + self.app.log_gui.log(f" color_name = {color_name}", level="info") - # ✅ 正确的访问方式:通过 test_items + # 正确的访问方式:通过 test_items if "accuracy" not in self.app.results.test_items: - self.app.log_gui.log(" ✗ 未找到 accuracy 测试项") + self.app.log_gui.log("[Error] 未找到 accuracy 测试项", level="error") return 0.0 test_item = self.app.results.test_items["accuracy"] accuracy_result = test_item.final_result - self.app.log_gui.log(f" accuracy_result = {accuracy_result is not None}") + self.app.log_gui.log(f" accuracy_result = {accuracy_result is not None}", level="info") if not accuracy_result: - self.app.log_gui.log(" ✗ accuracy_result 为空") + self.app.log_gui.log("[Error] accuracy_result 为空", level="error") return 0.0 # 获取色块名称列表和 ΔE 值列表 color_patches = accuracy_result.get("color_patches", []) delta_e_values = accuracy_result.get("delta_e_values", []) - self.app.log_gui.log(f" color_patches 数量: {len(color_patches)}") - self.app.log_gui.log(f" delta_e_values 数量: {len(delta_e_values)}") + self.app.log_gui.log(f" color_patches 数量: {len(color_patches)}", level="info") + self.app.log_gui.log(f" delta_e_values 数量: {len(delta_e_values)}", level="info") if color_patches: - self.app.log_gui.log(f" 前3个色块: {color_patches[:3]}") + self.app.log_gui.log(f" 前3个色块: {color_patches[:3]}", level="info") if delta_e_values: - self.app.log_gui.log(f" 前3个ΔE: {delta_e_values[:3]}") + self.app.log_gui.log(f" 前3个ΔE: {delta_e_values[:3]}", level="info") # 查找对应色块的索引 try: index = color_patches.index(color_name) delta_e = delta_e_values[index] self.app.log_gui.log( - f" ✓ 找到 {color_name}: index={index}, ΔE={delta_e:.2f}" - ) + f" 找到 {color_name}: index={index}, ΔE={delta_e:.2f}" + , level="success") return delta_e except ValueError: - self.app.log_gui.log(f" ✗ 未找到色块 '{color_name}'") - self.app.log_gui.log(f" 可用色块: {color_patches}") + self.app.log_gui.log(f"[Error] 未找到色块 '{color_name}'", level="error") + self.app.log_gui.log(f" 可用色块: {color_patches}", level="info") return 0.0 except IndexError: - self.app.log_gui.log(f" ✗ 索引超出范围: {index}/{len(delta_e_values)}") + self.app.log_gui.log(f"[Error] 索引超出范围: {index}/{len(delta_e_values)}", level="error") return 0.0 except Exception as e: - self.app.log_gui.log(f"⚠️ 读取 ΔE 失败: {str(e)}") + self.app.log_gui.log(f"读取 ΔE 失败: {str(e)}", level="error") import traceback - self.app.log_gui.log(traceback.format_exc()) + self.app.log_gui.log(traceback.format_exc(), level="error") return 0.0 def _calculate_delta_e_for_color( @@ -1215,5 +1215,5 @@ class PQDebugPanel: return delta_e except Exception as e: - self.app.log_gui.log(f"⚠️ 计算 ΔE 失败: {str(e)}") + self.app.log_gui.log(f"计算 ΔE 失败: {str(e)}", level="error") return 0.0 diff --git a/app/views/pq_log_gui.py b/app/views/pq_log_gui.py index 4f2c1fd..17e10a6 100644 --- a/app/views/pq_log_gui.py +++ b/app/views/pq_log_gui.py @@ -1,31 +1,160 @@ +import threading +from datetime import datetime import tkinter as tk import ttkbootstrap as ttk class PQLogGUI(ttk.Frame): + VALID_LEVELS = {"info", "success", "warning", "error", "debug", "separator", "blank"} + def __init__(self, parent): super().__init__(parent) + self._line_count = 0 + self._max_lines = 1500 self.create_widgets() def create_widgets(self): log_frame = ttk.LabelFrame(self, text="测试日志") log_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5) - self.log_text = ttk.Text(log_frame, height=8, width=50) + toolbar = ttk.Frame(log_frame) + toolbar.pack(fill=tk.X, padx=6, pady=(6, 2)) + + self.log_summary_var = tk.StringVar(value="0 条日志") + ttk.Label( + toolbar, + textvariable=self.log_summary_var, + bootstyle="secondary", + ).pack(side=tk.LEFT) + + ttk.Button( + toolbar, + text="清空日志", + command=self.clear_log, + bootstyle="secondary-outline", + width=10, + ).pack(side=tk.RIGHT) + + text_container = ttk.Frame(log_frame) + text_container.pack(fill=tk.BOTH, expand=True, padx=6, pady=(0, 6)) + + self.log_text = tk.Text( + text_container, + height=10, + width=50, + wrap=tk.WORD, + font=("Consolas", 10), + bg="#fbfcfe", + fg="#1f2937", + relief=tk.FLAT, + bd=0, + padx=10, + pady=8, + spacing1=2, + spacing3=2, + insertbackground="#1f2937", + ) self.log_text.pack(fill=tk.BOTH, expand=True, side=tk.LEFT) - - log_scrollbar = ttk.Scrollbar(log_frame, command=self.log_text.yview) + + log_scrollbar = ttk.Scrollbar(text_container, command=self.log_text.yview) log_scrollbar.pack(fill=tk.Y, side=tk.RIGHT) self.log_text.config(yscrollcommand=log_scrollbar.set) - + + self._configure_tags() self.log_text.config(state=tk.DISABLED) - def log(self, message): + def log(self, message, level="info"): + if threading.current_thread() is not threading.main_thread(): + self.after(0, self.log, message, level) + return + + text = "" if message is None else str(message) + normalized_level = self._normalize_level(level, text) self.log_text.config(state=tk.NORMAL) - self.log_text.insert(tk.END, message + "\n") + self._append_message(text, normalized_level) self.log_text.see(tk.END) self.log_text.config(state=tk.DISABLED) def clear_log(self): + if threading.current_thread() is not threading.main_thread(): + self.after(0, self.clear_log) + return + self.log_text.config(state=tk.NORMAL) self.log_text.delete(1.0, tk.END) - self.log_text.config(state=tk.DISABLED) \ No newline at end of file + self.log_text.config(state=tk.DISABLED) + self._line_count = 0 + self._update_summary() + + def _configure_tags(self): + self.log_text.tag_configure("timestamp", foreground="#6b7280") + self.log_text.tag_configure("level_info", foreground="#2563eb") + self.log_text.tag_configure("level_success", foreground="#0f766e") + self.log_text.tag_configure("level_warning", foreground="#b45309") + self.log_text.tag_configure("level_error", foreground="#b91c1c") + self.log_text.tag_configure("level_debug", foreground="#7c3aed") + self.log_text.tag_configure("message", foreground="#1f2937") + self.log_text.tag_configure("message_success", foreground="#0f766e") + self.log_text.tag_configure("message_warning", foreground="#b45309") + self.log_text.tag_configure("message_error", foreground="#991b1b") + self.log_text.tag_configure("message_debug", foreground="#6d28d9") + self.log_text.tag_configure("separator", foreground="#94a3b8") + self.log_text.tag_configure("traceback", foreground="#7f1d1d") + self.log_text.tag_configure("blank", spacing1=4, spacing3=4) + + def _append_message(self, message, level): + lines = message.splitlines() or [""] + for line in lines: + self._append_line(line, level) + self._trim_excess_lines() + self._update_summary() + + def _append_line(self, line, level): + timestamp = datetime.now().strftime("%H:%M:%S") + rendered = "" if line is None else str(line).strip() + + if level == "blank" or not rendered: + self.log_text.insert(tk.END, "\n", ("blank",)) + self._line_count += 1 + return + + if level == "separator": + self.log_text.insert(tk.END, f"[{timestamp}] ", ("timestamp",)) + self.log_text.insert(tk.END, "[SECTION] ", ("level_info",)) + self.log_text.insert(tk.END, rendered + "\n", ("separator",)) + self._line_count += 1 + return + + level_tag = f"level_{level}" + level_label = level.upper().ljust(7) + if level == "error" and rendered.startswith("Traceback"): + message_tag = "traceback" + elif level in {"success", "warning", "error", "debug"}: + message_tag = f"message_{level}" + else: + message_tag = "message" + + self.log_text.insert(tk.END, f"[{timestamp}] ", ("timestamp",)) + self.log_text.insert(tk.END, f"[{level_label}] ", (level_tag,)) + self.log_text.insert(tk.END, rendered + "\n", (message_tag,)) + self._line_count += 1 + + def _normalize_level(self, level, message): + normalized = "info" if level is None else str(level).strip().lower() + if normalized not in self.VALID_LEVELS: + normalized = "info" + + if normalized == "info" and (message is None or str(message).strip() == ""): + return "blank" + + return normalized + + def _trim_excess_lines(self): + overflow = self._line_count - self._max_lines + if overflow <= 0: + return + + self.log_text.delete("1.0", f"{overflow + 1}.0") + self._line_count = self._max_lines + + def _update_summary(self): + self.log_summary_var.set(f"{self._line_count} 条日志") \ No newline at end of file diff --git a/pqAutomationApp.py b/pqAutomationApp.py index eaa5a30..338828c 100644 --- a/pqAutomationApp.py +++ b/pqAutomationApp.py @@ -1,4 +1,4 @@ -import ttkbootstrap as ttk +import ttkbootstrap as ttk import tkinter as tk from tkinter import messagebox, filedialog import sys @@ -246,7 +246,7 @@ class PQAutomationApp: self.change_test_type("screen_module") except Exception as e: if hasattr(self, "log_gui"): - self.log_gui.log(f"初始化默认测试类型失败: {str(e)}") + self.log_gui.log(f"初始化默认测试类型失败: {str(e)}", level="error") get_config_path = _cfg_get_config_path load_pq_config = _cfg_load_pq_config @@ -360,7 +360,7 @@ class PQAutomationApp: if not swallow_errors: raise if hasattr(self, "log_gui"): - self.log_gui.log(f"保存参数失败: {str(e)}") + self.log_gui.log(f"保存参数失败: {str(e)}", level="error") def _save_cct_params_before_test_type_switch(self): """切换测试类型前,按当前类型保存色度参数。""" @@ -376,10 +376,10 @@ class PQAutomationApp: if combo is not None: combo.configure(state=state) if success_msg and hasattr(self, "log_gui"): - self.log_gui.log(success_msg) + self.log_gui.log(success_msg, level="success") except Exception as e: if hasattr(self, "log_gui"): - self.log_gui.log(f"{error_prefix}: {str(e)}") + self.log_gui.log(f"{error_prefix}: {str(e)}", level="error") def _hide_recalc_buttons(self, include_gamut=False): """隐藏重新计算按钮。include_gamut=True 时同时隐藏色域重算按钮。""" @@ -403,15 +403,15 @@ class PQAutomationApp: if hasattr(self, "debug_panel"): try: self.debug_panel.disable_all_debug() - self.log_gui.log("✓ 单步调试已禁用") + self.log_gui.log("单步调试已禁用", level="success") except Exception as e: - self.log_gui.log(f"[Error] 禁用单步调试失败: {str(e)}") + self.log_gui.log(f"[Error] 禁用单步调试失败: {str(e)}", level="error") if hasattr(self, "debug_container"): try: self.debug_container.pack_forget() - self.log_gui.log("✓ 单步调试面板已隐藏") + self.log_gui.log("单步调试面板已隐藏", level="success") except Exception as e: - self.log_gui.log(f"[Error] 隐藏调试面板失败: {str(e)}") + self.log_gui.log(f"[Error] 隐藏调试面板失败: {str(e)}", level="error") def _set_config_panel_btn_state(self, state): """统一设置配置面板按钮状态(disabled/normal)。""" @@ -428,13 +428,13 @@ class PQAutomationApp: if hasattr(self, "config") and hasattr(self.config, "set_current_test_type"): success = self.config.set_current_test_type(test_type) if not success and hasattr(self, "log_gui"): - self.log_gui.log(f"切换测试类型失败: {test_type}") + self.log_gui.log(f"切换测试类型失败: {test_type}", level="error") def _switch_signal_format_tabs(self, test_type): """切换信号格式 Tab 到目标测试类型。""" if not hasattr(self, "signal_tabs"): if hasattr(self, "log_gui"): - self.log_gui.log("[Error] signal_tabs 尚未创建") + self.log_gui.log("[Error] signal_tabs 尚未创建", level="error") return try: @@ -465,10 +465,10 @@ class PQAutomationApp: if hasattr(self, "log_gui"): tab_names = ["屏模组测试", "SDR测试", "HDR"] - self.log_gui.log(f"✓ 已切换到 {tab_names[target_tab]} 信号格式") + self.log_gui.log(f"已切换到 {tab_names[target_tab]} 信号格式", level="success") except Exception as e: if hasattr(self, "log_gui"): - self.log_gui.log(f"切换信号格式失败: {str(e)}") + self.log_gui.log(f"切换信号格式失败: {str(e)}", level="error") def _switch_chart_tabs_by_test_type(self, test_type): """按测试类型切换 Gamma/EOTF 与客户模板结果 Tab。""" @@ -485,23 +485,23 @@ class PQAutomationApp: gamma_index = current_tabs.index(gamma_tab_id) self.chart_notebook.forget(gamma_index) if hasattr(self, "log_gui"): - self.log_gui.log("✓ 已隐藏 Gamma 曲线 Tab") + self.log_gui.log("已隐藏 Gamma 曲线 Tab", level="success") if eotf_tab_id not in current_tabs: self.chart_notebook.insert(1, self.eotf_chart_frame, text="EOTF 曲线") if hasattr(self, "log_gui"): - self.log_gui.log("✓ 已显示 EOTF 曲线 Tab") + self.log_gui.log("已显示 EOTF 曲线 Tab", level="success") else: if eotf_tab_id in current_tabs: eotf_index = current_tabs.index(eotf_tab_id) self.chart_notebook.forget(eotf_index) if hasattr(self, "log_gui"): - self.log_gui.log("✓ 已隐藏 EOTF 曲线 Tab") + self.log_gui.log("已隐藏 EOTF 曲线 Tab", level="success") if gamma_tab_id not in current_tabs: self.chart_notebook.insert(1, self.gamma_chart_frame, text="Gamma 曲线") if hasattr(self, "log_gui"): - self.log_gui.log("✓ 已显示 Gamma 曲线 Tab") + self.log_gui.log("已显示 Gamma 曲线 Tab", level="success") custom_tab_id = str(self.custom_template_tab_frame) current_tabs = list(self.chart_notebook.tabs()) @@ -510,17 +510,17 @@ class PQAutomationApp: if custom_tab_id not in current_tabs: self.chart_notebook.add(self.custom_template_tab_frame, text="客户模板结果显示") if hasattr(self, "log_gui"): - self.log_gui.log("✓ 已显示客户模板结果 Tab") + self.log_gui.log("已显示客户模板结果 Tab", level="success") else: if custom_tab_id in current_tabs: self.chart_notebook.forget(self.custom_template_tab_frame) if hasattr(self, "log_gui"): - self.log_gui.log("✓ 已隐藏客户模板结果 Tab") + self.log_gui.log("已隐藏客户模板结果 Tab", level="success") self.chart_notebook.update_idletasks() except Exception as e: if hasattr(self, "log_gui"): - self.log_gui.log(f"切换 Gamma/EOTF Tab 失败: {str(e)}") + self.log_gui.log(f"切换 Gamma/EOTF Tab 失败: {str(e)}", level="error") def change_test_type(self, test_type): """切换测试类型""" @@ -637,9 +637,9 @@ class PQAutomationApp: def _signal_stop_and_update_ui(self): """设置停止标志并立即更新 UI 以反馈给用户。""" self.testing = False - self.log_gui.log("=" * 50) - self.log_gui.log("正在停止测试...") - self.log_gui.log("=" * 50) + self.log_gui.log("=" * 50, level="separator") + self.log_gui.log("正在停止测试...", level="info") + self.log_gui.log("=" * 50, level="separator") self.stop_btn.config(state=tk.DISABLED) self.status_var.set("正在停止测试,请稍候...") self.root.update() @@ -648,24 +648,24 @@ class PQAutomationApp: """等待测试线程结束,最多 timeout_seconds 秒,同时保持 UI 响应。""" if not (self.test_thread and self.test_thread.is_alive()): return - self.log_gui.log("等待测试线程结束...") + self.log_gui.log("等待测试线程结束...", level="info") for _ in range(int(timeout_seconds * 10)): if not self.test_thread.is_alive(): break time.sleep(0.1) self.root.update() if self.test_thread.is_alive(): - self.log_gui.log("[Error] 测试线程未能正常结束,将在后台继续等待") + self.log_gui.log("[Error] 测试线程未能正常结束,将在后台继续等待", level="error") else: - self.log_gui.log("✓ 测试线程已结束") + self.log_gui.log("测试线程已结束", level="success") def _clear_test_data(self): """清空测试结果对象与中间数据缓存。""" try: - self.log_gui.log("清理测试数据...") + self.log_gui.log("清理测试数据...", level="info") if hasattr(self, "results"): self.results = None - self.log_gui.log(" ✓ 测试结果对象已清空") + self.log_gui.log(" 测试结果对象已清空", level="success") for attr in [ "gamut_results", "gamma_results", @@ -675,28 +675,28 @@ class PQAutomationApp: ]: if hasattr(self, attr): setattr(self, attr, None) - self.log_gui.log(" ✓ 所有中间数据已清空") + self.log_gui.log(" 所有中间数据已清空", level="success") except Exception as e: - self.log_gui.log(f"[Error] 清理数据时出错: {str(e)}") + self.log_gui.log(f"[Error] 清理数据时出错: {str(e)}", level="error") def _clear_charts_and_tables(self): """清空图表与客户模板结果表格,并跳转到色域图 Tab。""" try: self.clear_chart() - self.log_gui.log("✓ 图表已清空") + self.log_gui.log("图表已清空", level="success") except Exception as e: - self.log_gui.log(f"[Error] 清空图表时出错: {str(e)}") + self.log_gui.log(f"[Error] 清空图表时出错: {str(e)}", level="error") try: self.clear_custom_template_results() - self.log_gui.log("✓ 客户模板结果表格已清空") + self.log_gui.log("客户模板结果表格已清空", level="success") except Exception as e: - self.log_gui.log(f"[Error] 清空客户模板结果表格失败: {str(e)}") + self.log_gui.log(f"[Error] 清空客户模板结果表格失败: {str(e)}", level="error") try: if hasattr(self, "chart_notebook"): self.chart_notebook.select(self.gamut_chart_frame) self.root.update_idletasks() except Exception as e: - self.log_gui.log(f"[Error] 跳转到色域图失败: {str(e)}") + self.log_gui.log(f"[Error] 跳转到色域图失败: {str(e)}", level="error") def _restore_ui_after_stop(self): """恢复主按钮与状态栏到非测试态。""" @@ -718,16 +718,16 @@ class PQAutomationApp: self._restore_ui_after_stop() self._set_gamut_combos_state( "disabled", - success_msg="✓ 色域参考标准已禁用", + success_msg="色域参考标准已禁用", error_prefix="禁用色域参考标准失败", ) hidden = self._hide_recalc_buttons(include_gamut=True) if hidden > 0: - self.log_gui.log(f"✓ 已隐藏 {hidden} 个重新计算按钮") + self.log_gui.log(f"已隐藏 {hidden} 个重新计算按钮", level="success") self._disable_debug_panel() - self.log_gui.log("=" * 50) - self.log_gui.log("✓ 测试已停止,所有数据已清空") - self.log_gui.log("=" * 50) + self.log_gui.log("=" * 50, level="separator") + self.log_gui.log("测试已停止,所有数据已清空", level="success") + self.log_gui.log("=" * 50, level="separator") messagebox.showinfo( "测试已停止", "测试已停止,本次测试数据已清空。\n\n可以重新开始新的测试。", @@ -760,8 +760,8 @@ class PQAutomationApp: selected_items = self.get_selected_test_items() log = self.log_gui.log - log(f"保存测试类型: {current_test_type}") - log(f"已选测试项: {selected_items}") + log(f"保存测试类型: {current_test_type}", level="info") + log(f"已选测试项: {selected_items}", level="info") # 1) 图片 _save_result_images_impl( @@ -779,16 +779,16 @@ class PQAutomationApp: ) # 3) 成功提示 - log("=" * 50) - log(f"测试结果已保存到目录: {result_dir}") - log("=" * 50) + log("=" * 50, level="separator") + log(f"测试结果已保存到目录: {result_dir}", level="success") + log("=" * 50, level="separator") messagebox.showinfo("成功", f"测试结果已保存到目录:\n{result_dir}") except Exception as e: - self.log_gui.log(f"[Error] 保存测试结果失败: {str(e)}") + self.log_gui.log(f"[Error] 保存测试结果失败: {str(e)}", level="error") import traceback - self.log_gui.log(traceback.format_exc()) + self.log_gui.log(traceback.format_exc(), level="error") messagebox.showerror("错误", f"保存测试结果失败: {str(e)}") new_pq_results = _run_new_pq_results @@ -862,7 +862,7 @@ class PQAutomationApp: self.save_pq_config() except Exception as e: - self.log_gui.log(f"[Error] 更新配置失败: {str(e)}") + self.log_gui.log(f"[Error] 更新配置失败: {str(e)}", level="error") def update_config_and_tabs(self): """更新配置并同步Tab状态""" @@ -879,7 +879,7 @@ class PQAutomationApp: def on_closing(self): """窗口关闭时的处理""" try: - # ✅ 检查是否清理了配置 + # 检查是否清理了配置 if not self.config_cleared: # 保存配置 self.save_pq_config()