SYSTEM: You are a Python programmer tasked with refactoring code. You are proficient in Python 3.12 and use modern features where appropriate. Your code is clear, easy to read, and free of unnecessary verbosity. You follow PEP 8, 20 and 257 to ensure high quality python code. You remove comments and instead focus on simple and clear one line docstrings to describe every function. Lets think step by step. USER: import tkinter as tk
from tkinter import simpledialog, messagebox, filedialog
from tksheet import Sheet
import json
import csv
from pathlib import Path
import ctypes

def set_dpi_awareness():
    """Enable DPI awareness for high DPI settings."""
    try:
        ctypes.windll.shcore.SetProcessDpiAwareness(1)
    except Exception as e:
        print(f"Error setting DPI awareness: {e}")

class TableApp:
    def __init__(self, root):
        """Initialize the Table Editor application."""
        self.root = root
        self.root.title("Table Editor with Sidebar")
        self.root.geometry("1280x720")

        # Font Configurations
        font_name = "Literata"
        font_size = 12
        font_style = "normal"
        self.default_font = (font_name, font_size, font_style)
        self.root.option_add("*Font", f"{font_name} {font_size}")

        # Color Configurations
        self.TEXT_COLOR = '#333333'
        self.BACKGROUND_COLOR = '#FFFFFF'
        self.SIDEBAR_COLOR = '#F7F8FC'
        self.BUTTON_COLOR = '#F7F8FC'
        self.BUTTON_COLOR_SELECTED = '#EAEDEF'

        self.setup_ui()
        self.initialize_data()
        self.load_tables()

    def setup_ui(self):
        """Set up the user interface components."""
        self.create_sidebar()
        self.create_main_frame()
        self.create_header_frame()
        self.create_table_frame()

    def create_sidebar(self):
        """Create the sidebar and its components."""
        self.sidebar = tk.Frame(self.root, width=200, bg=self.SIDEBAR_COLOR, bd=0, relief='flat')
        self.sidebar.pack(expand=False, fill='y', side='left', anchor='nw')

        self.separator = tk.Frame(self.root, width=100, bg=self.BACKGROUND_COLOR, bd=0, relief='flat')
        self.separator.pack(expand=False, fill='y', side='left', anchor='nw')

        self.new_table_label = self.create_clickable_label(
            self.sidebar, "Create New Table", self.create_new_table)
        self.new_table_label.pack(padx=0, pady=10, fill='x')

        self.import_table_label = self.create_clickable_label(
            self.sidebar, "Import Table", self.import_file)
        self.import_table_label.pack(padx=0, pady=10, fill='x')

        self.export_table_label = self.create_clickable_label(
            self.sidebar, "Export Table", self.export_table)
        self.export_table_label.pack(padx=0, pady=10, fill='x')

        self.table_labels_frame = tk.Frame(self.sidebar, bg=self.SIDEBAR_COLOR)
        self.table_labels_frame.pack(expand=True, fill='both', padx=0, pady=10)

        # Add delete table option
        self.delete_table_label = self.create_clickable_label(
            self.sidebar, "Delete Selected Table", self.delete_table)
        self.delete_table_label.pack(side='bottom', padx=0, pady=10, fill='x')

        self.add_custom_hover(self.new_table_label)
        self.add_custom_hover(self.import_table_label)
        self.add_custom_hover(self.export_table_label)
        self.add_custom_hover(self.delete_table_label)

    def create_main_frame(self):
        """Create the main frame of the application."""
        self.main_frame = tk.Frame(self.root)
        self.main_frame.pack(expand=True, fill='both', side='right')

    def create_header_frame(self):
        """Create the header frame with filter and rename options."""
        self.header_frame = tk.Frame(self.main_frame, height=50, bg=self.BACKGROUND_COLOR)
        self.header_frame.pack(fill='x')

        self.filter_label = tk.Label(self.header_frame, text="Filter:", bg=self.BACKGROUND_COLOR, fg=self.TEXT_COLOR, anchor="w")
        self.filter_label.pack(side='left', padx=5, pady=10)

        self.filter_entry = tk.Entry(self.header_frame, fg=self.TEXT_COLOR)
        self.filter_entry.pack(side='left', padx=5, pady=10)
        self.filter_entry.bind("<KeyRelease>", lambda event: self.apply_filter())

        self.rename_headers_label = self.create_clickable_label(
            self.header_frame, "Rename Headers", self.rename_headers)
        self.rename_headers_label.pack(side='left', padx=5, pady=10, fill='x')

    def create_table_frame(self):
        """Create the frame to hold the table."""
        self.table_frame = tk.Frame(self.main_frame, bg=self.BACKGROUND_COLOR)
        self.table_frame.pack(expand=True, fill='both')

    def initialize_data(self):
        """Initialize data structures and variables."""
        self.save_file_dir = Path("tables")
        self.tables = {}
        self.table_labels = {}
        self.original_data = {}
        self.headers = {}
        self.current_table_name = None
        self.rename_entry = None
        self.selected_table_label = None

        self.save_file_dir.mkdir(exist_ok=True)

    def create_clickable_label(self, parent, text, command, allow_rename=True):
        """Create a clickable Label widget."""
        label = tk.Label(
            parent, text=text, bg=self.BUTTON_COLOR, fg=self.TEXT_COLOR,
            borderwidth=0, relief='flat', cursor='arrow', anchor='w',
            padx=10, pady=5
        )
        label.bind("<Button-1>", lambda e: command())
        if allow_rename:
            label.bind("<Double-1>", lambda e: self.begin_rename(text))
        return label

    def create_new_table(self):
        """Create a new table and add it to the available tables list."""
        table_name = f"Table {len(self.tables) + 1}"
        self.add_table(table_name)
        self.load_selected_table(table_name)

    def import_file(self):
        """Import a CSV or JSON file to create a new table."""
        file_path = filedialog.askopenfilename(
            title="Open File",
            filetypes=[("JSON files", "*.json"), ("CSV files", "*.csv")]
        )
        if not file_path:
            return

        if file_path.endswith('.csv'):
            with open(file_path, newline='', encoding='utf-8') as csvfile:
                reader = csv.reader(csvfile)
                data = list(reader)
            headers, table_data = data[0], data[1:]

        elif file_path.endswith('.json'):
            with open(file_path, 'r', encoding='utf-8') as jsonfile:
                data = json.load(jsonfile)
            headers, table_data = data[0], data[1:]

        else:
            messagebox.showerror("Unsupported File", "The selected file format is not supported.")
            return

        table_name = Path(file_path).stem

        if table_name in self.tables:
            table_name += " (New)"
            while table_name in self.tables:
                table_name += " (New)"

        self.add_table(table_name, table_data)
        self.headers[table_name] = headers
        self.load_selected_table(table_name)

        self.save_data(table_name)

    def export_table(self):
        """Prompt user to export the current table to CSV or JSON."""
        if not self.current_table_name:
            messagebox.showwarning("No Table Selected", "Please select a table to export.")
            return

        file_path = filedialog.asksaveasfilename(
            title="Save As",
            defaultextension=".json",
            filetypes=[("JSON files", "*.json"), ("CSV files", "*.csv")],
            initialfile=self.current_table_name
        )

        if not file_path:
            return

        data = [self.headers[self.current_table_name]] + self.tables[self.current_table_name].get_sheet_data()

        try:
            if file_path.endswith('.csv'):
                with open(file_path, 'w', newline='', encoding='utf-8') as csvfile:
                    writer = csv.writer(csvfile)
                    writer.writerows(data)
            elif file_path.endswith('.json'):
                with open(file_path, 'w', encoding='utf-8') as jsonfile:
                    json.dump(data, jsonfile, indent=4)
            else:
                messagebox.showerror("Unsupported File", "The selected file format is not supported.")
                return

            messagebox.showinfo("Export Successful", f"Table '{self.current_table_name}' has been exported successfully.")
        except Exception as e:
            messagebox.showerror("Export Failed", f"An error occurred during export: {e}")

    def add_table(self, table_name, data=None):
        """Add a table to the UI and internal storage."""
        default_data = [["" for _ in range(5)] for _ in range(10)]
        self.tables[table_name] = Sheet(
            self.table_frame,
            data=data or default_data,
            show_table=True,
            headers=None,
            font=self.default_font,  # Use default font
            header_font=self.default_font,  # Set the header font
            auto_resize_columns=True  # Enable automatic column resizing
        )

        # Set table and header foreground colors
        self.tables[table_name].set_options(
            table_fg=self.TEXT_COLOR, header_fg=self.TEXT_COLOR
        )

        # Enable necessary bindings
        self.tables[table_name].enable_bindings()

        # Bind end of cell edit to save data
        self.tables[table_name].extra_bindings([
            ("end_edit_cell", lambda event, tn=table_name: self.save_data(tn)),
        ])

        # Initially hide the sheet
        self.tables[table_name].pack_forget()
        self.original_data[table_name] = self.tables[table_name].get_sheet_data()

        if table_name not in self.headers:
            self.headers[table_name] = self.tables[table_name].headers()

        # Create a clickable label for the table in the sidebar
        table_label = self.create_clickable_label(
            self.table_labels_frame, table_name,
            lambda name=table_name: self.load_selected_table(name)
        )
        table_label.pack(fill='x', pady=2)
        self.table_labels[table_name] = table_label

        # Add hover effect to the table label
        self.add_custom_hover(table_label)

    def delete_table(self):
        """Delete the currently selected table."""
        if not self.current_table_name:
            messagebox.showwarning("No Table Selected", "Please select a table to delete.")
            return

        response = messagebox.askyesno(
            "Delete Table",
            f"Are you sure you want to delete the table '{self.current_table_name}'?"
        )
        if response:
            # Remove from the UI
            self.tables[self.current_table_name].destroy()
            self.table_labels[self.current_table_name].destroy()

            # Remove from internal storage
            del self.tables[self.current_table_name]
            del self.original_data[self.current_table_name]
            del self.headers[self.current_table_name]
            del self.table_labels[self.current_table_name]

            # Remove from disk
            file_path = self.save_file_dir / f"{self.current_table_name}.json"
            if file_path.exists():
                file_path.unlink()

            # Reset current table selection
            self.current_table_name = None
            self.selected_table_label = None

    def load_tables(self):
        """Load existing tables from the directory."""
        for file_path in self.save_file_dir.glob("*.json"):
            table_name = file_path.stem
            with file_path.open('r') as file:
                data = json.load(file)
                headers = data.pop(0)
                self.add_table(table_name, data)
                self.headers[table_name] = headers

    def load_selected_table(self, table_name):
        """Display the selected table and hide others."""
        for name, sheet in self.tables.items():
            if name == table_name:
                sheet.pack(expand=True, fill='both')
                self.current_table_name = table_name
                sheet.headers(self.headers[name])
                self.apply_filter()

                selected_label = self.table_labels[name]
                selected_label.config(bg=self.BUTTON_COLOR_SELECTED)
                self.selected_table_label = selected_label
            else:
                sheet.pack_forget()
                self.table_labels[name].config(bg=self.BUTTON_COLOR)

    def save_data(self, table_name):
        """Save current data of a table to a file."""
        data = [self.headers[table_name]] + self.tables[table_name].get_sheet_data()
        file_path = self.save_file_dir / f"{table_name}.json"
        with file_path.open('w') as file:
            json.dump(data, file)
        print(f"Data for {table_name} saved successfully.")

    def apply_filter(self):
        """Apply filtering based on the entered term."""
        if not self.current_table_name:
            return

        term = self.filter_entry.get().lower()
        original_data = self.original_data[self.current_table_name]

        filtered_data = [
            row for row in original_data
            if any(term in str(cell).lower() for cell in row)
        ]

        self.tables[self.current_table_name].set_sheet_data(filtered_data)

    def rename_headers(self):
        """Prompt user to rename headers of the currently selected table."""
        if not self.current_table_name:
            messagebox.showwarning("No Table Selected", "Please select a table to rename headers.")
            return

        current_headers = self.headers[self.current_table_name]
        new_headers = simpledialog.askstring(
            "Rename Headers",
            "Enter new headers (comma-separated):",
            initialvalue=", ".join(current_headers)
        )

        if new_headers:
            header_list = [header.strip() for header in new_headers.split(',')]
            self.headers[self.current_table_name] = header_list
            self.tables[self.current_table_name].headers(header_list)
            self.save_data(self.current_table_name)

    def begin_rename(self, table_name):
        """Begin renaming of the table with a double click."""
        label = self.table_labels[table_name]
        
        if self.rename_entry:
            self.rename_entry.destroy()
        
        label.update_idletasks()
        label_x, label_y = label.winfo_x(), label.winfo_y()
        label_width, label_height = label.winfo_width(), label.winfo_height()
        
        self.rename_entry = tk.Entry(
            self.table_labels_frame,
            bg=label.cget("bg"),
            fg=self.TEXT_COLOR,
            relief='flat',
            borderwidth=0,
            highlightthickness=0,
            justify='left'
        )
        self.rename_entry.insert(0, table_name)
        
        self.rename_entry.place(x=label_x, y=label_y, width=label_width, height=label_height)
        self.rename_entry.focus()
        self.rename_entry.select_range(0, tk.END)
        
        self.rename_entry.bind('<Return>', lambda event: self.end_rename(table_name))
        self.rename_entry.bind('<FocusOut>', lambda event: self.cancel_rename())

    def end_rename(self, old_name):
        """Finish the renaming process for the specified table."""
        new_name = self.rename_entry.get().strip()

        if not new_name:
            messagebox.showerror("Invalid Name", "Table name cannot be empty.")
            self.rename_entry.focus_set()
            return

        if new_name in self.tables and new_name != old_name:
            messagebox.showerror("Duplicate Name", f"A table named '{new_name}' already exists.")
            self.rename_entry.focus_set()
            return

        if new_name != old_name:
            self.tables[new_name] = self.tables.pop(old_name)
            self.original_data[new_name] = self.original_data.pop(old_name)
            self.headers[new_name] = self.headers.pop(old_name)

            self.table_labels[new_name] = self.table_labels.pop(old_name)
            self.table_labels[new_name].config(text=new_name)

            if self.selected_table_label == self.table_labels[new_name]:
                self.selected_table_label = self.table_labels[new_name]
                self.table_labels[new_name].config(bg=self.BUTTON_COLOR_SELECTED)

            current_file = self.save_file_dir / f"{old_name}.json"
            new_file = self.save_file_dir / f"{new_name}.json"

            if current_file.exists():
                current_file.rename(new_file)

        self.cancel_rename()

    def cancel_rename(self):
        """Cancel the renaming entry."""
        if self.rename_entry:
            self.rename_entry.destroy()
            self.rename_entry = None

    def add_custom_hover(self, label):
        """Add custom hover effects to a label."""
        def on_enter(e):
            if label != self.selected_table_label:
                label.config(bg=self.BUTTON_COLOR_SELECTED)

        def on_leave(e):
            if label != self.selected_table_label:
                label.config(bg=self.BUTTON_COLOR)

        label.bind("<Enter>", on_enter)
        label.bind("<Leave>", on_leave)

if __name__ == "__main__":
    set_dpi_awareness()
    root = tk.Tk()
    app = TableApp(root)
    root.mainloop()