我的工作场景会遇到很多文件夹下有很多文件,文件名不太规则而且还很长,我需要抓取这些文件名中的关键字来匹配其他的数据,然后在将这些文件改成规则的文件名,所以做了这个小工具分享给大家。
文件名抓改工具下载:
文件名修改后可以通过下面这个工具来验证
文件名抓改工具源码:
- import os
- import pandas as pd
- import tkinter as tk
- from tkinter import ttk
- from tkinter import filedialog
- from tkinter import messagebox
- import re
- import shutil
- from datetime import datetime
- from tkinter import scrolledtext
- from PIL import Image, ImageTk
- import io
- import sys
-
- class FileManagementApp:
- def __init__(self, root):
- self.root = root
- self.root.title("文件名抓改系统V2.1 By来乐老弟")
- self.root.geometry("1024x768")
- self.root.minsize(800, 600)
-
- # 配置主窗口的网格布局
- self.root.grid_rowconfigure(0, weight=1)
- self.root.grid_columnconfigure(0, weight=1)
-
- # 设置主题样式
- style = ttk.Style()
- style.configure("TNotebook", background="#f0f0f0")
- style.configure("TFrame", background="#ffffff")
- style.configure("TButton", padding=5)
- style.configure("Reward.TButton", font=("SimHei", 9, "bold"), foreground="blue")
-
- # 初始化选择的文件夹列表
- self.selected_folders = []
-
- # 绑定快捷键
- self.root.bind("<Control-o>", lambda e: self.select_folder())
- self.root.bind("<Control-e>", lambda e: self.export_files())
- self.root.bind("<Control-r>", lambda e: self.rename_files())
- self.root.bind("<Control-z>", lambda e: self.undo_renaming())
-
- # 绑定快捷键
- self.root.bind("<Control-o>", lambda e: self.select_folder())
- self.root.bind("<Control-e>", lambda e: self.export_files())
- self.root.bind("<Control-r>", lambda e: self.rename_files())
- self.root.bind("<Control-z>", lambda e: self.undo_renaming())
-
- # 初始化文件信息列表
- self.all_files_info = []
- # 初始化备份信息
- self.backup_info = []
-
- # 绑定窗口关闭事件
- self.root.protocol("WM_DELETE_WINDOW", self.on_closing)
-
- # 创建主选项卡
- self.notebook = ttk.Notebook(root)
- self.notebook.grid(row=0, column=0, sticky="nsew", padx=5, pady=5)
-
- # 创建文件名抓取页面
- self.create_file_capture_tab()
- # 创建文件名修改页面
- self.create_file_rename_tab()
-
- def create_file_capture_tab(self):
- # 文件名抓取页面
- self.capture_tab = ttk.Frame(self.notebook)
- self.notebook.add(self.capture_tab, text='文件名抓取')
-
- # 创建顶部框架
- top_frame = ttk.Frame(self.capture_tab)
- top_frame.pack(fill='x', padx=10, pady=5)
-
- # 文件夹选择
- self.folder_label = ttk.Label(top_frame, text="选择文件夹(Ctrl+O):")
- self.folder_label.pack(side='left', padx=5)
-
- self.folder_entry = ttk.Entry(top_frame, width=50)
- self.folder_entry.pack(side='left', padx=5)
-
- self.browse_button = ttk.Button(top_frame, text="浏览...", command=self.select_folder)
- self.browse_button.pack(side='left', padx=5)
-
- self.append_button = ttk.Button(top_frame, text="追加文件夹", command=self.append_folder)
- self.append_button.pack(side='left', padx=5)
-
- # 创建主框架
- main_frame = ttk.Frame(self.capture_tab)
- main_frame.pack(fill='both', expand=True, padx=10, pady=5)
-
- # 子目录选项
- options_frame = ttk.Frame(main_frame)
- options_frame.pack(fill='x', pady=5)
-
- self.subdir_var = tk.BooleanVar(value=True)
- self.subdir_check = ttk.Checkbutton(options_frame, text="读取子目录文件", variable=self.subdir_var)
- self.subdir_check.pack(side='left')
-
- # 文件列表显示(带滚动条)
- list_frame = ttk.Frame(main_frame)
- list_frame.pack(fill='both', expand=True)
-
- self.listbox = tk.Listbox(list_frame, selectmode='extended')
- scrollbar_y = ttk.Scrollbar(list_frame, orient='vertical', command=self.listbox.yview)
- scrollbar_x = ttk.Scrollbar(list_frame, orient='horizontal', command=self.listbox.xview)
-
- self.listbox.configure(yscrollcommand=scrollbar_y.set, xscrollcommand=scrollbar_x.set)
-
- scrollbar_y.pack(side='right', fill='y')
- scrollbar_x.pack(side='bottom', fill='x')
- self.listbox.pack(side='left', fill='both', expand=True)
-
- # 底部工具栏
- bottom_frame = ttk.Frame(self.capture_tab)
- bottom_frame.pack(fill='x', padx=10, pady=5)
-
- self.export_button = ttk.Button(bottom_frame, text="导出到Excel(Ctrl+E)", command=self.export_files)
- self.export_button.pack(side='left', padx=5)
-
- # 添加赏赞按钮
- self.reward_button = ttk.Button(bottom_frame, text="赏赞", command=self.show_reward, style="Reward.TButton")
- self.reward_button.pack(side='left', padx=5)
-
- # 进度条
- self.progress_var = tk.DoubleVar()
- self.progress_bar = ttk.Progressbar(bottom_frame, length=300, mode='determinate', variable=self.progress_var)
- self.progress_bar.pack(side='left', padx=10, fill='x', expand=True)
-
- # 手动分割区域
- split_frame = ttk.Frame(self.capture_tab)
- split_frame.pack(fill='x', padx=10, pady=5)
-
- split_label = ttk.Label(split_frame, text="手动分割设置:")
- split_label.pack(side='left', padx=5)
-
- # 添加说明文字
- help_text = "说明:通过添加分割字段来设置文件名的分割规则。在示例值中填入与实际文件名对应位置相同长度的字符,系统将按照示例值的长度依次分割文件名。"
- help_label = ttk.Label(split_frame, text=help_text, wraplength=600)
- help_label.pack(side='top', padx=5, pady=5)
-
- self.split_entries = []
- self.split_frame = split_frame
-
- add_split_button = ttk.Button(split_frame, text="添加分割字段", command=self.add_split_entry)
- add_split_button.pack(side='left', padx=5)
-
-
- # 状态显示
- self.status_label = ttk.Label(bottom_frame, text="")
- style = ttk.Style()
- style.configure('Status.TLabel', foreground='blue')
- self.status_label.configure(style='Status.TLabel')
- self.status_label.pack(side='right', padx=5)
-
- # 绑定列表选择事件
- self.listbox.bind('<<ListboxSelect>>', self.on_select_file)
-
- def create_file_rename_tab(self):
- # 文件名修改页面
- self.rename_tab = ttk.Frame(self.notebook)
- self.notebook.add(self.rename_tab, text='文件名修改')
-
- # 创建顶部框架
- top_frame = ttk.Frame(self.rename_tab)
- top_frame.pack(fill='x', padx=10, pady=5)
-
- # Excel文件选择
- self.excel_label = ttk.Label(top_frame, text="选择Excel文件(Ctrl+E):")
- self.excel_label.pack(side='left', padx=5)
-
- self.excel_entry = ttk.Entry(top_frame, width=50)
- self.excel_entry.pack(side='left', padx=5)
-
- self.excel_button = ttk.Button(top_frame, text="浏览...", command=self.select_excel_file)
- self.excel_button.pack(side='left', padx=5)
-
- # 创建主框架
- main_frame = ttk.Frame(self.rename_tab)
- main_frame.pack(fill='both', expand=True, padx=10, pady=5)
-
- # 预览表格
- self.tree = ttk.Treeview(main_frame, columns=('A', 'B'), show='headings')
- self.tree.pack(pady=10, fill='both', expand=True)
-
- # 列选择
- self.column_frame = ttk.Frame(main_frame)
- self.column_frame.pack(fill='x', pady=5)
-
- self.old_name_label = ttk.Label(self.column_frame, text="原文件名列:")
- self.old_name_label.pack(side=tk.LEFT, padx=5)
-
- self.old_name_combobox = ttk.Combobox(self.column_frame, state='readonly')
- self.old_name_combobox.pack(side=tk.LEFT, padx=5)
-
- self.new_name_label = ttk.Label(self.column_frame, text="新文件名列:")
- self.new_name_label.pack(side=tk.LEFT, padx=5)
-
- self.new_name_combobox = ttk.Combobox(self.column_frame, state='readonly')
- self.new_name_combobox.pack(side=tk.LEFT, padx=5)
-
- # 操作按钮
- bottom_frame = ttk.Frame(self.rename_tab)
- bottom_frame.pack(fill='x', padx=10, pady=5)
-
- self.rename_button = ttk.Button(bottom_frame, text="执行重命名(Ctrl+R)", command=self.rename_files)
- self.rename_button.pack(side='left', padx=5)
-
- self.undo_button = ttk.Button(bottom_frame, text="撤回修改(Ctrl+Z)", command=self.undo_renaming)
- self.undo_button.pack(side='left', padx=5)
-
- self.cleanup_button = ttk.Button(bottom_frame, text="清理备份", command=self.cleanup_backups)
- self.cleanup_button.pack(side='left', padx=5)
-
- # 添加赏赞按钮
- self.reward_button_rename = ttk.Button(bottom_frame, text="赏赞", command=self.show_reward, style="Reward.TButton")
- self.reward_button_rename.pack(side='left', padx=5)
-
- # 进度条
- self.rename_progress_var = tk.DoubleVar()
- self.rename_progress_bar = ttk.Progressbar(bottom_frame, length=300, mode='determinate', variable=self.rename_progress_var)
- self.rename_progress_bar.pack(side='left', padx=10, fill='x', expand=True)
-
- # 状态显示
- self.rename_status_label = ttk.Label(bottom_frame, text="")
- style = ttk.Style()
- style.configure('RenameStatus.TLabel', foreground='blue')
- self.rename_status_label.configure(style='RenameStatus.TLabel')
- self.rename_status_label.pack(side='right', padx=5)
-
- def select_folder(self):
- folder = filedialog.askdirectory()
- if folder:
- self.selected_folders = [folder]
- self.folder_entry.delete(0, tk.END)
- self.folder_entry.insert(0, folder)
- self.collect_files(self.selected_folders)
-
- def append_folder(self):
- folder = filedialog.askdirectory()
- if folder:
- if folder not in self.selected_folders:
- self.selected_folders.append(folder)
- current_text = self.folder_entry.get()
- if current_text:
- self.folder_entry.delete(0, tk.END)
- self.folder_entry.insert(0, current_text + "; " + folder)
- else:
- self.folder_entry.insert(0, folder)
- self.collect_files(self.selected_folders)
-
- def collect_files(self, folders):
- self.listbox.delete(0, tk.END)
- self.all_files_info = []
- if isinstance(folders, str):
- folders = [folders]
-
- total_files = 0
- for folder in folders:
- total_files += sum([len(files) for _, _, files in os.walk(folder)])
-
- processed_files = 0
- for folder in folders:
- for root, dirs, files in os.walk(folder):
- if not self.subdir_var.get() and root != folder:
- continue
-
- for file in files:
- file_path = os.path.join(root, file)
- file_info = self.analyze_filename(file_path)
- self.all_files_info.append(file_info)
- self.listbox.insert(tk.END, file_info['file_name'])
-
- processed_files += 1
- progress = (processed_files / total_files) * 100
- self.progress_var.set(progress)
- self.status_label.config(text=f"正在处理: {processed_files}/{total_files}")
- self.root.update()
-
- if not self.all_files_info:
- self.status_label.config(text="没有找到任何文件!", foreground="red")
- else:
- self.status_label.config(text=f"共找到 {len(self.all_files_info)} 个文件", foreground="green")
-
- self.progress_var.set(0)
-
- def analyze_filename(self, file_path):
- """文件名智能分析逻辑,提取文件名中的各种信息"""
- file_stats = os.stat(file_path)
- # 获取文件名和扩展名
- file_name = os.path.basename(file_path)
- # 获取文件所在文件夹名
- folder_name = os.path.dirname(file_path)
- # 获取文件大小(KB)
- size_kb = file_stats.st_size / 1024
- # 获取文件创建时间
- create_time = datetime.fromtimestamp(file_stats.st_ctime).strftime('%Y-%m-%d %H:%M:%S')
- # 获取文件修改时间
- modify_time = datetime.fromtimestamp(file_stats.st_mtime).strftime('%Y-%m-%d %H:%M:%S')
- # 获取文件扩展名
- _, extension = os.path.splitext(file_path)
-
- # 自动提取文件名中的各种信息
- try:
- # 动态提取所有数字部分
- numbers = re.findall(r'\d+', file_name)
- number = numbers[0] if numbers else ''
-
- # 动态提取所有中文字符
- chinese_chars = re.findall(r'[\u4e00-\u9fa5]+', file_name)
- name = chinese_chars[0] if chinese_chars else ''
-
- # 动态提取电话号码(支持多种格式)
- phone_patterns = [
- r'1\d{10}',
- r'\d{3} \d{4} \d{4}',
- r'\d{3}-\d{4}-\d{4}',
- r'\d{3}\.\d{4}\.\d{4}'
- ]
- phone = ''
- for pattern in phone_patterns:
- match = re.search(pattern, file_name)
- if match:
- phone = match.group(0)
- break
-
- # 动态提取特殊符号
- special_chars = re.findall(r'[^\w\s\u4e00-\u9fa5]', file_name)
- special_chars = ''.join(set(special_chars))
-
- # 动态提取英文单词
- english_words = re.findall(r'[A-Za-z]+', file_name)
-
- # 动态提取日期时间(支持更多格式)
- datetime_patterns = [
- r'\d{4}-\d{2}-\d{2}',
- r'\d{4}\d{2}\d{2}',
- r'\d{2}-\d{2}-\d{4}',
- r'\d{2}\d{2}\d{4}',
- r'\d{4}年\d{2}月\d{2}日',
- r'\d{2}月\d{2}日\d{4}'
- ]
- dates = []
- for pattern in datetime_patterns:
- dates.extend(re.findall(pattern, file_name))
-
- # 动态提取时间格式
- time_patterns = [
- r'\d{2}:\d{2}:\d{2}',
- r'\d{2}-\d{2}-\d{2}',
- r'\d{2}\d{2}\d{2}',
- r'\d{2}时\d{2}分\d{2}秒'
- ]
- times = []
- for pattern in time_patterns:
- times.extend(re.findall(pattern, file_name))
-
- except Exception as e:
- number = ''
- name = ''
- phone = ''
- special_chars = ''
- english_words = []
- dates = []
- times = []
-
- return {
- 'folder': folder_name,
- 'file_name': file_name,
- 'number': number,
- 'name': name,
- 'phone': phone,
- 'extension': extension,
- 'size_kb': round(size_kb, 2),
- 'create_time': create_time,
- 'modify_time': modify_time,
- 'full_path': file_path,
- 'special_chars': special_chars,
- 'english_words': ','.join(english_words),
- 'dates': ','.join(dates),
- 'times': ','.join(times)
- }
-
- def select_excel_file(self):
- file_path = filedialog.askopenfilename(filetypes=[("Excel files", "*.xlsx")])
- if file_path:
- self.excel_entry.delete(0, tk.END)
- self.excel_entry.insert(0, file_path)
- self.preview_excel(file_path)
-
- def preview_excel(self, file_path):
- try:
- self.df = pd.read_excel(file_path)
- self.update_column_comboboxes()
- self.update_treeview()
- except Exception as e:
- messagebox.showerror("错误", f"无法打开Excel文件: {str(e)}")
-
- def update_column_comboboxes(self):
- columns = self.df.columns.tolist()
- self.old_name_combobox['values'] = columns
- self.new_name_combobox['values'] = columns
-
- def update_treeview(self):
- # 清空Treeview
- for i in self.tree.get_children():
- self.tree.delete(i)
-
- # 设置列标题
- columns = self.df.columns.tolist()
- self.tree['columns'] = columns
- for col in columns:
- self.tree.heading(col, text=col)
- self.tree.column(col, width=100, anchor='w')
-
- # 添加数据
- for _, row in self.df.iterrows():
- self.tree.insert('', 'end', values=row.tolist())
-
- def on_select_file(self, event):
- """处理文件选择事件"""
- pass
-
- def rename_files(self):
- """重命名文件的主要方法"""
- # 验证Excel文件和列选择
- if not hasattr(self, 'df') or self.df.empty:
- messagebox.showerror("错误", "请先选择并预览Excel文件!")
- return
-
- if not self.old_name_combobox.get() or not self.new_name_combobox.get():
- messagebox.showerror("错误", "请选择原文件名列和新文件名列!")
- return
-
- try:
- # 获取列名
- old_col = self.old_name_combobox.get()
- new_col = self.new_name_combobox.get()
- rename_count = 0
- failed_files = []
- self.backup_info = [] # 清空之前的备份信息
-
- # 处理每一行数据
- for _, row in self.df.iterrows():
- old_name = str(row[old_col])
- new_name = str(row[new_col])
-
- if not old_name or not new_name:
- continue
-
- # 统一路径格式
- file_path = old_name.replace('\\', '/')
-
- # 验证文件路径
- if not file_path or not os.path.exists(file_path):
- # 尝试通过文件名匹配获取完整路径
- if hasattr(self, 'all_files_info') and self.all_files_info:
- print(f'尝试匹配文件名: {old_name}')
- file_path = self.find_file_by_name(old_name)
-
- # 最终验证路径
- if not file_path or not os.path.exists(file_path):
- failed_files.append(f"未找到文件路径: {file_path}")
- continue
-
- # 组合新路径
- new_path = os.path.join(os.path.dirname(file_path), new_name).replace('\\', '/')
-
- # 验证目标路径
- if os.path.exists(new_path):
- failed_files.append(f"目标文件已存在: {new_path}")
- continue
-
- # 验证权限
- if not os.access(file_path, os.W_OK):
- failed_files.append(f"没有写权限: {file_path}")
- continue
-
- try:
- # 创建备份
- backup_path = self.backup_file(file_path)
- if backup_path:
- self.backup_info.append((backup_path, file_path, new_path))
-
- # 执行重命名
- os.rename(file_path, new_path)
- rename_count += 1
- print(f'成功重命名: {file_path} -> {new_path}')
- except PermissionError:
- failed_files.append(f"权限错误: {file_path}")
- print(f'权限错误: {file_path}')
- except Exception as e:
- failed_files.append(f"{old_name} -> {new_name}: {str(e)}")
- print(f'重命名错误: {str(e)}')
-
- # 显示结果
- result_message = f"成功重命名 {rename_count} 个文件\n失败 {len(failed_files)} 个文件"
- if failed_files:
- result_message += "\n失败原因:\n" + "\n".join(failed_files)
-
- style = ttk.Style()
- style.configure('RenameSuccess.TLabel', foreground='green')
- self.rename_status_label.configure(style='RenameSuccess.TLabel')
- self.rename_status_label.configure(text=result_message)
- messagebox.showinfo("完成", result_message)
-
- except Exception as e:
- messagebox.showerror("错误", f"重命名过程中发生错误: {str(e)}")
- # 保存日志
- if failed_files:
- self.save_log(failed_files)
-
- def find_file_by_name(self, name):
- """通过文件名查找文件路径"""
- for file_info in self.all_files_info:
- # 清理名称,忽略大小写、空格和特殊字符
- clean_name = re.sub(r'[^\w\u4e00-\u9fa5]', '', name)
- clean_file_name = re.sub(r'[^\w\u4e00-\u9fa5]', '', file_info['file_name'])
-
- # 尝试多种匹配方式
- if (clean_name.lower() in clean_file_name.lower() or
- re.search(r'\b' + re.escape(clean_name) + r'\b', file_info['file_name'], re.IGNORECASE)):
- return file_info['full_path']
- return None
-
- def backup_file(self, file_path):
- """创建文件备份"""
- try:
- backup_dir = os.path.join(os.path.dirname(file_path), '.backup')
- os.makedirs(backup_dir, exist_ok=True)
-
- timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
- filename = os.path.basename(file_path)
- backup_path = os.path.join(backup_dir, f"{timestamp}_{filename}")
-
- shutil.copy2(file_path, backup_path)
- return backup_path
- except Exception as e:
- print(f"备份文件失败: {str(e)}")
- return None
-
- def add_split_entry(self):
- """添加一个新的分割字段输入框"""
- if len(self.split_entries) >= 10: # 限制最大输入框数量
- messagebox.showinfo("提示", "最多只能添加10个分割字段!")
- return
-
- entry_frame = ttk.Frame(self.split_frame)
- entry_frame.pack(fill='x', pady=2)
-
- # 添加列标签
- col_label = ttk.Label(entry_frame, text=f"第{len(self.split_entries) + 1}列:")
- col_label.pack(side='left', padx=2)
-
- # 添加示例值输入框
- split_label = ttk.Label(entry_frame, text="示例值:")
- split_label.pack(side='left', padx=2)
-
- split_entry = ttk.Entry(entry_frame, width=20)
- split_entry.pack(side='left', padx=2)
-
- # 隐藏的name_entry用于保持代码兼容性
- name_entry = ttk.Entry(entry_frame)
- name_entry.pack_forget()
-
- def remove_entry():
- entry_frame.destroy()
- self.split_entries.remove((name_entry, split_entry, entry_frame))
-
- remove_button = ttk.Button(entry_frame, text="删除", command=remove_entry)
- remove_button.pack(side='left', padx=2)
-
- self.split_entries.append((name_entry, split_entry, entry_frame))
-
- def export_files(self):
- """将收集的文件信息导出到Excel"""
- if not self.all_files_info:
- messagebox.showinfo("提示", "没有文件可以导出!")
- return
-
- try:
- # 创建导出文件名
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
- export_filename = f"文件列表_{timestamp}.xlsx"
- export_path = filedialog.asksaveasfilename(
- defaultextension=".xlsx",
- filetypes=[("Excel files", "*.xlsx")],
- initialfile=export_filename
- )
-
- if not export_path:
- return # 用户取消了保存对话框
-
- # 创建DataFrame并导出
- df = pd.DataFrame(self.all_files_info)
-
- # 合并并转换文件路径格式
- df['full_path'] = df.apply(lambda row: os.path.join(row['folder'], row['full_path']).replace('\\', '/'), axis=1)
- df = df.drop('folder', axis=1) # 删除单独的folder列
-
- # 添加手动分割的列
- if self.split_entries:
- # 为每个文件名创建分割结果
- for idx, (name_entry, split_entry, _) in enumerate(self.split_entries):
- split_value = split_entry.get().strip()
- if not split_value:
- continue
-
- field_name = f'分割_{idx + 1}' # 使用更有意义的列名
- split_length = len(split_value) # 获取示例值的长度
-
- # 根据示例值的长度来分割文件名
- def split_filename(filename):
- # 如果文件名为空,返回空字符串
- if not filename:
- return ''
-
- try:
- # 获取当前处理的文件名位置
- start_pos = 0
- for i in range(idx):
- prev_split_value = self.split_entries[i][1].get().strip()
- if prev_split_value:
- start_pos += len(prev_split_value)
-
- # 根据示例值的长度提取对应位数的字符
- if start_pos < len(filename):
- return filename[start_pos:start_pos + split_length]
- except Exception:
- pass
-
- # 如果提取失败,返回空字符串
- return ''
-
- df[field_name] = df['file_name'].apply(split_filename)
-
- # 重新排列列的顺序
- columns_order = ['file_name'] # 首先添加原始文件名
- # 添加分割列(只添加实际存在的分割列)
- split_columns = [f'分割_{i + 1}' for i in range(len(self.split_entries))
- if self.split_entries[i][1].get().strip()]
- columns_order.extend(split_columns)
- # 添加其他重要信息
- other_columns = ['full_path', 'size_kb', 'create_time', 'modify_time']
- columns_order.extend([col for col in other_columns if col in df.columns])
- # 仅保留存在的列
- existing_columns = [col for col in columns_order if col in df.columns]
- df = df[existing_columns]
-
- # 导出到Excel
- df.to_excel(export_path, index=False)
-
- style = ttk.Style()
- style.configure('Success.TLabel', foreground='green')
- self.status_label.configure(style='Success.TLabel')
- self.status_label.configure(text=f"已成功导出 {len(self.all_files_info)} 个文件信息到 {export_path}")
- messagebox.showinfo("成功", f"文件信息已成功导出到:\n{export_path}")
-
- except Exception as e:
- style = ttk.Style()
- style.configure('Error.TLabel', foreground='red')
- self.status_label.configure(style='Error.TLabel')
- self.status_label.configure(text=f"导出失败: {str(e)}")
- messagebox.showerror("错误", f"导出文件时发生错误:\n{str(e)}")
-
- def undo_renaming(self):
- """撤销之前的重命名操作"""
- if not self.backup_info:
- messagebox.showinfo("提示", "没有可以撤销的重命名操作!")
- return
-
- try:
- undo_count = 0
- failed_undo = []
-
- # 反向遍历备份信息,以便按照相反的顺序撤销操作
- for backup_path, original_path, new_path in reversed(self.backup_info):
- try:
- # 检查当前文件是否存在
- if os.path.exists(new_path):
- # 检查备份文件是否存在
- if os.path.exists(backup_path):
- # 先删除当前文件,然后恢复备份
- os.remove(new_path)
- shutil.copy2(backup_path, original_path)
- undo_count += 1
- else:
- # 如果备份不存在,尝试直接重命名回去
- os.rename(new_path, original_path)
- undo_count += 1
- else:
- # 如果新文件不存在,但备份存在,直接恢复备份
- if os.path.exists(backup_path):
- shutil.copy2(backup_path, original_path)
- undo_count += 1
- else:
- failed_undo.append(f"无法撤销: {new_path} -> {original_path} (备份文件不存在)")
- except Exception as e:
- failed_undo.append(f"撤销失败: {new_path} -> {original_path}: {str(e)}")
-
- # 清空备份信息
- self.backup_info = []
-
- # 显示结果
- result_message = f"成功撤销 {undo_count} 个重命名操作"
- if failed_undo:
- result_message += f"\n失败 {len(failed_undo)} 个操作"
- result_message += "\n失败原因:\n" + "\n".join(failed_undo)
-
- style = ttk.Style()
- style.configure('RenameSuccess.TLabel', foreground='green')
- self.rename_status_label.configure(style='RenameSuccess.TLabel')
- self.rename_status_label.configure(text=result_message)
- messagebox.showinfo("完成", result_message)
-
- except Exception as e:
- messagebox.showerror("错误", f"撤销重命名时发生错误: {str(e)}")
-
- def save_log(self, failed_items, prefix="重命名"):
- """保存操作日志"""
- try:
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
- log_filename = f"{prefix}日志_{timestamp}.txt"
-
- with open(log_filename, 'w', encoding='utf-8') as f:
- f.write(f"{prefix}操作日志 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
- f.write("=" * 50 + "\n\n")
-
- for item in failed_items:
- f.write(f"{item}\n")
-
- print(f"日志已保存到: {log_filename}")
- return log_filename
- except Exception as e:
- print(f"保存日志失败: {str(e)}")
- return None
-
- def cleanup_backups(self):
- """清理所有备份文件"""
- try:
- cleaned_count = 0
- failed_paths = []
- processed_dirs = set()
-
- # 从已知的备份信息中获取目录
- for backup_path, _, _ in self.backup_info:
- backup_dir = os.path.dirname(backup_path)
- processed_dirs.add(os.path.dirname(backup_dir))
-
- # 从当前文件列表中获取目录
- if hasattr(self, 'all_files_info'):
- for file_info in self.all_files_info:
- dir_path = os.path.dirname(file_info.get('file_path', ''))
- if dir_path:
- processed_dirs.add(dir_path)
-
- # 清理每个目录中的备份文件
- for dir_path in processed_dirs:
- backup_dir = os.path.join(dir_path, '.backup')
- if os.path.exists(backup_dir):
- try:
- shutil.rmtree(backup_dir)
- cleaned_count += 1
- except Exception as e:
- failed_paths.append(f"{backup_dir}: {str(e)}")
-
- # 显示结果
- result_message = f"成功清理 {cleaned_count} 个备份目录"
- if failed_paths:
- result_message += f"\n清理失败 {len(failed_paths)} 个目录"
- result_message += "\n失败原因:\n" + "\n".join(failed_paths)
-
- style = ttk.Style()
- style.configure('RenameSuccess.TLabel', foreground='green')
- self.rename_status_label.configure(style='RenameSuccess.TLabel')
- self.rename_status_label.configure(text=result_message)
- messagebox.showinfo("完成", result_message)
-
- except Exception as e:
- error_message = f"清理备份文件时发生错误: {str(e)}"
- style = ttk.Style()
- style.configure('RenameError.TLabel', foreground='red')
- self.rename_status_label.configure(style='RenameError.TLabel')
- self.rename_status_label.configure(text=error_message)
- messagebox.showerror("错误", error_message)
-
- def show_reward(self):
- # 创建新窗口
- reward_window = tk.Toplevel(self.root)
- reward_window.title("赏赞码")
- reward_window.geometry("400x500")
-
- # 加载并显示图片
- try:
- # 获取资源文件路径
- if getattr(sys, 'frozen', False):
- # 如果是打包后的exe
- base_path = sys._MEIPASS
- else:
- # 如果是开发环境
- base_path = os.path.abspath(os.path.dirname(__file__))
- reward_path = os.path.join(base_path, "reward.jpg")
- image = Image.open(reward_path)
- # 调整图片大小以适应窗口
- image = image.resize((300, 300), Image.Resampling.LANCZOS)
- photo = ImageTk.PhotoImage(image)
-
- # 创建标签显示图片
- image_label = ttk.Label(reward_window, image=photo)
- image_label.image = photo # 保持对图片的引用
- image_label.pack(pady=20)
-
- # 添加文字提示
- text_label = ttk.Label(reward_window, text="您的支持是对作者最大的帮助", font=("SimHei", 12))
- text_label.pack(pady=10)
-
- # 添加关闭按钮
- close_button = ttk.Button(reward_window, text="关闭", command=reward_window.destroy)
- close_button.pack(pady=10)
-
- except Exception as e:
- messagebox.showerror("错误", f"无法加载赏赞码图片: {str(e)}")
- reward_window.destroy()
-
- def on_closing(self):
- """窗口关闭时的处理函数"""
- try:
- # 遍历所有已知的文件路径
- backup_dirs = set()
- for file_info in self.all_files_info:
- folder = file_info['folder']
- backup_dir = os.path.join(folder, '.backup')
- if os.path.exists(backup_dir):
- backup_dirs.add(backup_dir)
-
- # 清理所有.backup目录
- for backup_dir in backup_dirs:
- try:
- shutil.rmtree(backup_dir)
- print(f"已清理临时文件目录: {backup_dir}")
- except Exception as e:
- print(f"清理临时文件目录失败: {backup_dir}, 错误: {str(e)}")
- except Exception as e:
- print(f"清理临时文件时发生错误: {str(e)}")
- finally:
- self.root.destroy()
-
- # 主函数
- if __name__ == "__main__":
- root = tk.Tk()
- app = FileManagementApp(root)
- root.mainloop()
复制代码 文件路径验证工具源码
- import os
- from datetime import datetime
- import tkinter as tk
- from tkinter import ttk
- from tkinter import filedialog, messagebox
- import pandas as pd
-
- class VerifyFilesApp:
- def __init__(self, master):
- self.master = master
- master.title('文件路径验证工具V1.1 by来乐老弟')
-
- # 文件选择
- # 文件选择
- self.file_frame = ttk.LabelFrame(master, text='文件选择', padding=10)
- self.file_frame.grid(row=0, column=0, columnspan=3, padx=10, pady=5, sticky='ew')
-
- self.file_label = tk.Label(self.file_frame, text='Excel文件:')
- self.file_label.grid(row=0, column=0, padx=5, pady=5)
- self.file_entry = tk.Entry(self.file_frame, width=40)
- self.file_entry.grid(row=0, column=1, padx=5, pady=5)
- self.file_btn = tk.Button(self.file_frame, text='选择...', command=self.select_file)
- self.file_btn.grid(row=0, column=2, padx=5, pady=5)
-
- # 列选择
- self.column_label = tk.Label(self.file_frame, text='对比列:')
- self.column_label.grid(row=1, column=0, padx=5, pady=5)
- self.column_combobox = ttk.Combobox(self.file_frame, width=37)
- self.column_combobox.grid(row=1, column=1, padx=5, pady=5, columnspan=2, sticky='ew')
-
- # 操作按钮
- self.control_frame = ttk.Frame(master, padding=10)
- self.control_frame.grid(row=1, column=0, columnspan=3, padx=10, pady=5, sticky='ew')
-
- self.verify_btn = tk.Button(self.control_frame, text='开始验证', command=self.start_verification)
- self.verify_btn.pack(side='left', padx=5)
- self.save_btn = tk.Button(self.control_frame, text='保存日志', command=self.save_log)
- self.save_btn.pack(side='left', padx=5)
-
- # 日志显示
- self.log_frame = ttk.LabelFrame(master, text='验证日志', padding=10)
- self.log_frame.grid(row=2, column=0, columnspan=3, padx=10, pady=5, sticky='nsew')
-
- self.log_text = tk.Text(self.log_frame, wrap=tk.WORD, font=('Arial', 10))
- self.log_text.pack(fill='both', expand=True)
-
- master.grid_rowconfigure(2, weight=1)
- master.grid_columnconfigure(0, weight=1)
- self.log_text.tag_configure('valid', foreground='green')
- self.log_text.tag_configure('invalid', foreground='red')
-
- # 帮助菜单
- self.menu_bar = tk.Menu(master)
- self.help_menu = tk.Menu(self.menu_bar, tearoff=0)
- self.help_menu.add_command(label='使用帮助', command=self.show_help)
- self.menu_bar.add_cascade(label='帮助', menu=self.help_menu)
- master.config(menu=self.menu_bar)
-
- def select_file(self):
- filename = filedialog.askopenfilename(title='选择Excel文件', filetypes=[('Excel文件', '*.xlsx')])
- if filename:
- self.file_entry.delete(0, tk.END)
- self.file_entry.insert(0, filename)
- try:
- df = pd.read_excel(filename)
- self.column_combobox['values'] = df.columns.tolist()
- self.column_combobox.current(0)
- self.log_text.delete(1.0, tk.END)
- self.log_text.insert(tk.END, '文件预览:\n')
- self.log_text.insert(tk.END, df.to_string(index=False) + '\n\n')
- except Exception as e:
- messagebox.showerror('错误', f'读取Excel文件失败:{str(e)}')
-
- def save_log(self):
- filename = filedialog.asksaveasfilename(title='保存日志文件', defaultextension='.txt', filetypes=[('Text Files', '*.txt')])
- if filename:
- try:
- with open(filename, 'w', encoding='utf-8') as f:
- f.write(self.log_text.get(1.0, tk.END))
- messagebox.showinfo('成功', '日志已成功保存')
- except Exception as e:
- messagebox.showerror('错误', f'保存日志失败:{str(e)}')
-
- def start_verification(self):
- file_path = self.file_entry.get()
- column_name = self.column_combobox.get()
- if not file_path or not column_name:
- messagebox.showerror('错误', '请选择Excel文件和对比列')
- return
-
- try:
- df = pd.read_excel(file_path)
- file_names = df[column_name].dropna().tolist()
- file_names = [file_name.strip() for file_name in file_names if isinstance(file_name, str) and file_name.strip()]
- folder_path = os.path.dirname(file_path)
-
- self.log_text.delete(1.0, tk.END)
- self.log_text.insert(tk.END, '验证时间: ' + datetime.now().strftime('%Y-%m-%d %H:%M:%S') + '\n')
-
- total_count = len(file_names)
- valid_count = 0
-
- for idx, file_name in enumerate(file_names, 1):
- try:
- exists = os.path.exists(os.path.join(folder_path, file_name))
- status = '存在' if exists else '不存在'
- if exists:
- valid_count += 1
- self.log_text.insert(tk.END, f'{idx}. 文件名: {file_name} - {status}\n', 'valid' if status == '存在' else 'invalid')
- except Exception as e:
- self.log_text.insert(tk.END, f'{idx}. 文件名: {file_name} - 错误: {str(e)}\n')
-
- self.log_text.insert(tk.END, '\n验证结果:\n')
- self.log_text.insert(tk.END, f'总计:{total_count} 条路径\n')
- self.log_text.insert(tk.END, f'有效路径:{valid_count} 条\n')
- self.log_text.insert(tk.END, f'无效路径:{total_count - valid_count} 条\n')
-
- messagebox.showinfo('验证结果', f'总计:{total_count} 条路径\n有效路径:{valid_count} 条\n无效路径:{total_count - valid_count} 条')
-
- except Exception as e:
- messagebox.showerror('错误', f'验证过程中发生错误:{str(e)}')
-
- def show_help(self):
- help_text = '''使用步骤:
- 1. 准备一个Excel表,表里有一列是你要对比文件的完整路径
- 2. 选择导入事先准备的Excel表
- 3. 选择数据的对比列
- 4. 选择开始验证
- 5. 查看对比结果或输出txt日志查看'''
- messagebox.showinfo('使用帮助', help_text)
-
- if __name__ == '__main__':
- root = tk.Tk()
- app = VerifyFilesApp(root)
- root.mainloop()
复制代码
转载自吾爱破解,原作者:dl_hou
|