Skip to content

Basic File Explorer

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 filedialog integrates with the native OS picker.
  • How to read text safely with the correct encoding.
  • How to wire scrollbars to a Text widget.
  • The grid vs pack decision for layout.
  • How to add real features (editing, saving, a directory tree) one at a time.
  • 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:

install
sudo apt-get install python3-tk        # Debian/Ubuntu
sudo dnf install python3-tkinter       # Fedora
  1. Create folder file-explorer.
  2. Inside, create fileexplorer.py.
File Explorer pch.viewSource
File Explorer
# 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()
command
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 bottom

Click Browse Files, pick any text file, and its content appears in the panel.

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.

diagram Diagram mermaid
fileexplorer.py
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, Button directly is convenient — but only inside small scripts. For larger projects use tk.Text, tk.Label, etc.
fileexplorer.py
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. askopenfilename returns "" when the user closes the dialog; we bail early.
  • with open(...) closes the file even on exception.
  • UnicodeDecodeError handler. Lets the program survive binary or weird-encoding files.
fileexplorer.py
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.
fileexplorer.py
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.

A Text widget without scrollbars is unusable for big files. Wire one up:

scroll.py
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.

Tkinter’s Text widget is already editable by default. To save changes back:

save.py
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.

Real files come in many encodings. chardet can guess:

install
pip install chardet
detect.py
import 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.

For a “real” explorer, show a tree on the left:

tree.py
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.

ProblemCauseFix
App crashes on cancelfilename is ""if not filename: return
UnicodeDecodeError on binary fileDefault encodingUse try/except UnicodeDecodeError
Old content still shownForgot to clear the widgetoutput.delete("1.0", END) first
Scrollbar does not moveDid not wire yscrollcommandBidirectional binding both ways
Large file freezes UIReading 1 GB into a Text widgetStream / paginate, or refuse over a threshold

Store the last 10 opened paths in a JSON file; show as a dropdown.

Use Pygments to color Python/JS/HTML code:

pygments.py
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").

A small dialog with two Entry widgets and Tkinter’s built-in Text search:

find.py
idx = output.search(term, "1.0", stopindex=END)
if idx:
    output.tag_add("sel", idx, f"{idx}+{len(term)}c")
    output.see(idx)

Use ttk.Notebook so each opened file lives in its own tab.

For .png/.jpg files, swap the Text widget for a Label showing a PIL.ImageTk.PhotoImage.

When opening a .csv, parse and display in a ttk.Treeview table instead of plain text.

On open, render Markdown to HTML with pip install markdown and show in a side panel (use tkhtmlview for an HTML widget).

With tkinterdnd2, dropping a file onto the window opens it.

Compare on-open content with on-save content and show diff stats (lines added/removed).

Right-click context menu: Copy, Move, Rename, Delete — wire to shutil.move, os.remove, etc. Confirm before destructive operations.

When the script grows past 100 lines, refactor:

oop.py
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.

  • 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.
  • 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 reasoninggrid vs pack, sticky, weights.
  • 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.

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.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading