Smart Directory Visualizer

import os

import sys

import tkinter as tk

from tkinter import ttk, filedialog, messagebox

import subprocess

import platform


# -------------------------------------------------

# Utility: Open files/folders with default app

# -------------------------------------------------

def open_path(path):

    try:

        if platform.system() == "Windows":

            os.startfile(path)

        elif platform.system() == "Darwin":

            subprocess.Popen(["open", path])

        else:

            subprocess.Popen(["xdg-open", path])

    except Exception as e:

        messagebox.showerror("Error", f"Cannot open:\n{e}")


# -------------------------------------------------

# Main App

# -------------------------------------------------

class DirectoryVisualizer:

    def __init__(self, root):

        self.root = root

        self.root.title("Smart Directory Visualizer")

        self.root.geometry("800x500")


        self.create_ui()


    def create_ui(self):

        top = ttk.Frame(self.root)

        top.pack(fill="x", padx=10, pady=10)


        ttk.Button(top, text="Select Folder", command=self.choose_folder).pack(side="left")


        self.path_label = ttk.Label(top, text="No folder selected")

        self.path_label.pack(side="left", padx=10)


        # Treeview

        self.tree = ttk.Treeview(self.root)

        self.tree.pack(fill="both", expand=True, padx=10, pady=10)


        self.tree.heading("#0", text="Folder Structure", anchor="w")


        # Scrollbar

        scrollbar = ttk.Scrollbar(self.tree, orient="vertical", command=self.tree.yview)

        self.tree.configure(yscrollcommand=scrollbar.set)

        scrollbar.pack(side="right", fill="y")


        # Events

        self.tree.bind("<<TreeviewOpen>>", self.on_expand)

        self.tree.bind("<Double-1>", self.on_double_click)


    # -------------------------------------------------

    # Folder selection

    # -------------------------------------------------

    def choose_folder(self):

        folder = filedialog.askdirectory()

        if not folder:

            return


        self.path_label.config(text=folder)

        self.tree.delete(*self.tree.get_children())


        root_node = self.tree.insert("", "end", text=folder, open=False, values=[folder])

        self.tree.set(root_node, "fullpath", folder)

        self.add_dummy(root_node)


    # -------------------------------------------------

    # Lazy loading helpers

    # -------------------------------------------------

    def add_dummy(self, parent):

        self.tree.insert(parent, "end", text="Loading...")


    def on_expand(self, event):

        node = self.tree.focus()

        self.load_children(node)


    def load_children(self, parent):

        fullpath = self.get_fullpath(parent)


        # Remove dummy

        self.tree.delete(*self.tree.get_children(parent))


        try:

            for name in sorted(os.listdir(fullpath)):

                path = os.path.join(fullpath, name)

                node = self.tree.insert(parent, "end", text=name, open=False)

                self.tree.set(node, "fullpath", path)


                if os.path.isdir(path):

                    self.add_dummy(node)

        except PermissionError:

            pass


    def get_fullpath(self, item):

        parent = self.tree.parent(item)

        if parent:

            return os.path.join(self.get_fullpath(parent), self.tree.item(item, "text"))

        else:

            return self.tree.item(item, "text")


    # -------------------------------------------------

    # Open file/folder on double click

    # -------------------------------------------------

    def on_double_click(self, event):

        item = self.tree.focus()

        path = self.get_fullpath(item)

        if os.path.exists(path):

            open_path(path)


# -------------------------------------------------

# RUN

# -------------------------------------------------

if __name__ == "__main__":

    root = tk.Tk()

    app = DirectoryVisualizer(root)

    root.mainloop()


No comments: