[Python] 纯文本查看 复制代码
import os
import tkinter as tk
from tkinter import filedialog, messagebox
class UltimateRenameTool:
def __init__(self, root):
self.root = root
self.root.title("终极文件重命名工具 (跨文件夹追加版)")
self.root.geometry("850x650") # 稍微调宽了一点以放下新按钮
self.root.eval('tk::PlaceWindow . center')
# 存储当前加载的文件信息
self.file_items = []
# 批量操作相关的变量
self.old_str_var = tk.StringVar()
self.new_str_var = tk.StringVar()
self.new_ext_var = tk.StringVar()
self.setup_ui()
def setup_ui(self):
# ================= 第一部分:文件选择 =================
top_frame = tk.Frame(self.root)
top_frame.pack(fill=tk.X, pady=10, padx=10)
# 改为“追加选择”
tk.Button(top_frame, text="📄 1. 追加选择需要修改的文件", command=self.select_files, bg="#e0f7fa", font=("微软雅黑", 10, "bold")).pack(side=tk.LEFT)
# 新增:清空列表按钮
tk.Button(top_frame, text="🗑️ 清空当前列表", command=self.clear_list, bg="#ffebee", font=("微软雅黑", 9)).pack(side=tk.LEFT, padx=10)
self.status_label = tk.Label(top_frame, text="当前未选择任何文件", fg="#666666")
self.status_label.pack(side=tk.LEFT, padx=10)
# ================= 第二部分:批量操作面板 (预览更改) =================
batch_frame = tk.LabelFrame(self.root, text="2. 批量快捷操作 (点击应用后将在下方预览,不会立即修改文件)", font=("微软雅黑", 9), padx=10, pady=10)
batch_frame.pack(fill=tk.X, padx=15, pady=5)
# 批量替换功能
row1 = tk.Frame(batch_frame)
row1.pack(fill=tk.X, pady=2)
tk.Label(row1, text="批量替换名称中的:").pack(side=tk.LEFT)
tk.Entry(row1, textvariable=self.old_str_var, width=12).pack(side=tk.LEFT, padx=5)
tk.Label(row1, text="替换为:").pack(side=tk.LEFT)
tk.Entry(row1, textvariable=self.new_str_var, width=12).pack(side=tk.LEFT, padx=5)
tk.Button(row1, text="应用替换", command=self.apply_batch_replace, bg="#f5f5f5").pack(side=tk.LEFT, padx=10)
# 批量修改后缀功能
row2 = tk.Frame(batch_frame)
row2.pack(fill=tk.X, pady=5)
tk.Label(row2, text="批量修改后缀为 (如 .png):").pack(side=tk.LEFT)
tk.Entry(row2, textvariable=self.new_ext_var, width=12).pack(side=tk.LEFT, padx=5)
tk.Button(row2, text="应用新后缀", command=self.apply_batch_extension, bg="#f5f5f5").pack(side=tk.LEFT, padx=10)
# ================= 第三部分:列表表头 =================
header_frame = tk.Frame(self.root)
header_frame.pack(fill=tk.X, padx=20, pady=(10, 0))
tk.Label(header_frame, text="当前文件名 (左侧)", font=("微软雅黑", 9, "bold"), width=35, anchor='w').pack(side=tk.LEFT)
tk.Label(header_frame, text="输入新的文件名 (右侧)", font=("微软雅黑", 9, "bold")).pack(side=tk.LEFT, padx=10)
# ================= 第四部分:可滚动的列表区 =================
list_container = tk.Frame(self.root, bd=1, relief=tk.SUNKEN)
list_container.pack(fill=tk.BOTH, expand=True, padx=15, pady=5)
self.canvas = tk.Canvas(list_container, highlightthickness=0)
scrollbar = tk.Scrollbar(list_container, orient="vertical", command=self.canvas.yview)
self.scrollable_frame = tk.Frame(self.canvas)
self.scrollable_frame.bind(
"<Configure>",
lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all"))
)
self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
self.canvas.configure(yscrollcommand=scrollbar.set)
self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
# ================= 第五部分:执行按钮 =================
bottom_frame = tk.Frame(self.root)
bottom_frame.pack(fill=tk.X, pady=15)
tk.Button(bottom_frame, text="⚡ 3. 确认无误,执行真实修改", command=self.execute_rename, bg="#ffecb3", font=("微软雅黑", 11, "bold"), width=30).pack()
# ---------------- 核心逻辑区 ----------------
def select_files(self):
"""打开文件选择器,并将选中的文件 追加 到滚动列表中"""
paths = filedialog.askopenfilenames(title="请按住 Ctrl 多选你要修改的文件 (可多次追加)")
if not paths:
return
# 提取当前已经在列表里的旧路径,防止重复添加
existing_paths = [item['old_path'] for item in self.file_items]
added_count = 0
# 渲染新选中的文件
for path in paths:
# 去重:如果文件已经在列表里,跳过
if path in existing_paths:
continue
dir_name, file_name = os.path.split(path)
row_frame = tk.Frame(self.scrollable_frame)
row_frame.pack(fill=tk.X, pady=3, padx=5)
# 左侧文字
tk.Label(row_frame, text=file_name, width=40, anchor='w', fg="#333333").pack(side=tk.LEFT)
# 右侧输入框
new_name_var = tk.StringVar(value=file_name)
entry = tk.Entry(row_frame, textvariable=new_name_var, width=45)
entry.pack(side=tk.LEFT, padx=10)
# 准备数据结构
item_data = {
'old_path': path,
'new_name_var': new_name_var,
'row_frame': row_frame
}
# 新增:剔除本行按钮
btn_remove = tk.Button(row_frame, text="❌", fg="red", relief=tk.FLAT,
command=lambda d=item_data: self.remove_item(d))
btn_remove.pack(side=tk.LEFT, padx=5)
# 加入全局列表
self.file_items.append(item_data)
added_count += 1
self.update_status_label()
def remove_item(self, item_data):
"""从列表中剔除单个文件"""
# 销毁UI组件
item_data['row_frame'].destroy()
# 从数据列表中移除
self.file_items.remove(item_data)
self.update_status_label()
def clear_list(self):
"""一键清空所有已选文件"""
if not self.file_items:
return
if messagebox.askyesno("确认", "确定要清空当前所有已选择的文件吗?"):
for item in self.file_items:
item['row_frame'].destroy()
self.file_items.clear()
self.update_status_label()
def update_status_label(self):
"""更新文件数量显示"""
count = len(self.file_items)
if count == 0:
self.status_label.config(text="当前未选择任何文件")
else:
self.status_label.config(text=f"已加载 {count} 个文件")
def apply_batch_replace(self):
"""将上方设置的替换规则,应用到右侧所有的输入框中(预览效果)"""
old_str = self.old_str_var.get()
new_str = self.new_str_var.get()
if not old_str:
messagebox.showinfo("提示", "请输入需要被替换的字符!")
return
for item in self.file_items:
current_new_name = item['new_name_var'].get()
updated_name = current_new_name.replace(old_str, new_str)
item['new_name_var'].set(updated_name)
def apply_batch_extension(self):
"""将上方设置的新后缀,应用到右侧所有的输入框中(预览效果)"""
new_ext = self.new_ext_var.get().strip()
if not new_ext:
messagebox.showinfo("提示", "请输入新的后缀名,例如 .png 或 .txt")
return
if not new_ext.startswith("."):
new_ext = "." + new_ext
for item in self.file_items:
current_new_name = item['new_name_var'].get()
base_name, _ = os.path.splitext(current_new_name)
item['new_name_var'].set(base_name + new_ext)
def execute_rename(self):
"""遍历列表,将名称发生改变的文件进行真实的系统重命名"""
if not self.file_items:
messagebox.showwarning("提示", "列表为空,请先选择文件!")
return
success_count = 0
error_messages = []
for item in self.file_items:
old_path = item['old_path']
new_file_name = item['new_name_var'].get().strip()
dir_name, old_file_name = os.path.split(old_path)
if new_file_name == old_file_name or not new_file_name:
continue
new_path = os.path.join(dir_name, new_file_name)
try:
if os.path.exists(new_path) and new_path.lower() != old_path.lower():
error_messages.append(f"跳过: '{new_file_name}' 已存在。")
continue
os.rename(old_path, new_path)
item['old_path'] = new_path
success_count += 1
except Exception as e:
error_messages.append(f"失败: '{old_file_name}' -> {str(e)}")
# 为了避免重新选文件,修改成功后刷新列表展示的新老名字一致
for item in self.file_items:
_, current_name = os.path.split(item['old_path'])
item['new_name_var'].set(current_name)
# 更新左侧UI显示的标签
for child in item['row_frame'].winfo_children():
if isinstance(child, tk.Label):
child.config(text=current_name)
break
if error_messages:
error_text = "\n".join(error_messages[:5])
if len(error_messages) > 5:
error_text += "\n..."
messagebox.showwarning("部分完成", f"成功修改 {success_count} 个文件。\n\n以下出现问题:\n{error_text}")
elif success_count > 0:
messagebox.showinfo("完成", f"🎉 成功修改 {success_count} 个文件!")
else:
messagebox.showinfo("提示", "没有检测到任何名称的更改。")
if __name__ == "__main__":
root = tk.Tk()
app = UltimateRenameTool(root)
root.mainloop()