๐Ÿš€ FileMate Pro: Tkinter๋ฅผ ํ™œ์šฉํ•œ Python GUI ํŒŒ์ผ ๋งค๋‹ˆ์ €

๋ฐœํ–‰: (2026๋…„ 1์›” 1์ผ ์˜ค์ „ 01:32 GMT+9)
5 min read
์›๋ฌธ: Dev.to

Source: Dev.to

์œ„์— ์ œ๊ณต๋œ Source ๋งํฌ ์™ธ์— ๋ฒˆ์—ญํ•  ํ…์ŠคํŠธ๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฒˆ์—ญ์„ ์›ํ•˜๋Š” ๋ณธ๋ฌธ์„ ์•Œ๋ ค์ฃผ์‹œ๋ฉด ํ•œ๊ตญ์–ด๋กœ ๋ฒˆ์—ญํ•ด ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

๐Ÿ“Œ ๊ธฐ๋Šฅ

  • ํด๋” ๋ฐ ํŒŒ์ผ ์•„์ด์ฝ˜์œผ๋กœ ๋””๋ ‰ํ„ฐ๋ฆฌ ํƒ์ƒ‰
  • ๋”๋ธ” ํด๋ฆญ์œผ๋กœ ํŒŒ์ผ ์—ด๊ธฐ
  • ํŒŒ์ผ/ํด๋” ์ƒ์„ฑ, ์‚ญ์ œ ๋ฐ ์ด๋ฆ„ ๋ณ€๊ฒฝ
  • ๋ณต์‚ฌ ๋ฐ ๋ถ™์—ฌ๋„ฃ๊ธฐ ๊ธฐ๋Šฅ
  • ๋‹คํฌ/๋ผ์ดํŠธ ๋ชจ๋“œ ์ „ํ™˜
  • ์‹ค์‹œ๊ฐ„ ํ”ผ๋“œ๋ฐฑ์„ ์œ„ํ•œ ์ƒํƒœ ํ‘œ์‹œ์ค„

๐Ÿ–ฅ๏ธ ์Šคํฌ๋ฆฐ์ƒท ๋ฏธ๋ฆฌ๋ณด๊ธฐ

(์›ํ•œ๋‹ค๋ฉด ์—ฌ๊ธฐ GUI์˜ ์Šคํฌ๋ฆฐ์ƒท์„ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.)

๐Ÿ› ๏ธ ์ „์ฒด ์Šคํฌ๋ฆฝํŠธ

import sys
import os
import shutil
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import sv_ttk

def resource_path(file_name):
    """Get the absolute path to a resource, works for PyInstaller."""
    base_path = getattr(sys, "_MEIPASS", os.path.dirname(os.path.abspath(__file__)))
    return os.path.join(base_path, file_name)

# =========================
# App Setup
# =========================
root = tk.Tk()
root.title("FileMate Pro")
root.geometry("1000x680")
root.minsize(1000, 680)
sv_ttk.set_theme("light")

# =========================
# Globals
# =========================
dark_mode_var = tk.BooleanVar(value=False)
current_path_var = tk.StringVar(value=os.path.expanduser("~"))
status_var = tk.StringVar(value="Ready")
clipboard = None

# =========================
# Helpers
# =========================
def set_status(msg):
    status_var.set(msg)
    root.update_idletasks()

def toggle_theme():
    sv_ttk.set_theme("dark" if dark_mode_var.get() else "light")
    set_status(f"Theme switched to {'Dark' if dark_mode_var.get() else 'Light'} mode")

def refresh_file_list(path=None):
    path = path or current_path_var.get()
    if not os.path.exists(path):
        messagebox.showerror("Error", "Path does not exist!")
        return
    current_path_var.set(path)
    file_listbox.delete(0, tk.END)
    try:
        for item in os.listdir(path):
            full_path = os.path.join(path, item)
            display = "๐Ÿ“ " + item if os.path.isdir(full_path) else "๐Ÿ“„ " + item
            file_listbox.insert(tk.END, display)
        set_status(f"Listing files in {path}")
    except PermissionError:
        messagebox.showerror("Error", "Permission denied.")

