Basic File Explorer
Abstract
Section titled “Abstract”A file explorer is one of the most universally useful desktop GUIs you can build. In this project you will build a Python file viewer with Tkinter that lets the user pick any file via the OS file dialog, then displays its contents in a scrollable text area. Then we grow it: editable buffer, save-back, a directory tree, recent-files list, encoding detection, and a syntax-highlighted code view.
You will learn:
- How
filedialogintegrates with the native OS picker. - How to read text safely with the correct encoding.
- How to wire scrollbars to a
Textwidget. - The
gridvspackdecision for layout. - How to add real features (editing, saving, a directory tree) one at a time.
Prerequisites
Section titled “Prerequisites”- Python 3.6 or above.
- A text editor or IDE.
- Tkinter (ships with most Python installs).
- Comfort with classes (optional but recommended).
On Linux you may need to install Tkinter separately:
sudo apt-get install python3-tk # Debian/Ubuntu
sudo dnf install python3-tkinter # FedoraGetting Started
Section titled “Getting Started”Create the project
Section titled “Create the project”- Create folder
file-explorer. - Inside, create
fileexplorer.py.
Write the code
Section titled “Write the code”File Explorer
pch.viewSource# Basic File Explorer in Python
import tkinter as tk # pip install tk
from tkinter import *
from tkinter import filedialog, Text
root = tk.Tk()
apps = []
def browseFiles():
output.delete('1.0', END)
filename = filedialog.askopenfilename(initialdir = "/", title = "Select a File", filetypes = (("Text files","*.txt*"), ("all files", "*.*")))
pathh.config(text = "Path: " + filename)
tf = open(filename, encoding="utf8")
data = tf.read()
output.insert(END, data)
tf.close()
root.title("File Explorer")
root.geometry("700x700")
root.config(background="white")
label_file_explorer = Label(root, text = "File Explorer using Tkinter", width = 100, height = 3, fg = "gray", bg = "whitesmoke")
button_explore = Button(root, text = "Browse Files", command = browseFiles)
exit_button = Button(root, text = "Exit", command = root.destroy)
output = Text(root)
pathh = Label(root, text = "Path: ", width = 100, height = 3, fg = "gray", bg = "whitesmoke")
label_file_explorer.grid(column = 1, row = 1)
button_explore.grid(column = 1, row = 2)
exit_button.grid(column = 1, row = 3)
output.grid(column = 1, row = 4)
pathh.grid(column = 1, row = 5)
root.mainloop() Run it
Section titled “Run it”C:\Users\Your Name\file-explorer> python fileexplorer.py
# Window opens with:
# - Title bar "File Explorer using Tkinter"
# - "Browse Files" button
# - "Exit" button
# - Large text area
# - "Path: ..." label at the bottomClick Browse Files, pick any text file, and its content appears in the panel.
How it fits together
Section titled “How it fits together”Read from the top: this is what runs when you execute the file, and which function calls which. It is generated from the code, so it cannot drift from it.
flowchart TD
RUN(["python fileexplorer.py"])
browseFiles("browseFiles")
RUN --> browseFiles
Step-by-Step Explanation
Section titled “Step-by-Step Explanation”1. Imports and root window
Section titled “1. Imports and root window”import tkinter as tk
from tkinter import filedialog, Text, Label, Button, END
root = tk.Tk()
root.title("File Explorer")
root.geometry("700x700")
root.config(background="white")Tk()creates the main window.geometry("WxH")sets size in pixels.- Importing names like
Text,Label,Buttondirectly is convenient — but only inside small scripts. For larger projects usetk.Text,tk.Label, etc.
2. The browse callback
Section titled “2. The browse callback”def browseFiles():
output.delete("1.0", END)
filename = filedialog.askopenfilename(
initialdir="/",
title="Select a File",
filetypes=(("Text files", "*.txt"), ("All files", "*.*")),
)
if not filename:
return # user cancelled
path_label.config(text=f"Path: {filename}")
try:
with open(filename, encoding="utf-8") as f:
data = f.read()
except UnicodeDecodeError:
output.insert(END, "[binary or non-UTF-8 file]")
return
output.insert(END, data)Three improvements over the naive version:
- Cancel-safe.
askopenfilenamereturns""when the user closes the dialog; we bail early. with open(...)closes the file even on exception.UnicodeDecodeErrorhandler. Lets the program survive binary or weird-encoding files.
3. The widgets
Section titled “3. The widgets”title_label = Label(root, text="File Explorer using Tkinter",
width=100, height=3, fg="gray", bg="whitesmoke")
browse_button = Button(root, text="Browse Files", command=browseFiles)
exit_button = Button(root, text="Exit", command=root.destroy)
output = Text(root, wrap="none")
path_label = Label(root, text="Path: ", width=100, height=3, fg="gray", bg="whitesmoke")Text(root)is a multi-line text widget; the line/character coordinates work like"line.char"("1.0"means line 1, char 0).wrap="none"shows long lines with horizontal scrolling rather than wrapping.
4. Layout with grid
Section titled “4. Layout with grid”title_label.grid(column=1, row=1)
browse_button.grid(column=1, row=2)
exit_button.grid(column=1, row=3)
output.grid(column=1, row=4)
path_label.grid(column=1, row=5)
root.mainloop()grid arranges widgets in cells. Even with one column this gives a tidy vertical stack.
Add Scrollbars
Section titled “Add Scrollbars”A Text widget without scrollbars is unusable for big files. Wire one up:
vsb = tk.Scrollbar(root, orient="vertical", command=output.yview)
hsb = tk.Scrollbar(root, orient="horizontal", command=output.xview)
output.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
vsb.grid(column=2, row=4, sticky="ns")
hsb.grid(column=1, row=6, sticky="ew")The bidirectional binding — command=output.yview plus yscrollcommand=vsb.set — connects scrollbar and text so they stay in sync.
Make It Editable + Save
Section titled “Make It Editable + Save”Tkinter’s Text widget is already editable by default. To save changes back:
def save_file():
if not current_path:
save_file_as()
return
try:
with open(current_path, "w", encoding="utf-8") as f:
f.write(output.get("1.0", END))
except OSError as e:
path_label.config(text=f"Save failed: {e}")
def save_file_as():
path = filedialog.asksaveasfilename(defaultextension=".txt")
if path:
global current_path
current_path = path
save_file()output.get("1.0", END)returns the entire text content.defaultextension=".txt"ensures users get a sensible extension if they forget.
Encoding Detection
Section titled “Encoding Detection”Real files come in many encodings. chardet can guess:
pip install chardetimport chardet
with open(filename, "rb") as f:
raw = f.read()
enc = chardet.detect(raw)["encoding"] or "utf-8"
data = raw.decode(enc, errors="replace")errors="replace" substitutes a replacement character for un-decodable bytes instead of crashing.
Add a Directory Tree
Section titled “Add a Directory Tree”For a “real” explorer, show a tree on the left:
from tkinter import ttk
import os
tree = ttk.Treeview(root)
tree.grid(column=0, row=4, sticky="ns")
def populate(parent, path):
for entry in sorted(os.listdir(path)):
full = os.path.join(path, entry)
node = tree.insert(parent, "end", text=entry, values=[full])
if os.path.isdir(full):
tree.insert(node, "end") # placeholder for expansion
def on_open(event):
node = tree.focus()
tree.delete(*tree.get_children(node))
populate(node, tree.item(node, "values")[0])
tree.bind("<<TreeviewOpen>>", on_open)
populate("", "/")Lazy expansion (insert one placeholder child, populate only when opened) keeps the UI snappy for huge directories.
Common Mistakes
Section titled “Common Mistakes”| Problem | Cause | Fix |
|---|---|---|
| App crashes on cancel | filename is "" | if not filename: return |
UnicodeDecodeError on binary file | Default encoding | Use try/except UnicodeDecodeError |
| Old content still shown | Forgot to clear the widget | output.delete("1.0", END) first |
| Scrollbar does not move | Did not wire yscrollcommand | Bidirectional binding both ways |
| Large file freezes UI | Reading 1 GB into a Text widget | Stream / paginate, or refuse over a threshold |
Variations to Try
Section titled “Variations to Try”1. Recent files
Section titled “1. Recent files”Store the last 10 opened paths in a JSON file; show as a dropdown.
2. Syntax highlighting
Section titled “2. Syntax highlighting”Use Pygments to color Python/JS/HTML code:
from pygments import lex
from pygments.lexers import get_lexer_for_filename
for token, value in lex(data, get_lexer_for_filename(filename)):
output.insert(END, value, str(token))Define tag colors with output.tag_configure("Token.Keyword", foreground="blue").
3. Find / Replace
Section titled “3. Find / Replace”A small dialog with two Entry widgets and Tkinter’s built-in Text search:
idx = output.search(term, "1.0", stopindex=END)
if idx:
output.tag_add("sel", idx, f"{idx}+{len(term)}c")
output.see(idx)4. Multiple tabs
Section titled “4. Multiple tabs”Use ttk.Notebook so each opened file lives in its own tab.
5. Image preview
Section titled “5. Image preview”For .png/.jpg files, swap the Text widget for a Label showing a PIL.ImageTk.PhotoImage.
6. CSV viewer
Section titled “6. CSV viewer”When opening a .csv, parse and display in a ttk.Treeview table instead of plain text.
7. Markdown preview
Section titled “7. Markdown preview”On open, render Markdown to HTML with pip install markdown and show in a side panel (use tkhtmlview for an HTML widget).
8. Drag & drop
Section titled “8. Drag & drop”With tkinterdnd2, dropping a file onto the window opens it.
9. Recent edits tracker
Section titled “9. Recent edits tracker”Compare on-open content with on-save content and show diff stats (lines added/removed).
10. File operations
Section titled “10. File operations”Right-click context menu: Copy, Move, Rename, Delete — wire to shutil.move, os.remove, etc. Confirm before destructive operations.
Architecture: Move to a Class
Section titled “Architecture: Move to a Class”When the script grows past 100 lines, refactor:
class FileExplorer:
def __init__(self, root):
self.root = root
self.current_path = None
self._build_ui()
def _build_ui(self): ...
def browse(self): ...
def save(self): ...Everything else stays the same — but state lives on self, not module globals, and you can test the class without launching a window.
Real-World Applications
Section titled “Real-World Applications”- In-product file viewers — log viewers, debug consoles.
- Internal tools — quick text viewers for ops teams.
- Educational software — viewing student submissions.
- Configuration editors — load YAML/INI, validate on save.
- Prototype IDEs — every editor started as a file viewer.
Educational Value
Section titled “Educational Value”- Tkinter widgets — buttons, labels, text, frames, scrollbars.
- OS file dialogs — leveraging the native UI.
- Encoding handling — what is at stake when the bytes are not ASCII.
- Event-driven design — callbacks rather than top-down scripts.
- Layout reasoning —
gridvspack,sticky, weights.
Next Steps
Section titled “Next Steps”- Add scrollbars (most important quick win).
- Make the buffer editable + saveable.
- Add encoding detection with
chardet. - Add a directory tree with
ttk.Treeview. - Layer syntax highlighting with Pygments.
- Refactor into a class.
- See Basic Text Editor for the next evolution — a full editor with undo/redo, find/replace, and a menu bar.
Conclusion
Section titled “Conclusion”You built a working file viewer, fixed three real-world bugs (cancel, encoding, missing scrollbars), and have a road map for turning it into a real editor. Tkinter is unfashionable but its model — widgets, callbacks, the event loop — matches every other GUI framework you will meet. Full source on GitHub. Explore more GUI projects on Python Central Hub.
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading