294 lines
12 KiB
Python
294 lines
12 KiB
Python
#!/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() |