def open_selected():
    selection = file_listbox.curselection()
    if not selection:
        return
    item_text = file_listbox.get(selection[0])
    item_name = item_text[2:]          # strip the emoji prefix
    path = os.path.join(current_path_var.get(), item_name)
    if os.path.isdir(path):
        refresh_file_list(path)
    else:
        try:
            os.startfile(path)
            set_status(f"Opened {item_name}")
        except Exception as e:
            messagebox.showerror("Error", f"Cannot open file: {e}")

def go_up():
    parent = os.path.dirname(current_path_var.get())
    refresh_file_list(parent)

def create_folder():
    folder_name = simpledialog.askstring("Create Folder", "Enter folder name:")
    if folder_name:
        path = os.path.join(current_path_var.get(), folder_name)
        try:
            os.makedirs(path)
            refresh_file_list()
            set_status(f"Folder '{folder_name}' created")
        except FileExistsError:
            messagebox.showerror("Error", "Folder already exists.")
        except Exception as e:
            messagebox.showerror("Error", str(e))

def delete_item(path=None):
    if not path:
        selection = file_listbox.curselection()
        if not selection:
            return
        item_text = file_listbox.get(selection[0])
        item_name = item_text[2:]
        path = os.path.join(current_path_var.get(), item_name)
    if messagebox.askyesno("Confirm Delete", f"Are you sure you want to delete '{os.path.basename(path)}'?"):
        try:
            if os.path.isdir(path):
                shutil.rmtree(path)
            else:
                os.remove(path)
            refresh_file_list()
            set_status(f"Deleted '{os.path.basename(path)}'")
        except Exception as e:
            messagebox.showerror("Error", str(e))

def rename_item():
    selection = file_listbox.curselection()
    if not selection:
        return
    item_text = file_listbox.get(selection[0])
    old_name = item_text[2:]
    old_path = os.path.join(current_path_var.get(), old_name)
    new_name = simpledialog.askstring("Rename", f"Enter new name for '{old_name}':")
    if new_name:
        new_path = os.path.join(current_path_var.get(), new_name)
        try:
            os.re
name(old_path, new_path)
            refresh_file_list()
            set_status(f"Renamed '{old_name}' to '{new_name}'")
        except Exception as e:
            messagebox.showerror("Error", str(e))

def copy_item():
    global clipboard
    selection = file_listbox.curselection()
    if not selection:
        return
    item_text = file_listbox.get(selection[0])
    clipboard = os.path.join(current_path_var.get(), item_text[2:])
    set_status(f"Copied '{os.path.basename(clipboard)}'")

def paste_item():
    global clipboard
    if not clipboard:
        return
    dest = os.path.join(current_path_var.get(), os.path.basename(clipboard))
    try:
        if os.path.isdir(clipboard):
            shutil.copytree(clipboard, dest)
        else:
            shutil.copy2(clipboard, dest)
        refresh_file_list()
        set_status(f"Pasted '{os.path.basename(clipboard)}'")
    except Exception as e:
        messagebox.showerror("Error", str(e))

# =========================
# Context Menu
# =========================
context_menu = tk.Menu(root, tearoff=0)
context_menu.add_command(label="Open", command=open_selected)
context_menu.add_command(label="Delete", command=lambda: delete_item())
context_menu.add_command(label="Rename", command=rename_item)
context_menu.add_separator()
context_menu.add_command(label="Copy", command=copy_item)
context_menu.add_command(label="Paste", command=paste_item)

# =========================
# UI Layout
# =========================
# Top frame โ€“ navigation & actions
top_frame = ttk.Frame(root)
top_frame.pack(fill=tk.X, padx=5, pady=5)

path_entry = ttk.Entry(top_frame, textvariable=current_path_var, width=80)
path_entry.pack(side=tk.LEFT, padx=(0, 5))

btn_go = ttk.Button(top_frame, text="Go", command=lambda: refresh_file_list(current_path_var.get()))
btn_go.pack(side=tk.LEFT, padx=2)

btn_up = ttk.Button(top_frame, text="Up", command=go_up)
btn_up.pack(side=tk.LEFT, padx=2)

btn_new_folder = ttk.Button(top_frame, text="New Folder", command=create_folder)
btn_new_folder.pack(side=tk.LEFT, padx=2)

