import os import tkinter as tk from tkinter import ttk, filedialog, messagebox import openai from typing import Dict, Any class DirectoryAnalyzerApp: def __init__(self, root): self.root = root self.root.title("风电叶片目录分析工具") self.root.geometry("800x600") # 初始化变量 self.dir_path = tk.StringVar() self.original_structure = {} self.ai_structure = {} self.tree_items = {} # 用于存储树节点ID # 创建界面组件 self.create_widgets() def create_widgets(self): # 顶部框架 - 目录选择和扫描 top_frame = ttk.Frame(self.root, padding="10") top_frame.pack(fill=tk.X) ttk.Label(top_frame, text="目录路径:").pack(side=tk.LEFT) self.dir_entry = ttk.Entry(top_frame, textvariable=self.dir_path, width=50) self.dir_entry.pack(side=tk.LEFT, padx=5) browse_btn = ttk.Button(top_frame, text="浏览...", command=self.browse_directory) browse_btn.pack(side=tk.LEFT, padx=5) scan_btn = ttk.Button(top_frame, text="扫描目录", command=self.scan_directory) scan_btn.pack(side=tk.LEFT, padx=5) # 中间框架 - 目录树显示 mid_frame = ttk.Frame(self.root, padding="10") mid_frame.pack(fill=tk.BOTH, expand=True) # 创建Treeview和滚动条 self.tree = ttk.Treeview(mid_frame, columns=("original", "ai"), show="tree") self.tree.heading("#0", text="原始名称") self.tree.heading("original", text="原始名称") self.tree.heading("ai", text="AI建议名称") y_scroll = ttk.Scrollbar(mid_frame, orient=tk.VERTICAL, command=self.tree.yview) self.tree.configure(yscrollcommand=y_scroll.set) x_scroll = ttk.Scrollbar(mid_frame, orient=tk.HORIZONTAL, command=self.tree.xview) self.tree.configure(xscrollcommand=x_scroll.set) # 布局 self.tree.grid(row=0, column=0, sticky="nsew") y_scroll.grid(row=0, column=1, sticky="ns") x_scroll.grid(row=1, column=0, sticky="ew") mid_frame.grid_rowconfigure(0, weight=1) mid_frame.grid_columnconfigure(0, weight=1) # 底部框架 - 操作按钮 bottom_frame = ttk.Frame(self.root, padding="10") bottom_frame.pack(fill=tk.X) ai_btn = ttk.Button(bottom_frame, text="AI分析", command=self.ai_analyze) ai_btn.pack(side=tk.LEFT, padx=5) apply_btn = ttk.Button(bottom_frame, text="应用更改", command=self.apply_changes) apply_btn.pack(side=tk.LEFT, padx=5) edit_btn = ttk.Button(bottom_frame, text="更改叶片名", command=self.edit_selected) edit_btn.pack(side=tk.LEFT, padx=5) # 右键菜单 self.context_menu = tk.Menu(self.root, tearoff=0) self.context_menu.add_command(label="编辑名称", command=self.edit_selected) # 绑定右键事件 self.tree.bind("", self.show_context_menu) def browse_directory(self): """打开目录选择对话框""" dir_path = filedialog.askdirectory() if dir_path: self.dir_path.set(dir_path) def scan_directory(self): """扫描选择的目录""" path = self.dir_path.get() if not path: messagebox.showerror("错误", "请先选择目录") return try: self.original_structure = self.scan_dir(path) self.display_directory(self.original_structure) messagebox.showinfo("成功", "目录扫描完成") except Exception as e: messagebox.showerror("错误", f"扫描目录时出错: {str(e)}") def scan_dir(self, path): """扫描路径结构,递归查找路径并记录路径结构""" if not os.path.exists(path): return {} dir_structure = {} for item in os.listdir(path): item_path = os.path.join(path, item) if os.path.isdir(item_path): # 如果是目录,递归扫描 dir_structure[item] = self.scan_dir(item_path) return dir_structure def display_directory(self, dir_data, parent=""): """在Treeview中显示目录结构""" # 清空现有树 for item in self.tree.get_children(): self.tree.delete(item) # 递归插入节点 self._insert_nodes("", dir_data) def _insert_nodes(self, parent, data): """递归插入树节点""" for name, children in data.items(): if isinstance(children, dict): # 目录节点 node_id = self.tree.insert(parent, "end", text=name, values=(name, "")) self._insert_nodes(node_id, children) else: # 文件节点 ai_name = "" if name in self.ai_structure: ai_name = self.ai_structure[name] self.tree.insert(parent, "end", text=name, values=(name, ai_name)) def ai_analyze(self): """使用AI分析目录结构""" if not self.original_structure: messagebox.showerror("错误", "请先扫描目录") return try: # 提取所有文件名(最底层节点) file_names = self.extract_leaf_folders(self.original_structure) # 获取AI回复 self.ai_structure = self.get_ai_reply(file_names) # 更新显示 self.display_directory(self.ai_structure) messagebox.showinfo("成功", "AI分析完成") except Exception as e: messagebox.showerror("错误", f"AI分析时出错: {str(e)}") def extract_leaf_folders(self, dir_data): """从目录结构中提取所有最末端的文件夹名""" leaf_folders = [] def _extract(data): for name, children in data.items(): if isinstance(children, dict): if not children: # 如果子节点为空字典,说明这是最末端的文件夹 leaf_folders.append(name) else: _extract(children) _extract(dir_data) print(f'获取最底节点:{leaf_folders}') return leaf_folders def get_ai_reply(self, file_names: list) -> Dict[str, str]: """获取AI的返回信息,传入文件名列表,让AI返回标准化结构""" api_config = { "api_key": "sk-zbytvldaexbdauavbkclbhdcgrlahojvwfchtiqhyvlvryle", "base_url": "https://api.siliconflow.cn/v1", "model": "Qwen/Qwen2.5-72B-Instruct", "prompt": """请根据风电叶片目录命名规则,将以下叶片名称转换为标准格式。标准格式为:叶片号-检查人员姓名(编号)。编号内不包括字母,如果没有检查人员姓名,则格式为:叶片号-(编号)。并且标准的需要用花括号括起来 示例输入1: "1机组-1号叶片(2008-B10-185) 雷俊超" 示例输出1: "1-雷俊超(2008-B10-185)" #此处要加花括号 示例输入2: "1-3号叶片-(2008-B10-165)" 示例输出2: "3-(2008-B10-165)" #此处要加花括号 示例输入3: "2-韩凌柱(2008-B10-180)" 示例输出3: "2-韩凌柱(2008-B10-180)" #此处要加花括号 示例输入4:"1#(2008-B5-289)(王国辉)" #此处‘#’前面是叶片号 示例输出4:"1-王国辉(2008-B5-289)" #此处要加花括号 示例输入5:"8#3#~(刘晋宝)2008-B10-178" #此处8是机组号,3是叶片号 示例输出5:"3-刘晋宝(2008-B10-178)" #此处要加花括号 示例输入6:"A叶片 韩凌柱110030" #此处离”叶片”最近的字母为A,对应叶片号为1(B对应2,C对应3) 示例输出6:1-韩凌柱(110030)" #此处要加花括号 示例输入7:"105# B 叶片检查(刘晋宝)10136G" #此处离”叶片”最近的字母为B,对应叶片号为2(A对应1,C对应3) 示例输出7:"2-刘晋宝(10136G)" #此处要加花括号 示例输入8:"04F叶片A110033(蒋光明)" #04F表示机组,不必考虑在叶片命名内 示例输出8:"1-蒋光明(110033) #此处要加花括号 示例输入9:"2-雷俊超(内部防雷线有雷击痕迹)" #很显然此括号内是一种说明,而不是人名 示例输出9:"2-雷俊超" #此处要加花括号 现在请转换以下名称: {input}""" } try: # 初始化 OpenAI 客户端 client = openai.OpenAI( api_key=api_config["api_key"], base_url=api_config["base_url"] ) print(f"Client initialized: {client}") except Exception as e: print(f'初始化client失败:{e}') return {name: "客户端初始化失败" for name in file_names} result = {} for name in file_names: print(f'发送name:{name}') response = client.chat.completions.create( model=api_config["model"], messages=[ {"role": "user", "content": api_config["prompt"].format(input=name)} ], stream=False # 明确关闭流式传输 ) ai_reply = response.choices[0].message.content print(f'收到消息:{ai_reply}') # 使用正则表达式查找花括号内的内容 import re match = re.search(r'{(.*?)}', ai_reply) if match: content_inside_braces = match.group(1) result[name] = content_inside_braces else: result[name] = ai_reply # 如果没有花括号,使用原始内容 print(f'存入:{result[name]}') return result def apply_changes(self): """应用AI建议的更改到实际文件""" # 遍历原始结构并应用更改 def find_leaf_directories(directory_dict, current_path=None, result=None): if result is None: result = [] if current_path is None: current_path = [] elif isinstance(current_path, (str, type(self.dir_path))): # 处理 StringVar 或字符串情况 current_path = [current_path] for name, subdir in directory_dict.items(): new_path = current_path.copy() # 创建列表的副本 new_path.append(name) # 添加当前目录名 if not subdir: # 这是一个最子文件夹 result.append({ 'path': os.path.join(*new_path), # 使用os.path.join来构建路径 'name': name }) else: # 继续递归查找 find_leaf_directories(subdir, new_path, result) return result # 确保初始路径是列表形式 initial_path = [self.dir_path.get()] if hasattr(self.dir_path, 'get') else [str(self.dir_path)] result_to_replace = find_leaf_directories(self.original_structure, initial_path) for result in result_to_replace: original_path = result['path'] # 获取原始路径 original_name = result['name'] # 获取原始name # 从ai_structure中获取要修改的新name值 if original_name in self.ai_structure: new_name = self.ai_structure[original_name] # 构造新路径 dir_name = os.path.dirname(original_path) new_path = os.path.join(dir_name, new_name) try: # 确保目标目录不存在 if os.path.exists(new_path): print(f"Error: Target already exists - {new_path}") continue # 重命名文件夹 os.rename(original_path, new_path) print(f"Renamed: {original_path} -> {new_path}") except FileNotFoundError: print(f"Error: Path not found - {original_path}") except Exception as e: print(f"Error renaming {original_path}: {str(e)}") def edit_selected(self): """编辑选中的项目名称""" selected_item = self.tree.selection() if not selected_item: messagebox.showerror("错误", "请先选择一个项目") return # 获取当前值 item_text = self.tree.item(selected_item, "text") item_values = self.tree.item(selected_item, "values") # 创建编辑对话框 edit_dialog = tk.Toplevel(self.root) edit_dialog.title("编辑名称") edit_dialog.geometry("400x200") ttk.Label(edit_dialog, text="原始名称:").pack(pady=5) original_entry = ttk.Entry(edit_dialog) original_entry.insert(0, item_text) original_entry.config(state="readonly") original_entry.pack(fill=tk.X, padx=10, pady=5) ttk.Label(edit_dialog, text="AI建议名称:").pack(pady=5) ai_entry = ttk.Entry(edit_dialog) ai_entry.insert(0, item_values[1] if len(item_values) > 1 else "") ai_entry.pack(fill=tk.X, padx=10, pady=5) def save_changes(): new_name = ai_entry.get() if new_name: # 更新树显示 self.tree.item(selected_item, values=(item_text, new_name)) # 更新AI结构字典 if item_text in self.ai_structure: self.ai_structure[item_text] = new_name edit_dialog.destroy() save_btn = ttk.Button(edit_dialog, text="保存", command=save_changes) save_btn.pack(pady=10) def show_context_menu(self, event): """显示右键上下文菜单""" item = self.tree.identify_row(event.y) if item: self.tree.selection_set(item) self.context_menu.post(event.x_root, event.y_root) if __name__ == "__main__": root = tk.Tk() app = DirectoryAnalyzerApp(root) root.mainloop()