上次的文件批量重命名使用起来太麻烦,改进了一下
- 添加界面使用更方便
- 添加到右键菜单打开
- 添加正则匹配
- 添加修改历史记录,并支持撤销
先看效果
源代码
import os
import re
import sys
import time
import winreg
import tkinter as tk
from tkinter.filedialog import askdirectory
CUR_PATH = os.getcwd()
CUR_PROG = os.path.abspath(sys.argv[0])
HISTORY = []
def create_reg(path=CUR_PROG):
"""添加右键菜单注册表项"""
key = winreg.CreateKeyEx(winreg.HKEY_CLASSES_ROOT, "Directory\\Background\\shell\\rename", 0, winreg.KEY_SET_VALUE)
winreg.SetValueEx(key, "", 0, winreg.REG_SZ, "批量重命名")
winreg.SetValueEx(key, "Icon", 0, winreg.REG_SZ, path)
winreg.SetValue(key, "command", winreg.REG_SZ, path)
# 关闭注册表键
winreg.CloseKey(key)
def delete_reg(path):
"""删除右键菜单注册表项"""
winreg.DeleteKey(winreg.HKEY_CLASSES_ROOT, "Directory\\Background\\shell\\rename\\command")
winreg.DeleteKey(winreg.HKEY_CLASSES_ROOT, "Directory\\Background\\shell\\rename")
print("删除注册表成功")
def replace_name(src: str, dst: str, count: int = -1, depth: int = 0, reg=False):
success = []
error = []
# 遍历当前目录下的所有文件
try:
for root, dirs, files in os.walk(CUR_PATH, topdown=True):
if root.count(os.sep) - CUR_PATH.count(os.sep) > 0 and depth==0:
continue
for name in files:
if reg:
pattern = re.compile(rf"{src}", re.S)
new_name = pattern.sub(dst, name, count=0 if count < 0 else count)
print(pattern)
print(new_name)
else:
new_name = name.replace(src, dst, count)
src_name = os.path.join(root, name)
dst_name = os.path.join(root, new_name)
try:
if src_name != dst_name:
os.rename(src_name, dst_name)
success.append([src_name, dst_name])
except Exception as e:
error.append([src_name, dst_name])
finally:
global HISTORY
HISTORY.append([{"src": item[0], "dst": item[1]} for item in success])
return success, error
def main():
def replace():
"""在当前目录下批量重命名文件"""
src = src_entry.get()
dst = dst_entry.get()
success, error = replace_name(src, dst, depth=dir_mode.get(), reg=reg_mode.get())
error_info = "\n\n".join(["\n".join(item) for item in error])
success_info = "\n\n".join(["\n".join(item) for item in success])
history = f"\n\n{'='*20}{time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())}{'='*20}\n失败:{len(error)}\n{error_info}\n成功:{len(success)}\n{success_info}"
history_entry.insert(tk.END, history)
if len(HISTORY) > 0:
undo_button.config(state=tk.NORMAL)
def selectPath():
path_ = askdirectory() #使用askdirectory()方法返回文件夹的路径
if path_ == "":
path.get() #当打开文件路径选择框后点击"取消" 输入框会清空路径,所以使用get()方法再获取一次路径
else:
path_ = path_.replace("/", "\\") # 实际在代码中执行的路径为“\“ 所以替换一下
global CUR_PATH
CUR_PATH = path_
path.set(path_)
def openPath():
dir = os.path.dirname(path.get()+"\\")
os.system('start ' + dir)
def undo():
"""撤销重命名"""
cur = HISTORY.pop()
for item in cur:
os.rename(item.get("dst"), item.get("src"))
if len(HISTORY) == 0:
undo_button.config(state=tk.DISABLED)
root = tk.Tk()
root.title("批量重命名")
root.geometry("500x300")
# 设置窗口最大大小
root.minsize(400, 200)
root.maxsize(800, 600)
path = tk.StringVar()
path.set(CUR_PATH)
row1 = tk.Frame(root, height=30)
row2 = tk.Frame(root, height=30)
row3 = tk.Frame(root, height=30)
row4 = tk.Frame(root, height=30)
row5 = tk.Frame(root, height=30)
row1.pack(fill="x", pady=5)
row2.pack(fill="x", pady=5)
row3.pack(fill="x", pady=5)
row4.pack(fill="x", pady=5)
row5.pack(fill="x", pady=5)
# 文件夹层级模式
dir_mode = tk.IntVar(value=0)
dir_label = tk.Label(row1, text='文件夹层级: ')
dir_radio1 = tk.Radiobutton(row1, text='当前', value=0, variable=dir_mode, relief=tk.RAISED)
dir_radio2 = tk.Radiobutton(row1, text='递归', value=1, variable=dir_mode, relief=tk.RAISED)
dir_label.pack(side=tk.LEFT, padx=5)
dir_radio1.pack(side=tk.LEFT, padx=0)
dir_radio2.pack(side=tk.LEFT, padx=5)
# 替换模式
reg_mode = tk.IntVar(value=0)
reg_label = tk.Label(row1, text='替换模式: ')
reg_radio1 = tk.Radiobutton(row1, text='普通', value=0, variable=reg_mode, relief=tk.RAISED)
reg_radio2 = tk.Radiobutton(row1, text='正则', value=1, variable=reg_mode, relief=tk.RAISED)
reg_label.pack(side=tk.LEFT, padx=5)
reg_radio1.pack(side=tk.LEFT, padx=0)
reg_radio2.pack(side=tk.LEFT, padx=5)
# 加入右键菜单
add_menu_button = tk.Button(row1, text="加入右键菜单", command=create_reg)
add_menu_button.pack(side=tk.RIGHT, anchor=tk.E, padx=5) # 0行0列
# 目标路径
path_label = tk.Label(row2, text="目标路径:")
path_entry = tk.Entry(row2, textvariable=path, state="readonly")
path_sbtn = tk.Button(row2, text="路径选择", command=selectPath)
path_obtn = tk.Button(row2, text="打开文件位置", command=openPath)
path_label.pack(side=tk.LEFT, padx=5)
path_entry.pack(side=tk.LEFT, padx=5, expand=True, fill="x")
path_sbtn.pack(side=tk.LEFT, padx=5)
path_obtn.pack(side=tk.LEFT, padx=5)
# 源字符串 目标字符串
src_label = tk.Label(row3, text="源字符:")
dst_label = tk.Label(row3, text="目标字符:")
src_entry = tk.Entry(row3)
dst_entry = tk.Entry(row3)
src_label.pack(side=tk.LEFT, padx=5)
src_entry.pack(side=tk.LEFT, padx=5, expand=True, fill="x")
dst_label.pack(side=tk.LEFT, padx=5)
dst_entry.pack(side=tk.LEFT, padx=5, expand=True, fill="x")
# 提交按扭
undo_button = tk.Button(row4, text="撤销", state=tk.DISABLED, command=undo)
submit_button = tk.Button(row4, text="开始替换", command=replace)
submit_button.pack(side=tk.RIGHT, padx=5)
undo_button.pack(side=tk.RIGHT, padx=5)
# 操作记录
history_entry = tk.Text(row5)
history_entry.pack(side=tk.TOP, padx=5)
root.mainloop()
if __name__ == "__main__":
main()
打包
pyinstaller -Fw rename.py
加入右键菜单需要以管理员权限打开