#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 批量文件重命名工具 支持添加文件,按顺序重命名 """ import os import sys import tkinter as tk from tkinter import Label, Button, Entry, messagebox, Frame, Scrollbar, Listbox, VERTICAL, HORIZONTAL, END, filedialog class BatchRenameApp: def __init__(self, root): self.root = root self.root.title("批量文件重命名工具") self.root.geometry("750x800") self.root.resizable(True, True) # 初始化变量 self.file_list = [] self.new_prefix = "renamed" self.start_number = 1 self.number_digits = 3 # 创建界面 self.create_widgets() def create_widgets(self): # 主框架 main_frame = Frame(self.root, padx=20, pady=20) main_frame.pack(fill="both", expand=True) # 提示区域 drop_frame = Frame(main_frame, pady=10, bg="#E3F2FD", relief="solid", bd=2) drop_frame.pack(fill="x") self.drop_label = Label(drop_frame, text='📁 点击下方"添加文件"按钮选择文件', font=("SimHei", 14, "bold"), bg="#E3F2FD", fg="#1976D2", height=2) self.drop_label.pack(fill="x", expand=True) # 按钮区域 btn_frame = Frame(main_frame, pady=10) btn_frame.pack(fill="x") Button(btn_frame, text="添加文件", command=self.add_files, font=("SimHei", 10), width=12).pack(side="left", padx=5) Button(btn_frame, text="清空列表", command=self.clear_list, font=("SimHei", 10), width=12).pack(side="left", padx=5) Button(btn_frame, text="上移", command=self.move_up, font=("SimHei", 10), width=8).pack(side="left", padx=5) Button(btn_frame, text="下移", command=self.move_down, font=("SimHei", 10), width=8).pack(side="left", padx=5) Button(btn_frame, text="移除选中", command=self.remove_selected, font=("SimHei", 10), width=10).pack(side="left", padx=5) # 文件列表 list_frame = Frame(main_frame, pady=10) list_frame.pack(fill="both", expand=True) Label(list_frame, text="文件列表 (按顺序重命名):", font=("SimHei", 10, "bold")).pack(anchor="w") # 列表框容器 list_container = Frame(list_frame) list_container.pack(fill="both", expand=True, pady=5) # 文件列表 self.file_listbox = Listbox(list_container, width=80, height=12, selectmode="single") self.file_listbox.pack(side="left", fill="both", expand=True) # 垂直滚动条 vscrollbar = Scrollbar(list_container, orient=VERTICAL, command=self.file_listbox.yview) vscrollbar.pack(side="right", fill="y") self.file_listbox.config(yscrollcommand=vscrollbar.set) # 水平滚动条 hscrollbar = Scrollbar(list_container, orient=HORIZONTAL, command=self.file_listbox.xview) hscrollbar.pack(side="bottom", fill="x") self.file_listbox.config(xscrollcommand=hscrollbar.set) # 重命名设置 settings_frame = Frame(main_frame, pady=10) settings_frame.pack(fill="x") Label(settings_frame, text="重命名设置:", font=("SimHei", 10, "bold")).pack(anchor="w") # 前缀 prefix_frame = Frame(settings_frame) prefix_frame.pack(fill="x", pady=5) Label(prefix_frame, text="新名称前缀:", width=15).pack(side="left", padx=5) self.prefix_entry = Entry(prefix_frame, width=40) self.prefix_entry.insert(0, self.new_prefix) self.prefix_entry.pack(side="left", padx=5) # 起始编号 start_frame = Frame(settings_frame) start_frame.pack(fill="x", pady=5) Label(start_frame, text="起始编号:", width=15).pack(side="left", padx=5) self.start_entry = Entry(start_frame, width=10) self.start_entry.insert(0, str(self.start_number)) self.start_entry.pack(side="left", padx=5) # 编号位数 digits_frame = Frame(settings_frame) digits_frame.pack(fill="x", pady=5) Label(digits_frame, text="编号位数:", width=15).pack(side="left", padx=5) self.digits_entry = Entry(digits_frame, width=10) self.digits_entry.insert(0, str(self.number_digits)) self.digits_entry.pack(side="left", padx=5) Label(digits_frame, text="(例如: 3位 = 001, 002...)").pack(side="left", padx=5) # 执行按钮 button_frame = Frame(main_frame, pady=15) button_frame.pack(fill="x") Button(button_frame, text="预览重命名", command=self.preview_rename, font=("SimHei", 11), width=15).pack(side="left", padx=10) Button(button_frame, text="执行重命名", command=self.execute_rename, font=("SimHei", 11, "bold"), width=15, bg="#4CAF50", fg="white").pack(side="left", padx=10) # 结果显示 result_frame = Frame(main_frame, pady=10) result_frame.pack(fill="both", expand=True) Label(result_frame, text="重命名结果:", font=("SimHei", 10, "bold")).pack(anchor="w") # 结果列表 result_container = Frame(result_frame) result_container.pack(fill="both", expand=True) self.result_listbox = Listbox(result_container, width=80, height=8) self.result_listbox.pack(side="left", fill="both", expand=True) # 垂直滚动条 rvscrollbar = Scrollbar(result_container, orient=VERTICAL, command=self.result_listbox.yview) rvscrollbar.pack(side="right", fill="y") self.result_listbox.config(yscrollcommand=rvscrollbar.set) # 水平滚动条 rhscrollbar = Scrollbar(result_container, orient=HORIZONTAL, command=self.result_listbox.xview) rhscrollbar.pack(side="bottom", fill="x") self.result_listbox.config(xscrollcommand=rhscrollbar.set) def add_files(self): """通过对话框添加文件""" files = filedialog.askopenfilenames(title="选择文件") if files: for file_path in files: if file_path not in self.file_list: self.file_list.append(file_path) file_name = os.path.basename(file_path) self.file_listbox.insert(END, f"{len(self.file_list)}. {file_name}") self.drop_label.config(text=f"📁 已添加 {len(self.file_list)} 个文件") def clear_list(self): """清空文件列表""" self.file_list.clear() self.file_listbox.delete(0, END) self.drop_label.config(text='📁 点击下方"添加文件"按钮选择文件') def move_up(self): """上移选中的文件""" selection = self.file_listbox.curselection() if not selection: return index = selection[0] if index == 0: return # 交换列表中的位置 self.file_list[index], self.file_list[index-1] = self.file_list[index-1], self.file_list[index] # 更新显示 self.refresh_listbox() self.file_listbox.selection_set(index-1) def move_down(self): """下移选中的文件""" selection = self.file_listbox.curselection() if not selection: return index = selection[0] if index == len(self.file_list) - 1: return # 交换列表中的位置 self.file_list[index], self.file_list[index+1] = self.file_list[index+1], self.file_list[index] # 更新显示 self.refresh_listbox() self.file_listbox.selection_set(index+1) def remove_selected(self): """移除选中的文件""" selection = self.file_listbox.curselection() if not selection: return index = selection[0] self.file_list.pop(index) self.refresh_listbox() if self.file_list: self.drop_label.config(text=f"📁 已添加 {len(self.file_list)} 个文件") else: self.drop_label.config(text='📁 点击下方"添加文件"按钮选择文件') def refresh_listbox(self): """刷新列表框显示""" self.file_listbox.delete(0, END) for i, file_path in enumerate(self.file_list, 1): file_name = os.path.basename(file_path) self.file_listbox.insert(END, f"{i}. {file_name}") def format_number(self, num, digits): """格式化数字,添加前导零""" return str(num).zfill(digits) def preview_rename(self): """预览重命名操作""" if not self.file_list: messagebox.showinfo("提示", "文件列表为空") return self.new_prefix = self.prefix_entry.get().strip() try: self.start_number = int(self.start_entry.get().strip()) self.number_digits = int(self.digits_entry.get().strip()) if self.start_number < 0: raise ValueError("起始编号不能为负数") if self.number_digits < 1: raise ValueError("编号位数必须大于0") except ValueError as e: messagebox.showerror("错误", f"参数设置错误: {e}") return # 清空结果列表 self.result_listbox.delete(0, END) current_num = self.start_number for file_path in self.file_list: old_name = os.path.basename(file_path) ext = os.path.splitext(old_name)[1] formatted_num = self.format_number(current_num, self.number_digits) new_name = f"{self.new_prefix}{formatted_num}{ext}" self.result_listbox.insert(END, f"{old_name} -> {new_name}") current_num += 1 messagebox.showinfo("预览", f"预览完成!\n共 {len(self.file_list)} 个文件将被重命名") def execute_rename(self): """执行重命名操作""" if not self.file_list: messagebox.showinfo("提示", "文件列表为空") return self.new_prefix = self.prefix_entry.get().strip() try: self.start_number = int(self.start_entry.get().strip()) self.number_digits = int(self.digits_entry.get().strip()) if self.start_number < 0: raise ValueError("起始编号不能为负数") if self.number_digits < 1: raise ValueError("编号位数必须大于0") except ValueError as e: messagebox.showerror("错误", f"参数设置错误: {e}") return # 确认操作 confirm = messagebox.askyesno("确认", f"确定要重命名 {len(self.file_list)} 个文件吗?") if not confirm: return # 清空结果列表 self.result_listbox.delete(0, END) success_count = 0 skip_count = 0 current_num = self.start_number for file_path in self.file_list: old_name = os.path.basename(file_path) ext = os.path.splitext(old_name)[1] formatted_num = self.format_number(current_num, self.number_digits) new_name = f"{self.new_prefix}{formatted_num}{ext}" # 获取文件所在目录 dir_path = os.path.dirname(file_path) new_path = os.path.join(dir_path, new_name) if os.path.exists(new_path): if old_name.lower() != new_name.lower(): self.result_listbox.insert(END, f"[跳过] 目标已存在: {new_name}") skip_count += 1 else: try: os.rename(file_path, new_path) self.result_listbox.insert(END, f"[成功] {old_name} -> {new_name}") success_count += 1 except Exception as e: self.result_listbox.insert(END, f"[失败] {old_name} - {e}") skip_count += 1 current_num += 1 messagebox.showinfo("完成", f"重命名完成!\n成功: {success_count} 个\n失败/跳过: {skip_count} 个") # 清空列表 self.clear_list() def main(): """主函数""" root = tk.Tk() app = BatchRenameApp(root) root.mainloop() if __name__ == "__main__": main()