import os
import re
import time
import logging
import tkinter as tk
from tkinter import ttk, messagebox, filedialog, scrolledtext
from collections import Counter
import openpyxl
import datetime
增强的日志配置
def setup_logger():
“”“配置并返回日志记录器”“”
logger = logging.getLogger(‘SCLMultiProcessor’)
logger.setLevel(logging.DEBUG)
# 创建文件处理器 log_file = 'scl_processor.log' file_handler = logging.FileHandler(log_file, encoding='utf-8') file_handler.setLevel(logging.DEBUG) # 创建控制台处理器 console_handler = logging.StreamHandler() console_handler.setLevel(logging.INFO) # 创建格式化器 formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S' ) # 应用格式化器 file_handler.setFormatter(formatter) console_handler.setFormatter(formatter) # 添加处理器 logger.addHandler(file_handler) logger.addHandler(console_handler) # 记录启动信息 logger.info("=" * 50) logger.info(f"SCL Multi-Processor 启动于 {datetime.datetime.now()}") logger.info("=" * 50) return logger
获取日志记录器
logger = setup_logger()
class ExcelColorDetector:
“”“Excel单元格颜色检测器”“”
def init(self):
self.NO_FILL = “无填充”
def is_no_fill(self, cell): """检查单元格是否无填充颜色""" try: # 检查是否存在填充属性 if not hasattr(cell, 'fill') or cell.fill is None: return True # 检查填充类型 if cell.fill.patternType is None or cell.fill.patternType == 'none': return True # 检查背景色是否为默认(白色或无) if hasattr(cell.fill, 'bgColor') and cell.fill.bgColor.rgb == '00000000': return True return False except Exception as e: logger.error(f"颜色检测错误: {str(e)}") return True
class ColumnFinder:
“”“列查找器,支持任意位置查找列名”“”
def init(self, sheet, max_search_rows=100):
self.sheet = sheet
self.max_search_rows = max_search_rows
self.column_positions = {}
logger.debug(f"列查找器初始化: 最大搜索行数={max_search_rows}")
def find_column(self, column_name): """查找指定列名在表格中的位置""" logger.debug(f"查找列: {column_name}") if column_name in self.column_positions: pos = self.column_positions[column_name] logger.debug(f"从缓存中找到列: {column_name} -> {pos}") return pos # 在指定行数范围内查找 max_row = min(self.sheet.max_row, self.max_search_rows) logger.debug(f"搜索范围: 1-{max_row}行") for row_idx in range(1, max_row + 1): for col_idx in range(1, self.sheet.max_column + 1): cell = self.sheet.cell(row=row_idx, column=col_idx) if cell.value and str(cell.value).strip() == column_name: pos = (row_idx, col_idx) self.column_positions[column_name] = pos logger.info(f"找到列 '{column_name}' 位置: 行={row_idx}, 列={col_idx}") return pos logger.warning(f"未找到列: {column_name}") return None
在SCL处理器中使用高级列查找器
class SCLMultiProcessor:
def init(self, root):
self.root = root
self.root.title(“SCL文件处理系统 - 多条件统计版”)
self.root.geometry(“1000x700”)
# 初始化变量 self.input_file = None self.color_detector = ExcelColorDetector() self.progress_var = tk.DoubleVar() # 列映射表 self.target_columns = { "diff_no_fill": 16, # P列 "diff_fill": 23, # W列 "diff_add_no_fill": 27, # AA列 "diff_add_fill": 30, # AD列 "diff_change_no_fill": 34, # AH列 "diff_change_fill": 37, # AK列 "diff_delete_no_fill": 42, # AP列 "diff_delete_fill": 45, # AS列 "valid_yes_no_fill": 50, # AX列 "valid_yes_fill": 53, # BA列 "valid_no_no_fill": 57, # BE列 "valid_no_fill": 60, # BH列 "valid_yes_reason_no_fill": 62, # BL列 "valid_yes_reason_fill": 65, # BO列 "valid_no_reason_no_fill": 71, # BS列 "valid_no_reason_fill": 74, # BV列 "background_no_fill": 78, # BZ列 "background_fill": 85 # CG列 } # 创建主框架 self.main_frame = ttk.Frame(root, padding="10") self.main_frame.pack(fill=tk.BOTH, expand=True) # 创建UI self.create_ui() # 记录UI初始化完成 logger.info("用户界面初始化完成") def create_ui(self): """创建用户界面""" # 文件选择区域 file_frame = ttk.LabelFrame(self.main_frame, text="文件选择", padding="10") file_frame.pack(fill=tk.X, pady=5) # 输入文件选择 input_frame = ttk.Frame(file_frame) input_frame.pack(fill=tk.X, pady=5) ttk.Label(input_frame, text="输入文件:").pack(side=tk.LEFT, padx=5) self.input_path_var = tk.StringVar() input_entry = ttk.Entry(input_frame, textvariable=self.input_path_var, width=70) input_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) ttk.Button(input_frame, text="浏览...", command=self.browse_input_file).pack(side=tk.LEFT, padx=5) # 配置区域 config_frame = ttk.LabelFrame(self.main_frame, text="处理配置", padding="10") config_frame.pack(fill=tk.X, pady=5) # 列名配置 col_frame = ttk.Frame(config_frame) col_frame.pack(fill=tk.X, pady=5) ttk.Label(col_frame, text="差分列名:").grid(row=0, column=0, padx=5, sticky=tk.W) self.diff_col_var = tk.StringVar(value="差分種別") ttk.Entry(col_frame, textvariable=self.diff_col_var, width=15).grid(row=0, column=1, padx=5, sticky=tk.W) ttk.Label(col_frame, text="备注列名:").grid(row=0, column=2, padx=5, sticky=tk.W) self.note_col_var = tk.StringVar(value="备注") ttk.Entry(col_frame, textvariable=self.note_col_var, width=15).grid(row=0, column=3, padx=5, sticky=tk.W) ttk.Label(col_frame, text="判断列名:").grid(row=1, column=0, padx=5, sticky=tk.W) self.valid_col_var = tk.StringVar(value="差分の判断有意/無効") ttk.Entry(col_frame, textvariable=self.valid_col_var, width=15).grid(row=1, column=1, padx=5, sticky=tk.W) ttk.Label(col_frame, text="理由列名:").grid(row=1, column=2, padx=5, sticky=tk.W) self.reason_col_var = tk.StringVar(value="判断理由") ttk.Entry(col_frame, textvariable=self.reason_col_var, width=15).grid(row=1, column=3, padx=5, sticky=tk.W) ttk.Label(col_frame, text="背景列名:").grid(row=1, column=4, padx=5, sticky=tk.W) self.background_col_var = tk.StringVar(value="変更背景") ttk.Entry(col_frame, textvariable=self.background_col_var, width=15).grid(row=1, column=5, padx=5, sticky=tk.W) # 搜索选项 search_frame = ttk.Frame(config_frame) search_frame.pack(fill=tk.X, pady=5) ttk.Label(search_frame, text="最大搜索行数:").grid(row=0, column=0, padx=5, sticky=tk.W) self.max_search_var = tk.IntVar(value=100) ttk.Entry(search_frame, textvariable=self.max_search_var, width=5).grid(row=0, column=1, padx=5, sticky=tk.W) ttk.Label(search_frame, text="文件前缀:").grid(row=0, column=2, padx=5, sticky=tk.W) self.prefix_var = tk.StringVar(value="SCL_") ttk.Entry(search_frame, textvariable=self.prefix_var, width=10).grid(row=0, column=3, padx=5, sticky=tk.W) # 日志选项 log_frame = ttk.Frame(config_frame) log_frame.pack(fill=tk.X, pady=5) ttk.Label(log_frame, text="日志级别:").grid(row=0, column=0, padx=5, sticky=tk.W) self.log_level_var = tk.StringVar(value="INFO") log_level_combo = ttk.Combobox( log_frame, textvariable=self.log_level_var, width=10, state="readonly" ) log_level_combo['values'] = ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL') log_level_combo.grid(row=0, column=1, padx=5, sticky=tk.W) log_level_combo.bind("<<ComboboxSelected>>", self.change_log_level) # 处理按钮 btn_frame = ttk.Frame(self.main_frame) btn_frame.pack(fill=tk.X, pady=10) ttk.Button(btn_frame, text="开始处理", command=self.process_file).pack(side=tk.LEFT, padx=5) ttk.Button(btn_frame, text="查看日志", command=self.view_log).pack(side=tk.LEFT, padx=5) ttk.Button(btn_frame, text="导出配置", command=self.export_config).pack(side=tk.LEFT, padx=5) ttk.Button(btn_frame, text="加载配置", command=self.load_config).pack(side=tk.LEFT, padx=5) ttk.Button(btn_frame, text="清空日志", command=self.clear_log).pack(side=tk.LEFT, padx=5) # 进度条 progress_frame = ttk.Frame(self.main_frame) progress_frame.pack(fill=tk.X, pady=5) ttk.Label(progress_frame, text="处理进度:").pack(side=tk.LEFT, padx=5) self.progress_bar = ttk.Progressbar( progress_frame, variable=self.progress_var, maximum=100, length=700 ) self.progress_bar.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5) self.progress_label = ttk.Label(progress_frame, text="0%") self.progress_label.pack(side=tk.LEFT, padx=5) # 结果展示区域 result_frame = ttk.LabelFrame(self.main_frame, text="处理结果", padding="10") result_frame.pack(fill=tk.BOTH, expand=True, pady=5) # 结果文本框 self.result_text = scrolledtext.ScrolledText( result_frame, wrap=tk.WORD, height=20 ) self.result_text.pack(fill=tk.BOTH, expand=True) self.result_text.config(state=tk.DISABLED) # 状态栏 self.status_var = tk.StringVar(value="就绪") status_bar = ttk.Label(self.main_frame, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W) status_bar.pack(fill=tk.X, pady=5) logger.info("UI创建完成") def change_log_level(self, event=None): """更改日志级别""" level = self.log_level_var.get() logger.setLevel(getattr(logging, level)) logger.info(f"日志级别已更改为: {level}") def clear_log(self): """清空日志文件""" try: with open('scl_processor.log', 'w', encoding='utf-8') as log_file: log_file.write("") logger.info("日志文件已清空") messagebox.showinfo("成功", "日志文件已清空") except Exception as e: logger.error(f"清空日志失败: {str(e)}") messagebox.showerror("错误", f"清空日志失败: {str(e)}") def browse_input_file(self): """浏览输入文件""" file_path = filedialog.askopenfilename( filetypes=[ ("Excel 文件", "*.xlsx *.xls"), ("所有文件", "*.*") ] ) if file_path: self.input_path_var.set(file_path) self.input_file = file_path logger.info(f"已选择输入文件: {file_path}") def process_file(self): """处理文件""" if not self.input_file: messagebox.showwarning("警告", "请先选择输入文件") logger.warning("未选择输入文件") return try: # 重置结果 self.result_text.config(state=tk.NORMAL) self.result_text.delete(1.0, tk.END) self.result_text.insert(tk.END, "开始处理...\n") self.result_text.see(tk.END) self.result_text.config(state=tk.DISABLED) self.status_var.set("开始处理文件...") self.root.update() # 获取输入文件目录 input_dir = os.path.dirname(self.input_file) logger.info(f"开始处理文件: {self.input_file}") logger.info(f"文件目录: {input_dir}") # 使用openpyxl加载工作簿(保留格式) wb = openpyxl.load_workbook(self.input_file) sheet = wb.active logger.info(f"工作簿加载成功, 工作表: {sheet.title}") # 获取配置参数 diff_col_name = self.diff_col_var.get() note_col_name = self.note_col_var.get() valid_col_name = self.valid_col_var.get() reason_col_name = self.reason_col_var.get() background_col_name = self.background_col_var.get() prefix = self.prefix_var.get() max_search_rows = self.max_search_var.get() logger.info(f"配置参数: 差分列名={diff_col_name}, 备注列名={note_col_name}") logger.info(f"判断列名={valid_col_name}, 理由列名={reason_col_name}, 背景列名={background_col_name}") # 扫描E列(第5列) total_rows = sheet.max_row processed_count = 0 found_files = 0 logger.info(f"开始扫描E列, 总行数: {total_rows}") start_time = time.time() for row_idx in range(1, total_rows + 1): # 更新进度 progress = (row_idx / total_rows) * 100 self.progress_var.set(progress) self.progress_label.config(text=f"{progress:.1f}%") self.root.update() cell = sheet.cell(row=row_idx, column=5) cell_value = str(cell.value) if cell.value else "" # 检查是否包含前缀的文件名 if prefix in cell_value: # 提取文件名(可能有多个以空格分隔) file_names = re.findall(fr'{prefix}[^\s]+', cell_value) logger.info(f"行 {row_idx}: 找到文件: {', '.join(file_names)}") result_lines = [] for file_name in file_names: file_path = os.path.join(input_dir, file_name) # 检查文件是否存在 if not os.path.exists(file_path): result_lines.append(f"{file_name}: 文件不存在") logger.warning(f"文件不存在: {file_path}") continue # 处理SCL文件 results = self.process_scl_file( file_path, diff_col_name, note_col_name, valid_col_name, reason_col_name, background_col_name, max_search_rows ) # 将结果写入主Excel文件的不同列 for rule_name, result_str in results.items(): target_col = self.target_columns.get(rule_name) if target_col: target_cell = sheet.cell(row=row_idx, column=target_col) target_cell.value = result_str # 添加到结果列表 result_lines.append(f"{file_name}: 处理完成") found_files += 1 # 更新结果文本框 self.result_text.config(state=tk.NORMAL) self.result_text.insert( tk.END, f"行 {row_idx} 处理结果:\n" + "\n".join(result_lines) + "\n\n" ) self.result_text.see(tk.END) self.result_text.config(state=tk.DISABLED) processed_count += 1 # 保存修改后的Excel文件 output_path = self.input_file.replace(".xlsx", "_processed.xlsx") wb.save(output_path) logger.info(f"结果已保存到: {output_path}") elapsed_time = time.time() - start_time self.status_var.set(f"处理完成! 找到 {found_files} 个文件, 耗时 {elapsed_time:.2f} 秒") logger.info(f"处理完成! 找到 {found_files} 个文件, 耗时 {elapsed_time:.2f} 秒") # 更新结果文本框 self.result_text.config(state=tk.NORMAL) self.result_text.insert( tk.END, f"\n处理完成! 找到 {found_files} 个文件, 耗时 {elapsed_time:.2f} 秒\n" f"结果已保存到: {output_path}\n" ) self.result_text.see(tk.END) self.result_text.config(state=tk.DISABLED) messagebox.showinfo("完成", f"处理完成! 找到 {found_files} 个文件\n结果已保存到: {output_path}") except Exception as e: error_msg = f"处理文件时出错: {str(e)}" logger.exception(f"处理文件时出错: {str(e)}") messagebox.showerror("错误", error_msg) self.status_var.set(f"错误: {str(e)}") # 更新结果文本框 self.result_text.config(state=tk.NORMAL) self.result_text.insert(tk.END, f"\n错误: {error_msg}\n") self.result_text.see(tk.END) self.result_text.config(state=tk.DISABLED) def process_scl_file(self, file_path, diff_col_name, note_col_name, valid_col_name, reason_col_name, background_col_name, max_search_rows): """处理单个SCL文件并返回所有统计结果""" results = {} try: logger.info(f"开始处理SCL文件: {file_path}") # 加载SCL文件 scl_wb = openpyxl.load_workbook(file_path) scl_sheet = scl_wb.active logger.info(f"工作表加载成功: {scl_sheet.title}, 总行数: {scl_sheet.max_row}") # 创建列查找器 column_finder = AdvancedColumnFinder( scl_sheet, max_search_rows=max_search_rows, fuzzy_match=True # 启用模糊匹配 ) # 查找所有需要的列 diff_col_pos = column_finder.find_column(diff_col_name) note_col_pos = column_finder.find_column(note_col_name) valid_col_pos = column_finder.find_column(valid_col_name) reason_col_pos = column_finder.find_column(reason_col_name) background_col_pos = column_finder.find_column(background_col_name) # 如果找到多行列,则使用起始行作为列头行 if valid_col_pos: valid_header_row, valid_col_idx = valid_col_pos logger.info(f"找到多行列 '{valid_col_name}' 在列 {valid_col_idx}") else: logger.error(f"未找到列: {valid_col_name}") valid_col_idx = None # 获取列索引 diff_col_idx = diff_col_pos[1] if diff_col_pos else None note_col_idx = note_col_pos[1] if note_col_pos else None valid_col_idx = valid_col_pos[1] if valid_col_pos else None reason_col_idx = reason_col_pos[1] if reason_col_pos else None background_col_idx = background_col_pos[1] if background_col_pos else None # 记录列位置 logger.info(f"列位置: 差分={diff_col_idx}, 备注={note_col_idx}, 判断={valid_col_idx}, 理由={reason_col_idx}, 背景={background_col_idx}") # 初始化统计结果 stats = { "diff_no_fill": [], # 规则1 "diff_fill": [], # 规则2 "diff_add_no_fill": [], # 规则3 "diff_add_fill": [], # 规则4 "diff_change_no_fill": [], # 规则5 "diff_change_fill": [], # 规则6 "diff_delete_no_fill": [], # 规则7 "diff_delete_fill": [], # 规则8 "valid_yes_no_fill": [], # 规则9 "valid_yes_fill": [], # 规则10 "valid_no_no_fill": [], # 规则11 "valid_no_fill": [], # 规则12 "valid_yes_reason_no_fill": [], # 规则13 "valid_yes_reason_fill": [], # 规则14 "valid_no_reason_no_fill": [], # 规则15 "valid_no_reason_fill": [], # 规则16 "background_no_fill": [], # 规则17 "background_fill": [] # 规则18 } # 遍历所有行 start_row = max( diff_col_pos[0] if diff_col_pos else 1, note_col_pos[0] if note_col_pos else 1, valid_col_pos[0] if valid_col_pos else 1, reason_col_pos[0] if reason_col_pos else 1, background_col_pos[0] if background_col_pos else 1 ) + 1 logger.info(f"从第 {start_row} 行开始处理数据") for row_idx in range(start_row, scl_sheet.max_row + 1): # 获取所有需要的单元格 diff_cell = scl_sheet.cell(row_idx, diff_col_idx) if diff_col_idx else None note_cell = scl_sheet.cell(row_idx, note_col_idx) if note_col_idx else None valid_cell = scl_sheet.cell(row_idx, valid_col_idx) if valid_col_idx else None reason_cell = scl_sheet.cell(row_idx, reason_col_idx) if reason_col_idx else None background_cell = scl_sheet.cell(row_idx, background_col_idx) if background_col_idx else None # 获取备注值 note_value = str(note_cell.value).strip() if note_cell and note_cell.value else None # 规则1: 差分种别无颜色填充 if diff_cell and self.color_detector.is_no_fill(diff_cell) and note_value: stats["diff_no_fill"].append(note_value) # 规则2: 差分种别有颜色填充 if diff_cell and not self.color_detector.is_no_fill(diff_cell) and note_value: stats["diff_fill"].append(note_value) # 规则3: 差分种别="追加"且无颜色填充 if (diff_cell and diff_cell.value == "追加" and self.color_detector.is_no_fill(diff_cell) and note_value): stats["diff_add_no_fill"].append(note_value) # 规则4: 差分种别="追加"且有颜色填充 if (diff_cell and diff_cell.value == "追加" and not self.color_detector.is_no_fill(diff_cell) and note_value): stats["diff_add_fill"].append(note_value) # 规则5: 差分种别="変更"且无颜色填充 if (diff_cell and diff_cell.value == "変更" and self.color_detector.is_no_fill(diff_cell) and note_value): stats["diff_change_no_fill"].append(note_value) # 规则6: 差分种别="変更"且有颜色填充 if (diff_cell and diff_cell.value == "変更" and not self.color_detector.is_no_fill(diff_cell) and note_value): stats["diff_change_fill"].append(note_value) # 规则7: 差分种别="削除"且无颜色填充 if (diff_cell and diff_cell.value == "削除" and self.color_detector.is_no_fill(diff_cell) and note_value): stats["diff_delete_no_fill"].append(note_value) # 规则8: 差分种别="削除"且有颜色填充 if (diff_cell and diff_cell.value == "削除" and not self.color_detector.is_no_fill(diff_cell) and note_value): stats["diff_delete_fill"].append(note_value) # 规则9: 判断="有意"且无颜色填充 if (valid_cell and valid_cell.value == "有意" and self.color_detector.is_no_fill(valid_cell) and note_value): stats["valid_yes_no_fill"].append(note_value) # 规则10: 判断="有意"且有颜色填充 if (valid_cell and valid_cell.value == "有意" and not self.color_detector.is_no_fill(valid_cell) and note_value): stats["valid_yes_fill"].append(note_value) # 规则11: 判断="無効"且无颜色填充 if (valid_cell and valid_cell.value == "無効" and self.color_detector.is_no_fill(valid_cell) and note_value): stats["valid_no_no_fill"].append(note_value) # 规则12: 判断="無効"且有颜色填充 if (valid_cell and valid_cell.value == "無効" and not self.color_detector.is_no_fill(valid_cell) and note_value): stats["valid_no_fill"].append(note_value) # 规则13: 判断="有意"且理由无颜色填充 if (valid_cell and valid_cell.value == "有意" and reason_cell and self.color_detector.is_no_fill(reason_cell) and note_value): stats["valid_yes_reason_no_fill"].append(note_value) # 规则14: 判断="有意"且理由有颜色填充 if (valid_cell and valid_cell.value == "有意" and reason_cell and not self.color_detector.is_no_fill(reason_cell) and note_value): stats["valid_yes_reason_fill"].append(note_value) # 规则15: 判断="無効"且理由无颜色填充 if (valid_cell and valid_cell.value == "無効" and reason_cell and self.color_detector.is_no_fill(reason_cell) and note_value): stats["valid_no_reason_no_fill"].append(note_value) # 规则16: 判断="無効"且理由有颜色填充 if (valid_cell and valid_cell.value == "無効" and reason_cell and not self.color_detector.is_no_fill(reason_cell) and note_value): stats["valid_no_reason_fill"].append(note_value) # 规则17: 背景无颜色填充 if background_cell and self.color_detector.is_no_fill(background_cell) and note_value: stats["background_no_fill"].append(note_value) # 规则18: 背景有颜色填充 if background_cell and not self.color_detector.is_no_fill(background_cell) and note_value: stats["background_fill"].append(note_value) # 处理统计结果 for rule, values in stats.items(): if not values: results[rule] = "/" logger.info(f"{rule}: 无数据") else: counter = Counter(values) result_lines = [f"{value},{count}" for value, count in counter.most_common()] results[rule] = "\n".join(result_lines) logger.info(f"{rule}: 找到 {len(values)} 条数据") return results except Exception as e: error_msg = f"处理SCL文件失败: {str(e)}" logger.exception(f"处理SCL文件失败: {file_path} - {str(e)}") # 返回错误信息 return {rule: f"错误: {str(e)}" for rule in self.target_columns} def view_log(self): """查看日志""" log_window = tk.Toplevel(self.root) log_window.title("处理日志") log_window.geometry("800x600") log_frame = ttk.Frame(log_window, padding="10") log_frame.pack(fill=tk.BOTH, expand=True) # 日志文本框 log_text = scrolledtext.ScrolledText( log_frame, wrap=tk.WORD, height=30 ) log_text.pack(fill=tk.BOTH, expand=True) # 读取日志文件 log_file = 'scl_processor.log' try: if not os.path.exists(log_file): with open(log_file, 'w', encoding='utf-8') as f: f.write("日志文件已创建,暂无记录\n") with open(log_file, 'r', encoding='utf-8') as log_file: log_content = log_file.read() log_text.insert(tk.END, log_content) except Exception as e: log_text.insert(tk.END, f"无法读取日志文件: {str(e)}") # 设置为只读 log_text.config(state=tk.DISABLED) # 添加刷新按钮 refresh_btn = ttk.Button(log_frame, text="刷新日志", command=lambda: self.refresh_log(log_text)) refresh_btn.pack(pady=5) logger.info("日志查看窗口已打开") def refresh_log(self, log_text): """刷新日志内容""" log_text.config(state=tk.NORMAL) log_text.delete(1.0, tk.END) try: with open('scl_processor.log', 'r', encoding='utf-8') as log_file: log_content = log_file.read() log_text.insert(tk.END, log_content) except Exception as e: log_text.insert(tk.END, f"刷新日志失败: {str(e)}") log_text.config(state=tk.DISABLED) log_text.see(tk.END) logger.info("日志已刷新") def export_config(self): """导出配置到文件""" config = { "diff_col": self.diff_col_var.get(), "note_col": self.note_col_var.get(), "valid_col": self.valid_col_var.get(), "reason_col": self.reason_col_var.get(), "background_col": self.background_col_var.get(), "prefix": self.prefix_var.get(), "max_search": self.max_search_var.get(), "log_level": self.log_level_var.get() } file_path = filedialog.asksaveasfilename( defaultextension=".json", filetypes=[("JSON 文件", "*.json"), ("所有文件", "*.*")] ) if file_path: try: with open(file_path, 'w', encoding='utf-8') as f: f.write(str(config)) messagebox.showinfo("成功", f"配置已导出到: {file_path}") logger.info(f"配置已导出到: {file_path}") except Exception as e: messagebox.showerror("错误", f"导出配置失败: {str(e)}") logger.error(f"导出配置失败: {str(e)}") def load_config(self): """从文件加载配置""" file_path = filedialog.askopenfilename( filetypes=[("JSON 文件", "*.json"), ("所有文件", "*.*")] ) if file_path: try: with open(file_path, 'r', encoding='utf-8') as f: config = eval(f.read()) self.diff_col_var.set(config.get("diff_col", "差分種別")) self.note_col_var.set(config.get("note_col", "备注")) self.valid_col_var.set(config.get("valid_col", "差分の判断\n有意/無効")) self.reason_col_var.set(config.get("reason_col", "判断理由")) self.background_col_var.set(config.get("background_col", "変更背景")) self.prefix_var.set(config.get("prefix", "SCL_")) self.max_search_var.set(config.get("max_search", 100)) self.log_level_var.set(config.get("log_level", "INFO")) self.change_log_level() messagebox.showinfo("成功", "配置已加载") logger.info(f"配置已从 {file_path} 加载") except Exception as e: messagebox.showerror("错误", f"加载配置失败: {str(e)}") logger.error(f"加载配置失败: {str(e)}")
if name == “main”:
root = tk.Tk()
app = SCLMultiProcessor(root)
root.mainloop()
在这个上面改,差分の判断有意/無効是列名,在一个单元格内分成了两行,我希望正确查找这个列名