SoFunction
Updated on 2025-04-14

Implementing Image Batch Rename Tool with Python

Project Introduction

This is a graphical interface tool developed based on Python to batch rename image files in folders. It can recursively process images in selected folders and all subfolders, and intelligently rename them in the format of "folder name_serial number.extension".

Main functions

  • Recursive processing: Automatically scan and process selected folders and all subfolders
  • Intelligent renaming: Rename the image to a unified format of "Folder Name_Serial Number.Extension"
  • Multilingual support: Provides 7 language interfaces (Chinese, English, Russian, Japanese, German, Portuguese and French)
  • Real-time log: Show detailed processing progress and results
  • User-friendly: Simple and intuitive graphical interface, simple operation

Supported image formats

  • JPG/JPEG
  • PNG
  • GIF
  • BMP
  • TIFF
  • WEBP

Technology implementation

The project is mainly developed using the Python standard library, and there is no need to install additional third-party libraries. Core functions include:

  • Recursive file processing: useTraversing the folder structure
  • Multilingual support: Store translated text through JSON file, dynamically load
  • Thread processing: usethreadingModule prevents interface from being stuck
  • GUI interface: based ontkinterBuild a user-friendly graphical interface

Complete code

rename_images_gui.py

import os
import tkinter as tk
from tkinter import filedialog, messagebox, scrolledtext, ttk
import threading
from language_manager import LanguageManager

SUPPORTED_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp'}

# --- Core renaming logic ---def rename_images_in_folder(folder_path, log_callback):
    try:
        folder_name = (folder_path)
        if not folder_name:
             log_callback(lang_manager.get_text('skip_root').format(folder_path=folder_path))
             return 0

        count = 1
        renamed_count = 0
        log_callback(lang_manager.get_text('start_processing').format(folder_path=folder_path))

        items = sorted((folder_path))

        for filename in items:
            original_full_path = (folder_path, filename)

            if (original_full_path):
                _, ext = (filename)
                if () in SUPPORTED_EXTENSIONS:
                    new_filename = f"{folder_name}_{count}{ext}"
                    new_full_path = (folder_path, new_filename)

                    if original_full_path == new_full_path:
                        log_callback(lang_manager.get_text('skip_same_name').format(filename=filename))
                        count += 1
                        continue
                    elif (new_full_path):
                         log_callback(lang_manager.get_text('warning_existing_file').format(filename=filename, new_filename=new_filename))
                         continue

                    try:
                        (original_full_path, new_full_path)
                        log_callback(lang_manager.get_text('success').format(filename=filename, new_filename=new_filename))
                        renamed_count += 1
                        count += 1
                    except OSError as e:
                        log_callback(lang_manager.get_text('error_rename').format(filename=filename, error=str(e)))

        log_callback(lang_manager.get_text('folder_processed').format(folder_path=folder_path, renamed_count=renamed_count))
        return renamed_count

    except Exception as e:
        log_callback(lang_manager.get_text('unexpected_error').format(error=str(e)))
        return 0

def rename_images_recursively(root_dir, log_callback):
    total_renamed = 0
    if not (root_dir):
        log_callback(lang_manager.get_text('invalid_folder_error'))
        return 0

    log_callback(lang_manager.get_text('recursive_start').format(root_dir=root_dir))

    for dirpath, dirnames, filenames in (root_dir, topdown=True):
        renamed_in_folder = rename_images_in_folder(dirpath, log_callback)
        total_renamed += renamed_in_folder

    log_callback(lang_manager.get_text('recursive_complete').format(total_renamed=total_renamed))
    return total_renamed

