Rename_Manager————BY:mko897



main.py

gui_main import main_app

if __name__ == "__main__":

    main_app()


gui_main.py

import tkinter as tk

tkinter import ttk, filedialog, messagebox

gui_language import open_language_window

config_manager import load_user_config

language_manager import LANG_TEXTS

rename_logic import rename_files

import os

import time



def main_app():

    config = load_user_config()

    lang = config.get("language", "en")


    root = tk.Tk()

    root.title("File Renamer")

    root.geometry("1000x600")

    root.minsize(800, 500)

    root.option_add("*Font", ("Segoe UI", 12))


    text = LANG_TEXTS[lang]


    # ==================== Functions ====================

    def update_language(new_lang):

        nonlocal lang, text

        lang = new_lang

        text = LANG_TEXTS[lang]


        # 更新標籤文字

        btn_language.config(text=text["language"])

        lbl_folder.config(text=text["select_folder"])

        btn_browse.config(text=text["browse"])

        seq_frame.config(text=text["sequential"])

        replace_frame.config(text=text["replace"])

        prefix_frame.config(text=text["prefix"])

        lbl_start_number.config(text=text["start_number"])

        lbl_find.config(text=text["find"])

        lbl_replace_with.config(text=text["replace_with"])

        lbl_prefix_text.config(text=text["prefix_text"])

        lbl_func.config(text=text["function"])

        btn_start.config(text=text["start"])

        btn_exit.config(text=text["exit"])

        progress_label.config(text="")


        func_choice["values"] = [text["sequential"], text["replace"], text["prefix"]]

        if func_choice.current() == -1:

            func_choice.current(0)


        tree.heading("original", text=text["original_name"])

        tree.heading("new", text=text["new_name"])

        refresh_preview()


    # ==================== 第一層:資料夾選擇 ====================

    top_frame = ttk.Frame(root)

    top_frame.pack(fill="x", padx=10, pady=10)


    btn_language = ttk.Button(top_frame, text=text["language"],

                              command=lambda: open_language_window(root, update_language))

    btn_language.pack(side="left", padx=5)


    lbl_folder = ttk.Label(top_frame, text=text["select_folder"])

    lbl_folder.pack(side="left", padx=10)


    folder_var = tk.StringVar()

    entry_folder = ttk.Entry(top_frame, textvariable=folder_var, width=60)

    entry_folder.pack(side="left", padx=5)


    def browse_folder():

        folder = filedialog.askdirectory()

        if folder:

            folder_var.set(folder)

            refresh_preview()


    btn_browse = ttk.Button(top_frame, text=text["browse"], command=browse_folder)

    btn_browse.pack(side="left", padx=5)


    # ==================== 第二層:功能設定區 ====================

    mid_frame = ttk.Frame(root)

    mid_frame.pack(fill="x", padx=10, pady=10)


    # ---- Sequential ----

    seq_frame = ttk.LabelFrame(mid_frame, text=text["sequential"])

    seq_frame.pack(side="left", expand=True, fill="both", padx=5, pady=5)

    lbl_start_number = ttk.Label(seq_frame, text=text["start_number"])

    lbl_start_number.pack(pady=5)

    seq_start = tk.IntVar(value=1)

    ttk.Entry(seq_frame, textvariable=seq_start, width=10).pack(pady=5)


    # ---- Replace ----

    replace_frame = ttk.LabelFrame(mid_frame, text=text["replace"])

    replace_frame.pack(side="left", expand=True, fill="both", padx=5, pady=5)

    lbl_find = ttk.Label(replace_frame, text=text["find"])

    lbl_find.pack(pady=5)

    find_text = tk.StringVar()

    ttk.Entry(replace_frame, textvariable=find_text, width=20).pack(pady=5)

    lbl_replace_with = ttk.Label(replace_frame, text=text["replace_with"])

    lbl_replace_with.pack(pady=5)

    replace_text = tk.StringVar()

    ttk.Entry(replace_frame, textvariable=replace_text, width=20).pack(pady=5)


    # ---- Prefix ----

    prefix_frame = ttk.LabelFrame(mid_frame, text=text["prefix"])

    prefix_frame.pack(side="left", expand=True, fill="both", padx=5, pady=5)

    lbl_prefix_text = ttk.Label(prefix_frame, text=text["prefix_text"])

    lbl_prefix_text.pack(pady=5)

    prefix_var = tk.StringVar()

    ttk.Entry(prefix_frame, textvariable=prefix_var, width=20).pack(pady=5)


    # ==================== 第三層:功能與操作按鈕 ====================

    bottom_frame = ttk.Frame(root)

    bottom_frame.pack(fill="x", padx=10, pady=10)


    lbl_func = ttk.Label(bottom_frame, text=text["function"])

    lbl_func.pack(side="left", padx=5)


    func_choice = ttk.Combobox(bottom_frame, values=[

        text["sequential"], text["replace"], text["prefix"]

    ], state="readonly", width=15)

    func_choice.set(text["sequential"])

    func_choice.pack(side="left", padx=5)


    # ==================== 預覽功能 ====================

    def refresh_preview(*_):

        tree.delete(*tree.get_children())

        folder = folder_var.get()

        if not folder or not os.path.isdir(folder):

            return


        files = [f for f in os.listdir(folder)

                 if os.path.isfile(os.path.join(folder, f))

                 and not f.lower().endswith(".exe")]


        func = func_choice.get()

        for idx, filename in enumerate(sorted(files), start=seq_start.get()):

            name, ext = os.path.splitext(filename)

            preview_name = filename


            if func == text["sequential"]:

                preview_name = f"{idx:03d}{ext}"


            elif func == text["replace"]:

                if find_text.get():

                    preview_name = name.replace(find_text.get(), replace_text.get()) + ext


            elif func == text["prefix"]:

                if prefix_var.get():

                    preview_name = f"{prefix_var.get()}_{filename}"


            tree.insert("", "end", values=(filename, preview_name))


    # 綁定即時更新事件

    func_choice.bind("<<ComboboxSelected>>", refresh_preview)

    seq_start.trace_add("write", refresh_preview)

    find_text.trace_add("write", refresh_preview)

    replace_text.trace_add("write", refresh_preview)

    prefix_var.trace_add("write", refresh_preview)


    # ==================== 進度列 ====================

    progress_frame = ttk.Frame(root)

    progress_frame.pack(fill="x", padx=10, pady=5)


    progress_label = ttk.Label(progress_frame, text="", anchor="center")

    progress_label.pack(fill="x")


    progress_bar = ttk.Progressbar(progress_frame, orient="horizontal", length=100, mode="determinate")

    progress_bar.pack(fill="x", pady=5)


    # ==================== 執行按鈕 ====================

    def run_rename():

        folder = folder_var.get()

        if not folder:

            messagebox.showwarning("Warning", "Please a folder.")

            return


        # 取得檔案清單

        files = [f for f in os.listdir(folder)

                 if os.path.isfile(os.path.join(folder, f))

                 and not f.lower().endswith(".exe")]


        total = len(files)

        if total == 0:

            messagebox.showinfo("Info", "No files to rename.")

            return


        progress_bar["value"] = 0

        progress_bar["maximum"] = total


        # 改名邏輯 + 即時進度

        for idx, filename in enumerate(sorted(files), start=seq_start.get()):

            name, ext = os.path.splitext(filename)

            new_name = filename

            func = func_choice.get()


            if func == text["sequential"]:

                new_name = f"{idx:03d}{ext}"

            elif func == text["replace"] and find_text.get():

                new_name = name.replace(find_text.get(), replace_text.get()) + ext

            elif func == text["prefix"] and prefix_var.get():

                new_name = f"{prefix_var.get()}_{filename}"


            src = os.path.join(folder, filename)

            dst = os.path.join(folder, new_name)

            if src != dst:

                os.rename(src, dst)


            # 更新進度

            progress_bar["value"] += 1

            progress_label.config(text=f"Renaming {progress_bar['value']} / {total} files...")

            root.update()


        progress_label.config(text="✅ Rename complete!")

        messagebox.showinfo("Done", "Files renamed successfully.")

        refresh_preview()


    btn_start = ttk.Button(bottom_frame, text=text["start"], command=run_rename)

    btn_start.pack(side="left", padx=5)


    btn_exit = ttk.Button(bottom_frame, text=text["exit"], command=root.destroy)

    btn_exit.pack(side="right", padx=5)


    # ==================== 最下層:預覽表格 ====================

    tree_frame = ttk.Frame(root)

    tree_frame.pack(fill="both", expand=True, padx=10, pady=10)


    tree = ttk.Treeview(tree_frame, columns=("original", "new"), show="headings")

    tree.heading("original", text=text["original_name"])

    tree.heading("new", text=text["new_name"])

    tree.column("original", width=300)

    tree.column("new", width=300)

    tree.pack(fill="both", expand=True)


    root.mainloop()


