📂 Ever wanted a lightweight file explorer built entirely in Python? Meet FileMate Explorer, a Tkinter-based file manager that supports drag-and-drop, copy/paste, folder creation, renaming, and a dark mode toggle.
Managing files can be tedious, but Python makes it surprisingly easy to build a GUI-based file explorer. In this tutorial, we'll walk through creating FileMate Explorer, a fully functional file manager using Tkinter and sv_ttk (a modern theming library for Tkinter).
Here's what FileMate Explorer can do:
Browse folders and files.
Open files or folders directly.
Copy, paste, delete, and rename files.
Create new folders.
Toggle between light and dark themes.
Drag-and-drop support for moving files into folders.
Context menu on right-click for quick actions.
Full Script: FileMate_Explorer.py
import sys
import os
import shutil
import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import sv_ttk
# =========================
# Helpers
# =========================
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 Explorer")
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 = []
# =========================
# Status & Theme 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")
# =========================
# File Management Functions
# =========================
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 get_selected_items():
items = []
for idx in file_listbox.curselection():
item_text = file_listbox.get(idx)
item_name = item_text[2:]
items.append(os.path.join(current_path_var.get(), item_name))
return items
def open_selected():
items = get_selected_items()
for path in items:
if os.path.isdir(path):
refresh_file_list(path)
break
else:
try:
os.startfile(path)
set_status(f"Opened '{os.path.basename(path)}'")
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_items():
items = get_selected_items()
if not items:
return
names = ', '.join([os.path.basename(i) for i in items])
if messagebox.askyesno("Confirm Delete", f"Are you sure you want to delete:\n{names}?"):
for path in items:
try:
if os.path.isdir(path):
shutil.rmtree(path)
else:
os.remove(path)
except Exception as e:
messagebox.showerror("Error", str(e))
refresh_file_list()
set_status(f"Deleted selected items")
def rename_item():
items = get_selected_items()
if len(items) != 1:
messagebox.showinfo("Rename", "Please select a single item to rename.")
return
old_path = items[0]
old_name = os.path.basename(old_path)
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.rename(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_items():
global clipboard
clipboard = get_selected_items()
if clipboard:
set_status(f"Copied {len(clipboard)} item(s)")
def paste_items():
global clipboard
if not clipboard:
return
for item in clipboard:
dest = os.path.join(current_path_var.get(), os.path.basename(item))
try:
if os.path.isdir(item):
shutil.copytree(item, dest)
else:
shutil.copy2(item, dest)
except Exception as e:
messagebox.showerror("Error", str(e))
refresh_file_list()
set_status(f"Pasted {len(clipboard)} item(s)")
# =========================
# Drag & Drop Support
# =========================
drag_data = {"item": None}
def on_start_drag(event):
selection = file_listbox.curselection()
if selection:
drag_data["item"] = get_selected_items()
def on_drop(event):
if not drag_data["item"]:
return
drop_index = file_listbox.nearest(event.y)
dest_item_text = file_listbox.get(drop_index)
dest_name = dest_item_text[2:]
dest_path = os.path.join(current_path_var.get(), dest_name)
if not os.path.isdir(dest_path):
messagebox.showinfo("Drag & Drop", "You can only drop onto a folder.")
return
for item in drag_data["item"]:
try:
shutil.move(item, os.path.join(dest_path, os.path.basename(item)))
except Exception as e:
messagebox.showerror("Error", str(e))
drag_data["item"] = None
refresh_file_list()
set_status("Moved items successfully")
# =========================
# 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=delete_items)
context_menu.add_command(label="Rename", command=rename_item)
context_menu.add_separator()
context_menu.add_command(label="Copy", command=copy_items)
context_menu.add_command(label="Paste", command=paste_items)
def show_context_menu(event):
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)
context_menu.grab_release()
# =========================
# GUI Layout
# =========================
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_items).pack(side="left", padx=5)
ttk.Button(toolbar, text="Copy", command=copy_items).pack(side="left", padx=5)
ttk.Button(toolbar, text="Paste", command=paste_items).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), selectmode=tk.EXTENDED)
file_listbox.pack(side="left", expand=True, fill="both")
file_listbox.bind("<Double-Button-1>", lambda e: open_selected())
file_listbox.bind("<Button-3>", show_context_menu)
file_listbox.bind("<ButtonPress-1>", on_start_drag)
file_listbox.bind("<ButtonRelease-1>", on_drop)
scrollbar = ttk.Scrollbar(file_frame, orient="vertical", command=file_listbox.yview)
scrollbar.pack(side="right", fill="y")
file_listbox.config(yscrollcommand=scrollbar.set)
refresh_file_list()
root.mainloop()
📝 Key Features Highlight
Drag-and-drop support: Move files/folders into other folders by dragging.
Clipboard operations: Copy and paste multiple files at once.
Dark mode toggle: Switch between light and dark themes.
Context menu: Right-click files to quickly open, delete, rename, copy, or paste.
Real-time status updates: See what action is being performed at the bottom of the window.
Tools & Libraries Used:
tkinter – Python's built-in GUI library.
sv_ttk – A modern theme for Tkinter widgets.
shutil – For copying and moving files.
os – For interacting with the file system.
With FileMate Explorer, you now have a fully functional file manager built in Python. Customize it further by adding features like search, file previews, or integration with cloud storage.
Top comments (0)