设为首页收藏本站

 找回密码
 立即注册

只需一步,快速开始

搜索
查看: 106|回复: 8

傻瓜式批量重命名工具

  [复制链接]
累计签到:16 天
连续签到:5 天
灌水成绩
11
18
2309
主题
帖子
积分

等级头衔

ID : 675

初级技术员

积分成就 测量币 : 2309
在线时间 : 0 小时
注册时间 : 2026-2-13
最后登录 : 2026-5-11

勋章
UID勋章测量学徒测量员
发表于 2026-4-15 03:24:54 | 显示全部楼层 |阅读模式 IP:北京
🔥 零学习成本・一键操作・支持回退🌟 工具亮点


  • 超级简单:选文件夹 → 输前缀 → 点开始,三步搞定

  • 自动规整:文件名自动按数字排序,长短不一也整齐

  • 实时预览:改前改后看得见,避免改错

  • 安全可撤回:自动保存记录,误操作一键恢复原名
    干净无残留:日志自动隐藏,不污染文件夹


🚀 使用步骤(真・傻瓜式)


  • 选择要重命名的文件夹

  • 输入自定义前缀(默认:文件_)

  • 切换排序方式 / 筛选文件类型(可选)

  • 点击开始重命名,等待完成
    如需恢复,点击撤销重命名即可
📋 功能说明


  • 支持图片 / 视频 / 文档 / 表格 分类筛选

  • 自动补全数字位数(01、001、0001)

  • 文件夹内容变化实时同步更新

  • 预览采用表格形式,清晰整洁
    日志文件自动隐藏,永不参与重命名