gui_language.py

 import tkinter as tk

tkinter import ttk

config_manager import save_user_config


def open_language_window(root, on_language_change):

    lang_window = tk.Toplevel(root)

    lang_window.title("Language Settings")

    lang_window.geometry("360x360")

    lang_window.option_add("*Font", ("Segoe UI", 14))

    lang_window.grab_set()


    langs = {

        "English": "en",

        "繁體中文": "zh_tw",

        "简体中文": "zh_cn",

        "日本語": "ja"

    }


    ttk.Label(lang_window, text="Select Language:").pack(pady=20)


    lang_var = tk.StringVar(value="English")

    combo = ttk.Combobox(lang_window, values=list(langs.keys()), textvariable=lang_var, state="readonly", width=20)

    combo.pack(pady=10)


    def confirm_language():

        selected = combo.get()

        lang_code = langs[selected]

        save_user_config(lang_code)

        on_language_change(lang_code)

        lang_window.destroy()


    ttk.Button(lang_window, text="Confirm", command=confirm_language, width=12).pack(pady=20)



language_manager.py


LANG_TEXTS = {

    "en": {

        "language": "Language",

        "select_folder": "Select Folder",

        "browse": "Browse",

        "sequential": "Sequential",

        "replace": "Replace",

        "prefix": "Prefix",

        "start_number": "Start Number",

        "find": "Find Text",

        "replace_with": "Replace With",

        "prefix_text": "Prefix Text",

        "function": "Function",

        "start": "Start Rename",

        "undo": "Undo Rename",

        "exit": "Exit",

        "original_name": "Original Name",

        "new_name": "New Name",

    },

    "zh_tw": {

        "language": "語言",

        "select_folder": "選擇資料夾",

        "browse": "瀏覽",

        "sequential": "排序",

        "replace": "取代文字",

        "prefix": "前綴",

        "start_number": "起始編號",

        "find": "尋找文字",

        "replace_with": "取代為",

        "prefix_text": "前綴文字",

        "function": "功能",

        "start": "開始重新命名",

        "undo": "還原名稱",

        "exit": "離開",

        "original_name": "原始檔名",

        "new_name": "改名預覽",

    },

    "zh_cn": {

        "language": "语言",

        "select_folder": "选择文件夹",

        "browse": "浏览",

        "sequential": "排序",

        "replace": "替换文字",

        "prefix": "前缀",

        "start_number": "起始编号",

        "find": "查找文字",

        "replace_with": "替换为",

        "prefix_text": "前缀文字",

        "function": "功能",

        "start": "开始重命名",

        "undo": "还原名称",

        "exit": "退出",

        "original_name": "原始文件名",

        "new_name": "新文件名",

    },

    "ja": {

        "language": "言語",

        "select_folder": "フォルダーを選択",

        "browse": "参照",

        "sequential": "連番",

        "replace": "文字を置換",

        "prefix": "接頭辞",

        "start_number": "開始番号",

        "find": "検索文字",

        "replace_with": "置換文字",

        "prefix_text": "接頭文字",

        "function": "機能",

        "start": "名前変更を開始",

        "undo": "元に戻す",

        "exit": "終了",

        "original_name": "元のファイル名",

        "new_name": "新しいファイル名",

    }

}


