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. USER: import tkinter as tk
import tkinter.ttk as ttk
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("1920x1080")
        self.root.wm_minsize(1200, 560)
        self.config_fonts_and_colors()
        self.initialize_data()
        self.setup_ui()
        self.load_tables()

    def config_fonts_and_colors(self):
        """Configure fonts and colors."""
        font_name, font_size, font_style = "Literata", 12, "normal"
        self.default_font = (font_name, font_size, font_style)
        self.root.option_add("*Font", f"{font_name} {font_size}")
        self.TEXT_COLOR, self.BACKGROUND_COLOR = '#333333', '#FFFFFF'
        self.SIDEBAR_COLOR, self.BUTTON_COLOR = '#F7F8FC', '#F7F8FC'
        self.BUTTON_COLOR_SELECTED = '#EAEDEF'

    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."""
        # Create the main sidebar frame
        sidebar = self._create_frame(
            parent=self.root,
            width=self.SIDEBAR_WIDTH,
            bg=self.SIDEBAR_COLOR,
            side='left',
            anchor='nw',
            fill='y'
        )

        # Create an additional static frame next to the sidebar
        self._create_frame(
            parent=self.root,
            width=self.STATIC_FRAME_WIDTH,
            bg=self.BACKGROUND_COLOR,
            side='left',
            anchor='nw',
            fill='y'
        )

        # Add components to the sidebar
        self._add_normal_buttons(sidebar)
        self._add_scrollable_canvas(sidebar)
        self._add_delete_table_section(sidebar)

    def _create_frame(self, parent, width, bg, side, anchor, fill, expand=False):
        """Helper method to create and pack a frame."""
        frame = tk.Frame(parent, width=width, bg=bg, bd=0, relief='flat')
        frame.pack(side=side, anchor=anchor, fill=fill, expand=expand)
        return frame

    def _add_normal_buttons(self, sidebar):
        """Add the normal buttons section to the sidebar."""
        # Create a frame for normal buttons
        normal_buttons_frame = self._create_frame(
            parent=sidebar,
            width=None,
            bg=self.SIDEBAR_COLOR,
            side='top',
            anchor='nw',
            fill='x'
        )

        # Add "Create New Table" clickable label
        create_table_label = self._create_clickable_label(
            parent=normal_buttons_frame,
            text="Create New Table",
            command=self.create_new_table
        )
        create_table_label.pack(fill='x', padx=0, pady=0)
        self.add_custom_hover(create_table_label)

        # Add a separator below the button
        self._add_separator(normal_buttons_frame)

    def _add_scrollable_canvas(self, sidebar):
        """Add a scrollable canvas to the sidebar."""
        # Create a frame to hold the canvas and scrollbar
        scrollbar_frame = self._create_frame(
            parent=sidebar,
            width=None,
            bg=self.SIDEBAR_COLOR,
            side='top',
            anchor='nw',
            fill='both',
            expand=True
        )

        # Create the canvas
        canvas = tk.Canvas(
            scrollbar_frame,
            bg=self.SIDEBAR_COLOR,
            highlightthickness=0
        )
        canvas.pack(side="left", fill="both", expand=True)

        # Create the scrollbar
        scrollbar = ttk.Scrollbar(
            scrollbar_frame,
            orient="vertical",
            command=canvas.yview
        )
        scrollbar.pack(side="right", fill="y")

        # Configure the canvas to use the scrollbar
        canvas.configure(yscrollcommand=scrollbar.set)

        # Create a frame inside the canvas to hold table labels
        self.table_labels_frame = tk.Frame(canvas, bg=self.SIDEBAR_COLOR)
        canvas.create_window((0, 0), window=self.table_labels_frame, anchor="nw")

        # Bind events to adjust the scrollbar visibility
        self._bind_scrollbar_events(canvas, scrollbar, scrollbar_frame)

    def _bind_scrollbar_events(self, canvas, scrollbar, scrollbar_frame):
        """Bind configuration events to manage scrollbar visibility."""
        def configure_scrollbar(event=None):
            canvas.update_idletasks()
            canvas_height = scrollbar_frame.winfo_height()
            content_height = self.table_labels_frame.winfo_reqheight()

            if content_height > canvas_height and not scrollbar.winfo_viewport():
                scrollbar.pack(side="right", fill="y")
            elif content_height <= canvas_height:
                scrollbar.pack_forget()

            canvas.configure(scrollregion=canvas.bbox("all"))

        self.table_labels_frame.bind("<Configure>", configure_scrollbar)
        scrollbar_frame.bind("<Configure>", configure_scrollbar)

    def _add_delete_table_section(self, sidebar):
        """Add the delete table section to the sidebar."""
        # Add a separator
        self._add_separator(sidebar)

        # Add "Delete Selected Table" clickable label
        delete_table_label = self._create_clickable_label(
            parent=sidebar,
            text="Delete Selected Table",
            command=self.delete_table
        )
        delete_table_label.pack(fill='x', padx=0, pady=0, side='bottom')
        self.add_custom_hover(delete_table_label)

    def _add_separator(self, parent):
        """Helper method to add a horizontal separator."""
        separator = ttk.Separator(parent, orient='horizontal')
        separator.pack(fill='x', pady=0)

    def _create_clickable_label(self, parent, text, command):
        """Helper method to create a clickable label."""
        label = tk.Label(parent, text=text, bg=self.SIDEBAR_COLOR, cursor="hand2")
        label.bind("<Button-1>", lambda e: command())
        return 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."""
        header_frame = tk.Frame(self.main_frame, height=50, bg=self.BACKGROUND_COLOR)
        header_frame.pack(fill='x')
    
        tk.Label(header_frame, text="Filter:", bg=self.BACKGROUND_COLOR, fg=self.TEXT_COLOR).pack(side='left', padx=5, pady=10)
        self.filter_entry = tk.Entry(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())
    
        rename_headers = self.create_clickable_label(header_frame, "Rename Headers", self.rename_headers)
        rename_headers.pack(side='left', padx=5, pady=10, fill='x')
    
        import_table = self.create_clickable_label(header_frame, "Import Table", self.import_file)
        import_table.pack(side='right', padx=5, pady=10, fill='x')
    
        export_table = self.create_clickable_label(header_frame, "Export Table", self.export_table)
        export_table.pack(side='right', 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,
            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

        try:
            if file_path.endswith('.csv'):
                with open(file_path, newline='', encoding='utf-8') as csvfile:
                    data = list(csv.reader(csvfile))
            elif file_path.endswith('.json'):
                with open(file_path, 'r', encoding='utf-8') as jsonfile:
                    data = json.load(jsonfile)
            else:
                raise ValueError("Unsupported file format.")

            headers, table_data = data[0], data[1:]
            self.add_table_from_data(file_path, headers, table_data)

        except Exception as e:
            messagebox.showerror("File Import Error", str(e))

    def add_table_from_data(self, file_path, headers, table_data):
        """Add a table with existing data."""
        table_name = Path(file_path).stem
        table_name = self.ensure_unique_table_name(table_name)

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

    def ensure_unique_table_name(self, table_name):
        """Ensure that the table name is unique."""
        if table_name in self.tables:
            table_name += " (New)"
            while table_name in self.tables:
                table_name += " (New)"
        return 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

        try:
            data = [self.headers[self.current_table_name]] + self.tables[self.current_table_name].get_sheet_data()
            if file_path.endswith('.csv'):
                self.save_as_csv(file_path, data)
            elif file_path.endswith('.json'):
                self.save_as_json(file_path, data)
            else:
                raise ValueError("Unsupported file format.")
            
            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 save_as_csv(self, file_path, data):
        """Save data as a CSV file."""
        with open(file_path, 'w', newline='', encoding='utf-8') as csvfile:
            csv.writer(csvfile).writerows(data)

    def save_as_json(self, file_path, data):
        """Save data as a JSON file."""
        with open(file_path, 'w', encoding='utf-8') as jsonfile:
            json.dump(data, jsonfile, indent=4)

    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)]
        sheet = Sheet(
            self.table_frame,
            data=data or default_data,
            show_table=True,
            headers=None,
            font=self.default_font,
            header_font=self.default_font,
            auto_resize_columns=True
        )
        sheet.set_options(table_fg=self.TEXT_COLOR, header_fg=self.TEXT_COLOR)
        sheet.enable_bindings()
        sheet.extra_bindings([("end_edit_cell", lambda _, tn=table_name: self.save_data(tn))])
        sheet.pack_forget()

        self.tables[table_name] = sheet
        self.original_data[table_name] = sheet.get_sheet_data()
        self.headers.setdefault(table_name, sheet.headers())

        self.create_table_label(table_name)

    def create_table_label(self, table_name):
        """Create a clickable label for the table in the sidebar."""
        label = self.create_clickable_label(
            self.table_labels_frame, table_name,
            lambda name=table_name: self.load_selected_table(name)
        )
        label.pack(fill='x', pady=2)
        self.table_labels[table_name] = label
        self.add_custom_hover(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

        if messagebox.askyesno("Delete Table", f"Are you sure you want to delete the table '{self.current_table_name}'?"):
            self.remove_table(self.current_table_name)

    def remove_table(self, table_name):
        """Remove a table by its name."""
        self.tables[table_name].destroy()
        self.table_labels[table_name].destroy()
        del self.tables[table_name], self.original_data[table_name]
        del self.headers[table_name], self.table_labels[table_name]
        (self.save_file_dir / f"{table_name}.json").unlink(missing_ok=True)
        self.current_table_name, self.selected_table_label = None, 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 open(file_path, 'r') as file:
                data = json.load(file)
                
            headers, table_data = data.pop(0), data
            self.add_table(table_name, table_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():
            is_selected = name == table_name
            sheet.pack(expand=is_selected, fill='both') if is_selected else sheet.pack_forget()
            if is_selected:
                self.current_table_name = table_name
                sheet.headers(self.headers[name])
                self.apply_filter()
                selected_label, self.selected_table_label = self.table_labels[name], self.table_labels[name]
                selected_label.config(bg=self.BUTTON_COLOR_SELECTED)
            else:
                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."""
        if table_name not in self.table_labels:
            messagebox.showerror("Error", f"Table '{table_name}' not found.")
            return
    
        label = self.table_labels[table_name]
        if self.rename_entry:
            self.rename_entry.destroy()
    
        self.rename_entry = tk.Entry(self.table_labels_frame, bg=label.cget("bg"), fg=self.TEXT_COLOR, relief='flat')
        self.rename_entry.insert(0, table_name)
        self.rename_entry.place(x=label.winfo_x(), y=label.winfo_y(), width=label.winfo_width(), height=label.winfo_height())
        self.rename_entry.focus()
        self.rename_entry.select_range(0, tk.END)
        self.rename_entry.bind('<Return>', lambda _: self.end_rename(table_name))
        self.rename_entry.bind('<FocusOut>', lambda _: 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.update_table_name(old_name, new_name)
            self.load_selected_table(new_name)
    
        self.cancel_rename()

    def update_table_name(self, old_name, new_name):
        """Update table name and its associated resources."""
        if new_name not in self.tables:
            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)
    
            label = self.table_labels.pop(old_name)
            label.config(text=new_name)
            label.bind("<Button-1>", lambda e, tn=new_name: self.load_selected_table(tn))
            label.bind("<Double-1>", lambda e, tn=new_name: self.begin_rename(tn))  # Update to bind new name
    
            self.table_labels[new_name] = label
    
            if self.selected_table_label == label:
                self.selected_table_label.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"
            current_file.rename(new_file)
    
            self.load_selected_table(new_name)
    
    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()