dark_check = ttk.Checkbutton(
    top_frame,
    text="Dark Mode",
    variable=dark_mode_var,
    command=toggle_theme
)
dark_check.pack(side=tk.RIGHT, padx=2)

# Main listbox โ€“ file view
list_frame = ttk.Frame(root)
list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

file_listbox = tk.Listbox(
    list_frame,
    selectmode=tk.SINGLE,
    font=("Segoe UI", 10),
    activestyle="none"
)
file_listbox.pack(fill=tk.BOTH, expand=True, side=tk.LEFT)

# Scrollbars
scroll_y = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=file_listbox.yview)
scroll_y.pack(fill=tk.Y, side=tk.RIGHT)
file_listbox.config(yscrollcommand=scroll_y.set)

# Bind doubleโ€‘click and rightโ€‘click
file_listbox.bind("", lambda e: open_selected())
file_listbox.bind("", lambda e: context_menu.tk_popup(e.x_root, e.y_root))

# Status bar
status_bar = ttk.Label(root, textvariable=status_var, relief=tk.SUNKEN, anchor=tk.W)
status_bar.pack(fill=tk.X, side=tk.BOTTOM, ipady=2)

# Initial population
refresh_file_list()

root.mainloop()

์ปจํ…์ŠคํŠธ ๋ฉ”๋‰ด

context_menu.add_command(label="Paste", command=paste_item)

def show_context_menu(event):
    try:
        selection = file_listbox.nearest(event.y)
        file_listbox.selection_clear(0, tk.END)
        file_listbox.selection_set(selection)
        context_menu.post(event.x_root, event.y_root)
    finally:
        context_menu.grab_release()

์ƒํƒœ ํ‘œ์‹œ์ค„

ttk.Label(root, textvariable=status_var, anchor="w", font=("Segoe UI", 10)).pack(
    side=tk.BOTTOM, fill="x"
)

๋ฉ”์ธ ํ”„๋ ˆ์ž„

main_frame = ttk.Frame(root, padding=20)
main_frame.pack(expand=True, fill="both")

ํˆด๋ฐ”

toolbar = ttk.Frame(main_frame)
toolbar.pack(fill="x", pady=(0, 10))

ttk.Button(toolbar, text="Up", command=go_up).pack(side="left", padx=5)
ttk.Button(toolbar, text="New Folder", command=create_folder).pack(side="left", padx=5)
ttk.Button(toolbar, text="Delete", command=delete_item).pack(side="left", padx=5)
ttk.Button(toolbar, text="Copy", command=copy_item).pack(side="left", padx=5)
ttk.Button(toolbar, text="Paste", command=paste_item).pack(side="left", padx=5)
ttk.Checkbutton(
    toolbar, text="Dark Mode", variable=dark_mode_var, command=toggle_theme
).pack(side="right", padx=5)

ํ˜„์žฌ ๊ฒฝ๋กœ

path_frame = ttk.Frame(main_frame)
path_frame.pack(fill="x", pady=(0, 10))

ttk.Label(path_frame, text="Current Path:").pack(side="left")
ttk.Entry(path_frame, textvariable=current_path_var, width=80).pack(
    side="left", padx=(5, 0)
)
ttk.Button(
    path_frame,
    text="Go",
    command=lambda: refresh_file_list(current_path_var.get()),
).pack(side="left", padx=5)

ํŒŒ์ผ ๋ชฉ๋ก

file_frame = ttk.Frame(main_frame)
file_frame.pack(expand=True, fill="both")

file_listbox = tk.Listbox(file_frame, font=("Segoe UI", 11))
file_listbox.pack(side="left", expand=True, fill="both")
file_listbox.bind("", lambda e: open_selected())
file_listbox.bind("", show_context_menu)

scrollbar = ttk.Scrollbar(file_frame, orient="vertical", command=file_listbox.yview)
scrollbar.pack(side="right", fill="y")
file_listbox.config(yscrollcommand=scrollbar.set)

# Initialize file list
refresh_file_list()

์•ฑ ์‹คํ–‰

root.mainloop()

์‹คํ–‰ ๋ฐฉ๋ฒ•

  1. ์ข…์†์„ฑ ์„ค์น˜

    pip install sv_ttk
  2. ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰

    python FileMat_v3.py