rename_logic.py

import os


def rename_files(folder, func_type, start_num, find_str, replace_str, prefix, text):

    """

    批次重新命名檔案(排序、取代、前綴)

    不處理資料夾與 .exe 檔案

    排序功能會自動略過已存在的檔案名稱

    """

    if not folder or not os.path.isdir(folder):

        return


    files = [f for f in os.listdir(folder)

             if os.path.isfile(os.path.join(folder, f))

             and not f.lower().endswith(".exe")]


    files.sort()


    for filename in files:

        name, ext = os.path.splitext(filename)

        new_name = filename


        # === 排序功能 ===

        if func_type == text["sequential"]:

            counter = start_num

            # 找到第一個可用的檔名

            while True:

                new_name = f"{counter:03d}{ext}"

                dst = os.path.join(folder, new_name)

                if not os.path.exists(dst):  # 找到可用編號

                    break

                counter += 1

            start_num = counter + 1  # 下一輪從下個編號開始


        # === 取代功能 ===

        elif func_type == text["replace"]:

            if find_str:

                new_name = name.replace(find_str, replace_str) + ext


        # === 前綴功能 ===

        elif func_type == text["prefix"]:

            if prefix:

                new_name = f"{prefix}_{filename}"


        src = os.path.join(folder, filename)

        dst = os.path.join(folder, new_name)


        if src != dst:

            try:

                os.rename(src, dst)

            except Exception as e:

                print(f"❌ Rename failed for {filename}: {e}")


Releases:

RENAME.GUI.v1.0.exe