# --- GUI part ---class RenamerApp:
    def __init__(self, master):
         = master
        self.lang_manager = LanguageManager()
        self.update_window_title()

        self.selected_folder = ()
        self.is_running = False

        # Create a language selection drop-down box        self.create_language_selector()

        # Folder selection section        (master, text=self.lang_manager.get_text('select_folder')).grid(row=1, column=0, padx=5, pady=5)
        self.folder_entry = (master, textvariable=self.selected_folder, width=50, state='readonly')
        self.folder_entry.grid(row=1, column=1, padx=5, pady=5, sticky="ew")
        self.browse_button = (master, text=self.lang_manager.get_text('browse'), command=self.browse_folder)
        self.browse_button.grid(row=1, column=2, padx=5, pady=5)

        # Start Button        self.rename_button = (master, text=self.lang_manager.get_text('start_rename'), command=self.start_renaming_thread)
        self.rename_button.grid(row=2, column=0, columnspan=3, padx=5, pady=10)

        # Log area        (master, text=self.lang_manager.get_text('log')).grid(row=3, column=0, padx=5, pady=5, sticky="w")
        self.log_area = (master, wrap=, height=15, width=70)
        self.log_area.grid(row=4, column=0, columnspan=3, padx=5, pady=5, sticky="nsew")
        self.log_area.config(state='disabled')

        # Layout configuration        master.grid_columnconfigure(1, weight=1)
        master.grid_rowconfigure(4, weight=1)

    def create_language_selector(self):
        languages = self.lang_manager.get_supported_languages()
        current_lang = self.lang_manager.get_current_language()
        
        frame = ()
        (row=0, column=0, columnspan=3, padx=5, pady=5, sticky="e")
        
        # Get the localized name of the current language        current_lang_name = languages[current_lang]
        self.lang_var = (value=current_lang_name)
        
        # Create a mapping of language code to localized names        self.lang_code_to_name = languages
        self.lang_name_to_code = {v: k for k, v in ()}
        
        self.lang_combobox = (frame, textvariable=self.lang_var, values=list(()), state='readonly', width=10)
        self.lang_combobox.bind('<<ComboboxSelected>>', self.on_language_change)
        self.lang_combobox.pack(side=)

    def on_language_change(self, event=None):
        selected_lang_name = self.lang_var.get()
        selected_lang_code = self.lang_name_to_code[selected_lang_name]
        if self.lang_manager.load_language(selected_lang_code):
            self.update_ui_texts()

    def update_ui_texts(self):
        self.update_window_title()
        self.browse_button.config(text=self.lang_manager.get_text('browse'))
        self.rename_button.config(text=self.lang_manager.get_text('start_rename') if not self.is_running else self.lang_manager.get_text('processing'))
        
        # Update label texts
        for widget in .grid_slaves():
            if isinstance(widget, ):
                if widget.grid_info()['row'] == 1:  # Select folder label
                    (text=self.lang_manager.get_text('select_folder'))
                elif widget.grid_info()['row'] == 3:  # Log label
                    (text=self.lang_manager.get_text('log'))

    def update_window_title(self):
        (self.lang_manager.get_text('title'))

    def log(self, message):
        def _update_log():
            self.log_area.config(state='normal')
            self.log_area.insert(, message + "\n")
            self.log_area.see()
            self.log_area.config(state='disabled')
        (0, _update_log)

    def browse_folder(self):
        if self.is_running:
            (self.lang_manager.get_text('title'), self.lang_manager.get_text('task_running'))
            return
        folder = ()
        if folder:
            self.selected_folder.set(folder)

    def start_renaming_thread(self):
        if self.is_running:
            (self.lang_manager.get_text('title'), self.lang_manager.get_text('task_running'))
            return

        root_dir = self.selected_folder.get()
        if not root_dir:
            (self.lang_manager.get_text('title'), self.lang_manager.get_text('select_folder_error'))
            return
        if not (root_dir):
            (self.lang_manager.get_text('title'), self.lang_manager.get_text('invalid_folder_error'))
            return

        if not (self.lang_manager.get_text('title'),
                                  self.lang_manager.get_text('confirm_operation').format(folder=(root_dir))):
            return

        self.log_area.config(state='normal')
        self.log_area.delete('1.0', )
        self.log_area.config(state='disabled')
        self.rename_button.config(state='disabled', text=self.lang_manager.get_text('processing'))
        self.browse_button.config(state='disabled')
        self.is_running = True

        self.rename_thread = (target=self.run_rename_task, args=(root_dir,))
        self.rename_thread.daemon = True
        self.rename_thread.start()

    def run_rename_task(self, root_dir):
        try:
            rename_images_recursively(root_dir, )
        except Exception as e:
            (self.lang_manager.get_text('unexpected_error').format(error=str(e)))
        finally:
            (0, self.on_rename_complete)

    def on_rename_complete(self):
        (self.lang_manager.get_text('title'), self.lang_manager.get_text('completed'))
        self.rename_button.config(state='normal', text=self.lang_manager.get_text('start_rename'))
        self.browse_button.config(state='normal')
        self.is_running = False

# --- Main program entry ---if __name__ == "__main__":
    lang_manager = LanguageManager()
    root = ()
    app = RenamerApp(root)
    ()

language_manager.py

import os
import json
import locale

class LanguageManager:
    def __init__(self):
        self.current_language = None
         = {}
        self.locales_dir = ((__file__), 'locales')
        self.supported_languages = {
            'en': 'English',
            'zh': 'Chinese',
            'ru': 'Русский',
            'ja': 'Japanese',
            'de': 'Deutsch',
            'pt': 'Português',
            'fr': 'Français'
        }
        
        # Automatically detect system language during initialization        system_lang = ()[0]
        self.default_language = 'en'  # Use English by default        self.load_language(self.default_language)
    
    def load_language(self, lang_code):
        """Load the specified language file"""
        if lang_code not in self.supported_languages:
            lang_code = 'en'  # Use English by default        
        try:
            file_path = (self.locales_dir, f'{lang_code}.json')
            with open(file_path, 'r', encoding='utf-8') as f:
                 = (f)
            self.current_language = lang_code
            return True
        except Exception as e:
            print(f'Error loading language file: {e}')
            return False
    
    def get_text(self, key):
        """Get the translated text"""
        return (key, key)
    
    def get_current_language(self):
        """Get the current language code"""
        return self.current_language
    
    def get_supported_languages(self):
        """Get a list of supported languages"""
        return self.supported_languages

How to use

  • Clone or download the project code
  • Run the program:python rename_images_gui.py
  • Select the root folder to process
  • Click the "Start Rename" button
  • View processing results in the log area

The above is the detailed content of using Python to implement image batch renaming tool. For more information about Python image renaming, please follow my other related articles!