data-return/structure_file.py

354 lines
15 KiB
Python
Raw Permalink Normal View History

2025-07-25 18:08:17 +08:00
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("<Button-3>", 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对应2C对应3)
示例输出6:1-韩凌柱110030" #此处要加花括号
示例输入7:"105# B 叶片检查刘晋宝10136G" #此处离”叶片”最近的字母为B对应叶片号为2(A对应1C对应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()