
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)
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:
投稿請回覆一下我才會發現,才好放進目錄中。
另請稍微補充一下使用說明,謝謝。