#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 图形界面文件重命名工具 """ import tkinter as tk from tkinter import ttk, filedialog, messagebox, scrolledtext import os import threading from file_renamer import FileRenamer class GuiRenamer: def __init__(self, root): self.root = root self.root.title("文件重命名工具") self.root.geometry("800x600") # 创建重命名工具实例 self.renamer = FileRenamer() self.renamer.load_history() # 当前选择的目录 self.current_directory = "" # 创建界面 self.create_widgets() def create_widgets(self): """创建界面组件""" # 主框架 main_frame = ttk.Frame(self.root, padding="10") main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # 配置网格权重 self.root.columnconfigure(0, weight=1) self.root.rowconfigure(0, weight=1) main_frame.columnconfigure(1, weight=1) main_frame.rowconfigure(4, weight=1) # 目录选择 ttk.Label(main_frame, text="选择目录:").grid(row=0, column=0, sticky=tk.W, pady=5) self.dir_var = tk.StringVar() self.dir_entry = ttk.Entry(main_frame, textvariable=self.dir_var, width=50) self.dir_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(5, 0), pady=5) ttk.Button(main_frame, text="浏览", command=self.browse_directory).grid(row=0, column=2, padx=(5, 0), pady=5) # 分隔线 ttk.Separator(main_frame, orient=tk.HORIZONTAL).grid(row=1, column=0, columnspan=3, sticky=(tk.W, tk.E), pady=10) # 重命名选项卡 self.notebook = ttk.Notebook(main_frame) self.notebook.grid(row=2, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5) # 简单替换选项卡 self.create_replace_tab() # 正则表达式选项卡 self.create_regex_tab() # 添加前缀/后缀选项卡 self.create_prefix_suffix_tab() # 序号选项卡 self.create_enumerate_tab() # 大小写选项卡 self.create_case_tab() # 预览按钮 self.preview_btn = ttk.Button(main_frame, text="预览", command=self.preview_rename) self.preview_btn.grid(row=3, column=1, sticky=tk.E, pady=10) # 执行按钮 self.execute_btn = ttk.Button(main_frame, text="执行重命名", command=self.execute_rename) self.execute_btn.grid(row=3, column=2, padx=(5, 0), pady=10) # 结果显示区域 result_frame = ttk.LabelFrame(main_frame, text="结果", padding="5") result_frame.grid(row=4, column=0, columnspan=3, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5) result_frame.columnconfigure(0, weight=1) result_frame.rowconfigure(0, weight=1) self.result_text = scrolledtext.ScrolledText(result_frame, wrap=tk.WORD, height=15) self.result_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) # 撤销按钮 self.undo_btn = ttk.Button(main_frame, text="撤销上次操作", command=self.undo_last_rename) self.undo_btn.grid(row=5, column=0, pady=10) # 清空结果按钮 ttk.Button(main_frame, text="清空结果", command=self.clear_result).grid(row=5, column=2, pady=10) def create_replace_tab(self): """创建简单替换选项卡""" frame = ttk.Frame(self.notebook, padding="10") frame.columnconfigure(1, weight=1) ttk.Label(frame, text="查找:").grid(row=0, column=0, sticky=tk.W, pady=5) self.replace_search_var = tk.StringVar() ttk.Entry(frame, textvariable=self.replace_search_var, width=30).grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(5, 0), pady=5) ttk.Label(frame, text="替换为:").grid(row=1, column=0, sticky=tk.W, pady=5) self.replace_replace_var = tk.StringVar() ttk.Entry(frame, textvariable=self.replace_replace_var, width=30).grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(5, 0), pady=5) self.notebook.add(frame, text="简单替换") self.replace_frame = frame def create_regex_tab(self): """创建正则表达式选项卡""" frame = ttk.Frame(self.notebook, padding="10") frame.columnconfigure(1, weight=1) ttk.Label(frame, text="正则表达式:").grid(row=0, column=0, sticky=tk.W, pady=5) self.regex_pattern_var = tk.StringVar() ttk.Entry(frame, textvariable=self.regex_pattern_var, width=30).grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(5, 0), pady=5) ttk.Label(frame, text="替换为:").grid(row=1, column=0, sticky=tk.W, pady=5) self.regex_replace_var = tk.StringVar() ttk.Entry(frame, textvariable=self.regex_replace_var, width=30).grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(5, 0), pady=5) # 正则表达式说明 help_text = "常用正则表达式:\n\\d+ 匹配数字\n[a-zA-Z]+ 匹配字母\n.+ 匹配任意字符\n^ 匹配开头\n$ 匹配结尾" ttk.Label(frame, text=help_text, foreground="gray").grid(row=2, column=0, columnspan=2, sticky=tk.W, pady=(10, 0)) self.notebook.add(frame, text="正则表达式") self.regex_frame = frame def create_prefix_suffix_tab(self): """创建前缀/后缀选项卡""" frame = ttk.Frame(self.notebook, padding="10") frame.columnconfigure(1, weight=1) ttk.Label(frame, text="前缀:").grid(row=0, column=0, sticky=tk.W, pady=5) self.prefix_var = tk.StringVar() ttk.Entry(frame, textvariable=self.prefix_var, width=30).grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(5, 0), pady=5) ttk.Label(frame, text="后缀:").grid(row=1, column=0, sticky=tk.W, pady=5) self.suffix_var = tk.StringVar() ttk.Entry(frame, textvariable=self.suffix_var, width=30).grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(5, 0), pady=5) self.notebook.add(frame, text="前缀/后缀") self.prefix_suffix_frame = frame def create_enumerate_tab(self): """创建序号选项卡""" frame = ttk.Frame(self.notebook, padding="10") frame.columnconfigure(1, weight=1) ttk.Label(frame, text="前缀:").grid(row=0, column=0, sticky=tk.W, pady=5) self.enum_prefix_var = tk.StringVar() ttk.Entry(frame, textvariable=self.enum_prefix_var, width=30).grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(5, 0), pady=5) ttk.Label(frame, text="起始数字:").grid(row=1, column=0, sticky=tk.W, pady=5) self.enum_start_var = tk.StringVar(value="1") ttk.Entry(frame, textvariable=self.enum_start_var, width=30).grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(5, 0), pady=5) ttk.Label(frame, text="数字位数:").grid(row=2, column=0, sticky=tk.W, pady=5) self.enum_digits_var = tk.StringVar(value="3") ttk.Entry(frame, textvariable=self.enum_digits_var, width=30).grid(row=2, column=1, sticky=(tk.W, tk.E), padx=(5, 0), pady=5) self.notebook.add(frame, text="添加序号") self.enumerate_frame = frame def create_case_tab(self): """创建大小写选项卡""" frame = ttk.Frame(self.notebook, padding="10") ttk.Label(frame, text="选择大小写格式:").grid(row=0, column=0, sticky=tk.W, pady=5) self.case_var = tk.StringVar(value="lower") case_frame = ttk.Frame(frame) case_frame.grid(row=1, column=0, sticky=(tk.W, tk.E), pady=5) ttk.Radiobutton(case_frame, text="小写 (lowercase)", variable=self.case_var, value="lower").pack(anchor=tk.W) ttk.Radiobutton(case_frame, text="大写 (UPPERCASE)", variable=self.case_var, value="upper").pack(anchor=tk.W) ttk.Radiobutton(case_frame, text="首字母大写 (Title Case)", variable=self.case_var, value="title").pack(anchor=tk.W) self.notebook.add(frame, text="大小写") self.case_frame = frame def browse_directory(self): """浏览目录""" directory = filedialog.askdirectory() if directory: self.dir_var.set(directory) self.current_directory = directory def preview_rename(self): """预览重命名""" self.perform_rename(preview=True) def execute_rename(self): """执行重命名""" self.perform_rename(preview=False) def perform_rename(self, preview=True): """执行重命名操作""" # 获取当前目录 directory = self.dir_var.get().strip() if not directory: messagebox.showerror("错误", "请选择一个目录") return if not os.path.exists(directory): messagebox.showerror("错误", "选择的目录不存在") return self.current_directory = directory # 获取当前选中的选项卡 current_tab = self.notebook.index(self.notebook.select()) # 在新线程中执行重命名以避免界面冻结 thread = threading.Thread(target=self._perform_rename_thread, args=(current_tab, preview)) thread.daemon = True thread.start() def _perform_rename_thread(self, tab_index, preview): """在后台线程中执行重命名""" try: # 根据选项卡索引执行不同的重命名操作 if tab_index == 0: # 简单替换 search_pattern = self.replace_search_var.get() replace_pattern = self.replace_replace_var.get() count = self.renamer.simple_rename(self.current_directory, search_pattern, replace_pattern, preview) elif tab_index == 1: # 正则表达式 regex_pattern = self.regex_pattern_var.get() replace_pattern = self.regex_replace_var.get() count = self.renamer.regex_rename(self.current_directory, regex_pattern, replace_pattern, preview) elif tab_index == 2: # 前缀/后缀 prefix = self.prefix_var.get() suffix = self.suffix_var.get() count = 0 if prefix: count += self.renamer.add_prefix(self.current_directory, prefix, preview) if suffix: count += self.renamer.add_suffix(self.current_directory, suffix, preview) elif tab_index == 3: # 序号 prefix = self.enum_prefix_var.get() try: start = int(self.enum_start_var.get() or "1") digits = int(self.enum_digits_var.get() or "3") count = self.renamer.enumerate_files(self.current_directory, prefix, start, digits, preview) except ValueError: self.show_result("错误: 起始数字或数字位数必须是整数") return elif tab_index == 4: # 大小写 case_type = self.case_var.get() count = self.renamer.change_case(self.current_directory, case_type, preview) # 显示结果 action = "预览" if preview else "重命名" self.show_result(f"{action}完成,共处理了 {count} 个文件") except Exception as e: self.show_result(f"操作失败: {str(e)}") def show_result(self, message): """在结果区域显示信息""" self.result_text.insert(tk.END, f"{message}\n") self.result_text.see(tk.END) def clear_result(self): """清空结果区域""" self.result_text.delete(1.0, tk.END) def undo_last_rename(self): """撤销上次重命名操作""" try: if self.renamer.undo_last_rename(): self.show_result("成功撤销上次重命名操作") else: self.show_result("没有可撤销的操作") except Exception as e: self.show_result(f"撤销操作失败: {str(e)}") def main(): """主函数""" root = tk.Tk() app = GuiRenamer(root) root.mainloop() if __name__ == "__main__": main()