FileMate Pro โ€“ ์นœ์ ˆํ•œ ํŒŒ์ด์ฌ ํŒŒ์ผ ๊ด€๋ฆฌ์ž์ด๋ฉฐ ๋‹คํฌ ๋ชจ๋“œ, ๋ณต์‚ฌโ€‘๋ถ™์—ฌ๋„ฃ๊ธฐ, ํŒŒ์ผ์„ ์†์‰ฝ๊ฒŒ ํƒ์ƒ‰ํ•˜๊ณ  ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ํ•„์ˆ˜ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค!

Back to Blog

๊ด€๋ จ ๊ธ€

๋” ๋ณด๊ธฐ ยป

Python๊ณผ Tkinter๋กœ ๊ฐ„๋‹จํ•œ ํŒŒ์ผ ํƒ์ƒ‰๊ธฐ ๋งŒ๋“ค๊ธฐ โ€“ FileMate Explorer

๐Ÿ“‚ FileMate Explorer โ€“ ๊ฐ€๋ฒผ์šด ํŒŒ์ด์ฌ ํŒŒ์ผ ๊ด€๋ฆฌ์ž. ์™„์ „ํžˆ ํŒŒ์ด์ฌ์œผ๋กœ ๋งŒ๋“  ๊ฐ€๋ฒผ์šด ํŒŒ์ผ ํƒ์ƒ‰๊ธฐ๋ฅผ ์›ํ•˜์…จ๋‚˜์š”? Tkinter ๊ธฐ๋ฐ˜ FileMate Explorer๋ฅผ ๋งŒ๋‚˜๋ณด์„ธ์š”.

๐Ÿงฎ Python (Tkinter)์œผ๋กœ Desktop Word Counter ์•ฑ ๋งŒ๋“ค๊ธฐ

WordCounter Pro ํ˜„๋Œ€์ ์ธ ์˜คํ”„๋ผ์ธ ์›Œ๋“œ ์นด์šดํ„ฐ๋กœ ์‹ค์‹œ๊ฐ„ ํ†ต๊ณ„, ํ‚ค์›Œ๋“œ ๋ฐ€๋„, ๋‹คํฌ ๋ชจ๋“œ ๋ฐ ํŒŒ์ผ ์ง€์›์„ ์ œ๊ณตํ•˜๋ฉฐ, ์ „์ ์œผ๋กœ Python ๋ฐ Tkinter๋กœ ๊ตฌ์ถ•๋˜์—ˆ์Šต๋‹ˆ๋‹ค. If yo...

๐Ÿงฎ Python๊ณผ Tkinter๋ฅผ ์‚ฌ์šฉํ•œ ํ˜„๋Œ€ ํ†ต๊ณ„ ๊ณ„์‚ฐ๊ธฐ ๋งŒ๋“ค๊ธฐ

StatMate โ€“ ํ˜„๋Œ€์ ์ธ ๋ฐ์Šคํฌํ†ฑ ํ†ต๊ณ„ ๊ณ„์‚ฐ๊ธฐ Python์€ ๋ฐ์ดํ„ฐ ์‚ฌ์ด์–ธ์Šค ์ƒํƒœ๊ณ„๋กœ ์ž์ฃผ ์ฐฌ์‚ฌ๋ฅผ ๋ฐ›์ง€๋งŒ, Pandas๋‚˜ NumPy ์—†์ด๋„ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Tkinter๋ฅผ ์‚ฌ์šฉํ•œ 2D ๊ฒŒ์ž„ ์‹œ์ž‘ํ•˜๊ธฐ (ํŒŒํŠธ 9): ์นด์šดํ„ฐ ํ‘œ์‹œ

์นด์šดํ„ฐ ํ‘œ์‹œ ์ด ๊ธฐ์‚ฌ์—์„œ๋Š” ํ™”๋ฉด์— ๋‚จ์•„ ์žˆ๋Š” ์•…๋งˆ ์ˆ˜๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ์นด์šดํ„ฐ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๋จผ์ €, ์นด์šดํ„ฐ๋ผ๋Š” ๋ณ€์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ๋‚จ์€ ์ˆ˜๋ฅผ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค.