Implement renaming of the tksheet Headers in the gui when the user double clicks the header a field should open and the user can type in the new header name. a import tkinter as tk
from tksheet import Sheet
import json
from pathlib import Path
import ctypes

def set_dpi_awareness():
    """Enable DPI awareness to ensure high DPI settings are supported."""
    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")
        
        # Sidebar Frame
        self.sidebar = tk.Frame(root, width=200, bg='#F7F8FC', bd=0, relief='flat')
        self.sidebar.pack(expand=False, fill='y', side='left', anchor='nw')
    
        # Separator Frame
        self.separator = tk.Frame(root, width=100, bg='white', bd=0, relief='flat')
        self.separator.pack(expand=False, fill='y', side='left', anchor='nw')
    
        # Main Frame
        self.main_frame = tk.Frame(root)
        self.main_frame.pack(expand=True, fill='both', side='right')
    
        # Header Frame with Filter
        self.header_frame = tk.Frame(self.main_frame, height=50, bg='white')
        self.header_frame.pack(fill='x')
    
        self.filter_label = tk.Label(self.header_frame, text="Filter:", bg='white', anchor="w")
        self.filter_label.pack(side='left', padx=5, pady=10)
    
        self.filter_entry = tk.Entry(self.header_frame)
        self.filter_entry.pack(side='left', padx=5, pady=10)
        self.filter_entry.bind("<KeyRelease>", lambda event: self.apply_filter())
    
        # Table Frame
        self.table_frame = tk.Frame(self.main_frame, bg='white')
        self.table_frame.pack(expand=True, fill='both')
    
        # New Table Button
        self.new_table_button = tk.Button(
            self.sidebar,
            text="New Table",
            command=self.create_new_table,
            bg='#F7F8FC',
            borderwidth=0,
            highlightthickness=0,
            relief='flat'
        )
        self.new_table_button.pack(padx=10, pady=10, fill='x')
    
        # Listbox for Tables
        self.table_listbox = tk.Listbox(
            self.sidebar,
            bg='#F7F8FC',
            borderwidth=0,
            highlightthickness=0,
            relief='flat',
            selectbackground='#EAEDEF',
            selectforeground='black',
            activestyle='none'
        )
        self.table_listbox.pack(expand=True, fill='both', padx=10, pady=10)
        self.table_listbox.bind('<<ListboxSelect>>', self.load_selected_table)
        self.table_listbox.bind('<Double-1>', self.begin_rename_table)
    
        # Initialization
        self.save_file_dir = Path("tables")
        self.tables = {}
        self.original_data = {}
    
        self.rename_entry = None
        if not self.save_file_dir.exists():
            self.save_file_dir.mkdir()
        self.load_tables()
    
    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)
    
    def add_table(self, table_name, data=None):
        """Add a table to the UI and internal storage."""
        headers = [f"Column {i+1}" for i in range(5)]
        data = data if data else [["" for _ in range(5)] for _ in range(10)]
        
        sheet = Sheet(
            self.table_frame,
            headers=headers,
            data=data,
            show_header=True
        )
        sheet.grid(row=0, column=0, sticky='nsew')
        sheet.enable_bindings((
            "single_select",
            "row_select",
            "column_select",
            "column_drag_and_drop",
            "row_drag_and_drop",
            "reorder_columns",
            "reorder_rows",
            "delete_row",
            "delete_column",
            "insert_row",
            "insert_column",
            "undo",
            "edit_cell"
        ))
        sheet.extra_bindings([
            ("end_edit_cell", lambda event, table_name=table_name: self.save_data(table_name)),
            ("column_header_double_click", lambda event, table_name=table_name: self.rename_header(event, table_name))
        ])
        
        self.tables[table_name] = sheet
        self.original_data[table_name] = data  # Store original data for filtering
        self.table_listbox.insert('end', table_name)
    
    def rename_header(self, event, table_name):
        """Enable renaming of header columns."""
        # Get the column index from the event
        col_index = event[1]
        header_text = self.tables[table_name].headers[col_index]
    
        # Get the header cell's bounding box
        bbox = self.tables[table_name].get_header_bbox(col_index)
        if not bbox:
            return
    
        x, y, width, height = bbox
        # Convert to absolute position
        abs_x = self.tables[table_name].winfo_rootx() + x
        abs_y = self.tables[table_name].winfo_rooty() + y
    
        # Create an entry widget for renaming
        self.rename_entry = tk.Entry(
            self.table_frame,
            bg='#EAEDEF',
            fg='black',
            borderwidth=1,
            relief='solid'
        )
        self.rename_entry.place(x=x, y=y, width=width, height=height)
        self.rename_entry.insert(0, header_text)
        self.rename_entry.focus()
    
        # Bind events to handle renaming
        self.rename_entry.bind('<Return>', lambda _: self.end_rename_header(table_name, col_index))
        self.rename_entry.bind('<FocusOut>', lambda _: self.cancel_rename_header())
    
    def end_rename_header(self, table_name, col_index):
        """Finish renaming a header column."""
        new_header = self.rename_entry.get().strip()
        if new_header:
            headers = self.tables[table_name].headers()
            headers[col_index] = new_header
            self.tables[table_name].headers(headers)
            self.tables[table_name].refresh()
            self.save_data(table_name)  # Optionally save after renaming
        self.rename_entry.destroy()
        self.rename_entry = None
    
    def cancel_rename_header(self):
        """Cancel the renaming of a header column."""
        if self.rename_entry:
            self.rename_entry.destroy()
            self.rename_entry = None
    
    def begin_rename_table(self, event):
        """Begin renaming of the table name in the listbox."""
        selection = self.table_listbox.curselection()
        if selection:
            index = selection[0]
            current_name = self.table_listbox.get(index)
    
            bbox = self.table_listbox.bbox(index)
            if not bbox:
                return
    
            listbox_x = self.table_listbox.winfo_rootx() - self.root.winfo_rootx()
            listbox_y = self.table_listbox.winfo_rooty() - self.root.winfo_rooty()
    
            bbox_x, bbox_y, bbox_width, bbox_height = bbox
            entry_x = bbox_x
            entry_y = bbox_y
    
            self.rename_entry = tk.Entry(
                self.sidebar,
                bg='#EAEDEF',
                fg='black',
                selectbackground='#EAEDEF',
                selectforeground='black',
                borderwidth=1,
                relief='solid'
            )
    
            entry_width = self.table_listbox.winfo_width()
    
            self.rename_entry.place(
                x=entry_x,
                y=entry_y,
                width=entry_width,
                height=bbox_height
            )
            self.rename_entry.insert(0, current_name)
            self.rename_entry.focus()
            self.rename_entry.configure(font=self.table_listbox.cget("font"))
            self.rename_entry.bind('<Return>', lambda _: self.end_rename_table(index))
            self.rename_entry.bind('<FocusOut>', lambda _: self.cancel_rename_table())
    
    def end_rename_table(self, index):
        """Finish the renaming process and update the table name."""
        new_name = self.rename_entry.get().strip()
        current_name = self.table_listbox.get(index)
    
        if new_name and new_name != current_name and new_name not in self.tables:
            self.tables[new_name] = self.tables.pop(current_name)
            self.original_data[new_name] = self.original_data.pop(current_name)
    
            # Update the listbox
            self.table_listbox.delete(index)
            self.table_listbox.insert(index, new_name)
    
            # Save the renamed table's data if you're persisting it
            old_file = self.save_file_dir / f"{current_name}.json"
            new_file = self.save_file_dir / f"{new_name}.json"
    
            if old_file.exists():
                old_file.rename(new_file)
            else:
                # Optionally, handle the case where the old file doesn't exist
                pass
    
        self.rename_entry.destroy()
        self.rename_entry = None
    
    def cancel_rename_table(self):
        """Cancel the renaming process."""
        if self.rename_entry:
            self.rename_entry.destroy()
            self.rename_entry = 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)
                self.add_table(table_name, data)
    
    def load_selected_table(self, event=None):
        """Display the selected table and hide others."""
        selection = self.table_listbox.curselection()
        for table_name, sheet in self.tables.items():
            sheet.grid_remove()
    
        if selection:
            selected_table_name = self.table_listbox.get(selection[0])
            self.tables[selected_table_name].grid()
            self.apply_filter()
        else:
            self.table_frame.configure(bg='white')
    
    def save_data(self, table_name):
        """Save current data of a table to a file."""
        data = 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."""
        term = self.filter_entry.get().lower()
        selection = self.table_listbox.curselection()
        if selection:
            table_name = self.table_listbox.get(selection[0])
            original_data = self.original_data[table_name]
    
            if term:
                filtered_data = [
                    row for row in original_data 
                    if any(term in str(cell).lower() for cell in row)
                ]
            else:
                filtered_data = original_data
    
            self.tables[table_name].set_sheet_data(filtered_data)
            self.tables[table_name].refresh()

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