下载地址:exe地址:rgbrgb[Python] 纯文本查看 复制代码import osimport tkinter as tkfrom tkinter import ttk, filedialog, messageboximport ctypesclass BatchRenameApp:    def __init__(self, root):        self.root = root        self.root.title(&quot;批量重命名工具&quot;)        self.root.geometry(&quot;780x780&quot;)        self.root.resizable(True, True)        self.root.configure(bg=&quot;#f5f5f5&quot;)        self.center_window()        self.folder_path = tk.StringVar()        self.prefix = tk.StringVar(value=&quot;文件_&quot;)        self.sort_type = tk.StringVar(value=&quot;name&quot;)        self.file_type = tk.StringVar(value=&quot;全部&quot;)        self.file_list = []        self.log_file = &quot;.rename_log.txt&quot;        self.last_folder_mtime = 0        self.setup_style()        self.create_widgets()        self.start_folder_monitor()    def center_window(self):        self.root.update_idletasks()        w = self.root.winfo_width()        h = self.root.winfo_height()        x = (self.root.winfo_screenwidth() // 2) - (w // 2)        y = (self.root.winfo_screenheight() // 2) - (h // 2)        self.root.geometry(f&quot;{w}x{h}+{x}+{y}&quot;)    def setup_style(self):        style = ttk.Style()        style.theme_use('clam')        style.configure('Modern.TButton', font=('微软雅黑', 10), background=&quot;#4285F4&quot;, foreground=&quot;white&quot;, padding=8)        style.map('Modern.TButton', background=[('active', '#3367D6')])        style.configure('Modern.TLabel', font=('微软雅黑', 10), background=&quot;#f5f5f5&quot;)        style.configure('Title.TLabel', font=('微软雅黑', 16, 'bold'), background=&quot;#f5f5f5&quot;, foreground=&quot;#202124&quot;)        style.configure('TRadiobutton', font=('微软雅黑', 10), background=&quot;#f5f5f5&quot;)        style.configure(&quot;Treeview.Heading&quot;, font=('微软雅黑', 10, 'bold'))        style.configure(&quot;Treeview&quot;, font=('微软雅黑', 10), rowheight=25)    def create_widgets(self):        title_label = ttk.Label(self.root, text=&quot;批量重命名工具&quot;, style=&quot;Title.TLabel&quot;)        title_label.pack(pady=20)        folder_frame = tk.Frame(self.root, bg=&quot;#f5f5f5&quot;)        folder_frame.pack(fill=tk.X, padx=30, pady=6)        ttk.Label(folder_frame, text=&quot;目标文件夹:&quot;, style=&quot;Modern.TLabel&quot;).pack(side=tk.LEFT)        self.folder_entry = ttk.Entry(folder_frame, textvariable=self.folder_path, font=('微软雅黑', 10), width=45)        self.folder_entry.pack(side=tk.LEFT, padx=10)        ttk.Button(folder_frame, text=&quot;选择文件夹&quot;, command=self.select_folder, style='Modern.TButton').pack(side=tk.LEFT)        prefix_frame = tk.Frame(self.root, bg=&quot;#f5f5f5&quot;)        prefix_frame.pack(fill=tk.X, padx=30, pady=6)        ttk.Label(prefix_frame, text=&quot;文件前缀:&quot;, style=&quot;Modern.TLabel&quot;).pack(side=tk.LEFT)        prefix_entry = ttk.Entry(prefix_frame, textvariable=self.prefix, font=('微软雅黑', 10), width=30)        prefix_entry.pack(side=tk.LEFT, padx=10)        prefix_entry.bind('', lambda e: self.update_preview())        sort_frame = tk.Frame(self.root, bg=&quot;#f5f5f5&quot;)        sort_frame.pack(fill=tk.X, padx=30, pady=6)        ttk.Label(sort_frame, text=&quot;排序方式:&quot;, style=&quot;Modern.TLabel&quot;).pack(side=tk.LEFT, padx=0)        ttk.Radiobutton(sort_frame, text=&quot;按名称&quot;, variable=self.sort_type, value=&quot;name&quot;, command=self.load_files).pack(side=tk.LEFT, padx=8)        ttk.Radiobutton(sort_frame, text=&quot;按修改时间&quot;, variable=self.sort_type, value=&quot;mtime&quot;, command=self.load_files).pack(side=tk.LEFT, padx=8)        type_frame = tk.Frame(self.root, bg=&quot;#f5f5f5&quot;)        type_frame.pack(fill=tk.X, padx=30, pady=6)        ttk.Label(type_frame, text=&quot;文件类型:&quot;, style=&quot;Modern.TLabel&quot;).pack(side=tk.LEFT, padx=0)        ttk.Radiobutton(type_frame, text=&quot;全部&quot;, variable=self.file_type, value=&quot;全部&quot;, command=self.load_files).pack(side=tk.LEFT, padx=6)        ttk.Radiobutton(type_frame, text=&quot;图片&quot;, variable=self.file_type, value=&quot;图片&quot;, command=self.load_files).pack(side=tk.LEFT, padx=6)        ttk.Radiobutton(type_frame, text=&quot;视频&quot;, variable=self.file_type, value=&quot;视频&quot;, command=self.load_files).pack(side=tk.LEFT, padx=6)        ttk.Radiobutton(type_frame, text=&quot;文档&quot;, variable=self.file_type, value=&quot;文档&quot;, command=self.load_files).pack(side=tk.LEFT, padx=6)        ttk.Radiobutton(type_frame, text=&quot;表格&quot;, variable=self.file_type, value=&quot;表格&quot;, command=self.load_files).pack(side=tk.LEFT, padx=6)        info_frame = tk.Frame(self.root, bg=&quot;#f5f5f5&quot;)        info_frame.pack(fill=tk.X, padx=30, pady=6)        self.file_count_label = ttk.Label(info_frame, text=&quot;文件数量:0 个&quot;, style=&quot;Modern.TLabel&quot;)        self.file_count_label.pack(side=tk.LEFT)        # 表格预览区        preview_frame = tk.LabelFrame(self.root, text=&quot;重命名预览&quot;, font=('微软雅黑', 11, 'bold'), bg=&quot;#f5f5f5&quot;, padx=10, pady=10)        preview_frame.pack(fill=tk.BOTH, expand=True, padx=30, pady=10)        self.preview_tree = ttk.Treeview(preview_frame, columns=(&quot;原文件名&quot;, &quot;新文件名&quot;), show=&quot;headings&quot;, height=12)        self.preview_tree.heading(&quot;原文件名&quot;, text=&quot;原文件名&quot;)        self.preview_tree.heading(&quot;新文件名&quot;, text=&quot;新文件名&quot;)        self.preview_tree.column(&quot;原文件名&quot;, width=380, anchor=tk.W)        self.preview_tree.column(&quot;新文件名&quot;, width=280, anchor=tk.W)        scrollbar = ttk.Scrollbar(preview_frame, orient=tk.VERTICAL, command=self.preview_tree.yview)        self.preview_tree.config(yscrollcommand=scrollbar.set)        self.preview_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)        btn_frame = tk.Frame(self.root, bg=&quot;#f5f5f5&quot;)        btn_frame.pack(pady=20)        ttk.Button(btn_frame, text=&quot;开始重命名&quot;, command=self.start_rename, style='Modern.TButton').pack(side=tk.LEFT, padx=10)        ttk.Button(btn_frame, text=&quot;撤销重命名&quot;, command=self.undo_rename, style='Modern.TButton').pack(side=tk.LEFT, padx=10)        ttk.Button(btn_frame, text=&quot;清空重置&quot;, command=self.clear_all, style='Modern.TButton').pack(side=tk.LEFT, padx=10)    # 文件夹监控    def start_folder_monitor(self):        self.check_folder_change()    def check_folder_change(self):        folder = self.folder_path.get()        if os.path.isdir(folder):            current_mtime = os.path.getmtime(folder)            if current_mtime != self.last_folder_mtime:                self.last_folder_mtime = current_mtime                self.load_files()        self.root.after(2000, self.check_folder_change)    # 排除日志文件    def match_file_type(self, filename):        if filename == self.log_file:            return False        ext = os.path.splitext(filename)[1].lower()        ftype = self.file_type.get()        image = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp']        video = ['.mp4', '.mov', '.avi', '.mkv', '.flv', '.wmv', '.rmvb']        doc = ['.doc', '.docx', '.txt', '.pdf', '.ppt', '.pptx']        excel = ['.xls', '.xlsx', '.csv']        if ftype == &quot;全部&quot;:            return True        elif ftype == &quot;图片&quot;:            return ext in image        elif ftype == &quot;视频&quot;:            return ext in video        elif ftype == &quot;文档&quot;:            return ext in doc        elif ftype == &quot;表格&quot;:            return ext in excel        return False    def select_folder(self):        path = filedialog.askdirectory(title=&quot;选择文件夹&quot;)        if path:            self.folder_path.set(path)            self.last_folder_mtime = os.path.getmtime(path)            self.load_files()    def load_files(self):        folder = self.folder_path.get()        if not os.path.isdir(folder):            return        self.file_list = []        for name in os.listdir(folder):            fp = os.path.join(folder, name)            if os.path.isfile(fp) and self.match_file_type(name):                self.file_list.append(name)        if self.sort_type.get() == &quot;name&quot;:            self.file_list.sort()        else:            self.file_list.sort(key=lambda x: os.path.getmtime(os.path.join(folder, x)))        self.file_count_label.config(text=f&quot;文件数量:{len(self.file_list)} 个&quot;)        self.update_preview()    def get_num_format(self, count):        if count < 10:            return &quot;{:01d}&quot;        elif count < 100:            return &quot;{:02d}&quot;        elif count < 1000:            return &quot;{:03d}&quot;        else:            return &quot;{:04d}&quot;    # 表格预览    def update_preview(self):        for item in self.preview_tree.get_children():            self.preview_tree.delete(item)        if not self.file_list:            return        prefix = self.prefix.get()        count = len(self.file_list)        fmt = self.get_num_format(count)        for i, name in enumerate(self.file_list, 1):            ext = os.path.splitext(name)[1]            new_name = f&quot;{prefix}{fmt.format(i)}{ext}&quot;            self.preview_tree.insert(&quot;&quot;, tk.END, values=(name, new_name))    # =================== 修复权限报错的核心代码 ===================    def start_rename(self):        folder = self.folder_path.get()        if not os.path.isdir(folder):            messagebox.showerror(&quot;错误&quot;, &quot;请先选择文件夹&quot;)            return        if not self.file_list:            messagebox.showwarning(&quot;提示&quot;, &quot;没有可重命名的文件&quot;)            return        prefix = self.prefix.get()        count = len(self.file_list)        fmt = self.get_num_format(count)        log_path = os.path.join(folder, self.log_file)        if not messagebox.askyesno(&quot;确认&quot;, f&quot;即将重命名 {count} 个文件,是否继续?&quot;):            return        # 先取消隐藏,解决权限问题        try:            ctypes.windll.kernel32.SetFileAttributesW(log_path, 0)        except:            pass        rename_records = []        ok = 0        ng = 0        for i, name in enumerate(self.file_list, 1):            old_path = os.path.join(folder, name)            ext = os.path.splitext(name)[1]            new_name = f&quot;{prefix}{fmt.format(i)}{ext}&quot;            new_path = os.path.join(folder, new_name)            try:                if os.path.exists(new_path):                    new_path = os.path.join(folder, f&quot;temp_{fmt.format(i)}{ext}&quot;)                os.rename(old_path, new_path)                rename_records.append(f&quot;{name}|{new_name}&quot;)                ok += 1            except:                ng += 1        # 写入日志        with open(log_path, 'w', encoding='utf-8') as f:            f.write(&quot;\n&quot;.join(rename_records))        # 重新设置隐藏        try:            ctypes.windll.kernel32.SetFileAttributesW(log_path, 2)        except:            pass        messagebox.showinfo(&quot;完成&quot;, f&quot;重命名完成!\n成功:{ok} 个\n失败:{ng} 个&quot;)        self.load_files()    # ==============================================================    def undo_rename(self):        folder = self.folder_path.get()        log_path = os.path.join(folder, self.log_file)        if not os.path.exists(log_path):            messagebox.showerror(&quot;错误&quot;, &quot;未找到重命名记录,无法撤销!&quot;)            return        if not messagebox.askyesno(&quot;确认&quot;, &quot;确定要恢复所有文件的原始名称吗?&quot;):            return        try:            with open(log_path, 'r', encoding='utf-8') as f:                lines = [l.strip() for l in f.readlines() if l.strip()]        except:            messagebox.showerror(&quot;错误&quot;, &quot;记录文件读取失败&quot;)            return        ok = 0        ng = 0        for line in lines:            if &quot;|&quot; not in line:                continue            old_name, new_name = line.split(&quot;|&quot;, 1)            new_path = os.path.join(folder, new_name)            old_path = os.path.join(folder, old_name)            try:                if os.path.exists(new_path):                    os.rename(new_path, old_path)                    ok += 1            except:                ng += 1        messagebox.showinfo(&quot;完成&quot;, f&quot;撤销完成!\n成功恢复:{ok} 个\n失败:{ng} 个&quot;)        self.load_files()    def clear_all(self):        self.folder_path.set(&quot;&quot;)        self.prefix.set(&quot;文件_&quot;)        self.sort_type.set(&quot;name&quot;)        self.file_type.set(&quot;全部&quot;)        self.file_list = []        self.last_folder_mtime = 0        self.file_count_label.config(text=&quot;文件数量:0 个&quot;)        for item in self.preview_tree.get_children():            self.preview_tree.delete(item)if __name__ == &quot;__main__&quot;:    app = tk.Tk()    window = BatchRenameApp(app)    app.mainloop()<font color="#3f3f3f">rgb
精密测量技术论坛免责声明
✈️重要声明:以上内容仅代表该作者观点,不代表本站精密测量技术论坛立场。
✅如有涉及侵权请尽快告知,我们将会在第一时间处理。作者原创内容未经允许不得转载!
📱 站长联系邮箱:1339305021@qq.com
📱 站长联系微信:dddnnbbb
累计签到:19 天
连续签到:7 天
灌水成绩
1
152
11455
主题
帖子
积分

等级头衔

ID : 536

高级技术员

积分成就 测量币 : 11455
在线时间 : 0 小时
注册时间 : 2026-1-14
最后登录 : 2026-5-18

勋章
UID勋章测量学徒测量员
发表于 2026-4-20 15:57:57 | 显示全部楼层 IP:美国
很实用的技巧,马上用到项目里。
回复

使用道具 举报

累计签到:16 天
连续签到:1 天
灌水成绩
0
166
12968
主题
帖子
积分

等级头衔

ID : 591

高级技术员

积分成就 测量币 : 12968
在线时间 : 0 小时
注册时间 : 2025-10-29
最后登录 : 2026-5-17

勋章
UID勋章测量学徒测量员
发表于 2026-4-28 03:55:27 | 显示全部楼层 IP:美国
逻辑很清晰,一步步跟着走就行。
回复

使用道具 举报

累计签到:17 天
连续签到:1 天
灌水成绩
0
132
11246
主题
帖子
积分

等级头衔

ID : 574

高级技术员

积分成就 测量币 : 11246
在线时间 : 0 小时
注册时间 : 2026-2-2
最后登录 : 2026-5-11

勋章
UID勋章测量学徒测量员
发表于 2026-4-30 02:09:19 | 显示全部楼层 IP:广东东莞
实用性极强,强烈推荐。
回复

使用道具 举报

累计签到:19 天
连续签到:4 天
灌水成绩
0
133
10801
主题
帖子
积分

等级头衔

ID : 598

高级技术员

积分成就 测量币 : 10801
在线时间 : 0 小时
注册时间 : 2025-12-27
最后登录 : 2026-4-9

勋章
UID勋章测量学徒测量员
发表于 2026-4-30 02:09:19 | 显示全部楼层 IP:广东东莞
内容专业但不晦涩。
回复

使用道具 举报

累计签到:19 天
连续签到:6 天
灌水成绩
0
163
12764
主题
帖子
积分

等级头衔

ID : 577

高级技术员

积分成就 测量币 : 12764
在线时间 : 0 小时
注册时间 : 2025-12-6
最后登录 : 2026-4-5

勋章
UID勋章测量学徒测量员
发表于 2026-5-1 01:41:59 | 显示全部楼层 IP:广东东莞
讲解细致,不容易踩坑。
回复

使用道具 举报

累计签到:20 天
连续签到:14 天
灌水成绩
1
153
11906
主题
帖子
积分

等级头衔

ID : 522

高级技术员

积分成就 测量币 : 11906
在线时间 : 0 小时
注册时间 : 2026-1-16
最后登录 : 2026-5-20

勋章
UID勋章测量学徒测量员
发表于 2026-5-1 01:43:51 | 显示全部楼层 IP:广东东莞
对架构思路有很大帮助。
回复

使用道具 举报

累计签到:16 天
连续签到:6 天
灌水成绩
0
123
9142
主题
帖子
积分

等级头衔

ID : 530

中级技术员

积分成就 测量币 : 9142
在线时间 : 0 小时
注册时间 : 2025-12-7
最后登录 : 2026-5-19

勋章
UID勋章测量学徒测量员
发表于 2026-5-1 01:43:51 | 显示全部楼层 IP:广东东莞
语言精炼,不啰嗦。
回复

使用道具 举报

累计签到:19 天
连续签到:2 天
灌水成绩
0
174
13460
主题
帖子
积分

等级头衔

ID : 524

高级技术员

积分成就 测量币 : 13460
在线时间 : 0 小时
注册时间 : 2026-3-21
最后登录 : 2026-5-17

勋章
UID勋章测量学徒测量员
发表于 2026-5-3 07:28:38 | 显示全部楼层 IP:广东东莞
内容质量很高,比很多视频讲得好。
回复

使用道具 举报

快速回复换一批
好贴支持!
顶顶顶
路过留名
好贴帮顶
2333333333
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|精密测量技术论坛 ( 桂ICP备2026007449号-1 )

GMT+8, 2026-5-21 23:27 , Processed in 0.152312 second(s), 32 queries .

快速回复 返回顶部 返回列表