Currently the datatypes menu opens a new window for the datatypes. Make it to a sidemenu for the datatypes (string, integer, float, boolean) class Header: """Combined class for handling header UI and column data types functionality.""" DATA_TYPE_OPTIONS = ["string", "integer", "float", "boolean"] def __init__(self, root: tk.Tk, app: 'TableApp'): self.root = root self.app = app self.style = app.style self.colors = app.colors self.fonts = app.fonts self.dimensions = app.dimensions self.table_name_label = None self.lower_header_frame = None self.result = None self._setup_header() def _setup_header(self) -> None: header_frame = self._setup_main_frame() self._setup_upper_section(header_frame) self._setup_lower_section(header_frame) def _setup_main_frame(self) -> tk.Frame: header_frame = create_frame(parent=self.root, width=None, bg=self.colors["background"]) header_frame.grid(row=0, column=1, columnspan=2, sticky="ew") header_frame.config(height=self.dimensions["header_height"]) header_frame.grid_propagate(False) header_frame.grid_rowconfigure(0, weight=0) header_frame.grid_rowconfigure(1, weight=0) for i in range(5): header_frame.grid_columnconfigure(i, weight=1 if i == 4 else 0) return header_frame def _setup_upper_section(self, parent: tk.Frame) -> None: upper_frame = create_frame(parent=parent, width=None, bg=self.colors["background"]) upper_frame.grid(row=0, column=0, columnspan=5, sticky="ew", pady=(0, 10)) upper_frame.grid_columnconfigure(5, weight=1) self._add_upper_content(upper_frame) def _add_upper_content(self, frame: tk.Frame) -> None: tk.Label(frame, text="Data > ", bg=self.colors["background"], fg=self.colors["header_info"], font=self.fonts["header_info"]).grid(row=0, column=0, padx=22, pady=(22, 0), sticky="nw") self.table_name_label = tk.Label(frame, text=self.app.current_table_name or "No Table Selected", bg=self.colors["background"], fg=self.colors["text"], font=self.fonts["header_info"]) self.table_name_label.grid(row=0, column=0, padx=(88, 10), pady=(22, 0), sticky="nw") self._setup_settings_section(frame) def _setup_settings_section(self, frame: tk.Frame) -> None: settings_container = tk.Frame(frame, bg=self.colors["background"]) settings_container.grid(row=0, column=5, padx=(0, 40), pady=(22, 0), sticky="ne") settings_label = tk.Label(settings_container, text="...", bg=self.colors["background"], fg=self.colors["text"], cursor="hand2", anchor="e", font=self.fonts["text"]) settings_label.pack(anchor="e") def on_enter(_): settings_label.config(bg=self.colors["button_selected"]) def on_leave(_): settings_label.config(bg=self.colors["background"]) settings_menu = self._create_settings_menu() settings_label.bind("<Button-1>", lambda e: settings_menu.post(e.x_root, e.y_root)) settings_label.bind("<Enter>", on_enter) settings_label.bind("<Leave>", on_leave) def _create_settings_menu(self) -> tk.Menu: menu = tk.Menu(self.root, tearoff=0, bg=self.colors["background"], fg=self.colors["text"], font=self.fonts["header_info"]) stats_menu = tk.Menu(menu, tearoff=0, bg=self.colors["background"], fg=self.colors["text"], font=self.fonts["header_info"]) menu.add_cascade(label="Stats", menu=stats_menu) stats_menu.add_command(label="Boolean Count: 0", command=None) self.true_counter_label = stats_menu menu_items = [ ("Export Table", self.app.table_io.export_table), ("Import Table", self.app.table_io.import_file), ("Change Datatypes", self.show_datatype_dialog) ] for label, command in menu_items: menu.add_command(label=label, command=command) return menu def _setup_lower_section(self, parent: tk.Frame) -> None: self.lower_header_frame = create_frame( parent=parent, width=None, bg=self.colors["background"] ) self.lower_header_frame.grid(row=1, column=0, columnspan=5, sticky="ew") def update_true_counter(self, count_text: str) -> None: if hasattr(self, 'true_counter_label'): self.true_counter_label.entryconfig(0, label=f"Boolean Count: {count_text}") def show_datatype_dialog(self) -> None: """Show menu for changing column data types.""" table_name = self.app.current_table_name if not table_name: messagebox.showwarning("No Table Selected", "Please select a table to change datatypes.") return current_headers = self.app.current_sheet.headers() if self.app.current_sheet else [] self.app.data_manager.headers[table_name] = current_headers data_types = self.app.data_manager.tables_data[table_name].get("data_types", []) while len(data_types) < len(current_headers): data_types.append('string') # Create popup menu self.datatype_menu = tk.Menu(self.root, tearoff=0, bg=self.colors["background"], fg=self.colors["text"], font=self.fonts["text"]) self.datatype_vars = {} # Store StringVars for each header for header, dtype in zip(current_headers, data_types): var = tk.StringVar(value=dtype) self.datatype_vars[header] = var # Create cascade menu item with current datatype self.datatype_menu.add_command( label=f"{header}: {dtype}", command=lambda h=header: self._show_type_options(h) ) # Add Apply button at the bottom self.datatype_menu.add_separator() self.datatype_menu.add_command(label="Apply Changes", command=self._apply_datatype_changes) # Show menu at cursor position self.datatype_menu.post(self.root.winfo_pointerx(), self.root.winfo_pointery()) def _show_type_options(self, header: str) -> None: """Show submenu with datatype options.""" # Create options menu options_menu = tk.Menu(self.root, tearoff=0, bg=self.colors["background"], fg=self.colors["text"], font=self.fonts["text"]) current_type = self.datatype_vars[header].get() for option in self.DATA_TYPE_OPTIONS: options_menu.add_command( label=option, command=lambda h=header, t=option: self._on_datatype_change(h, t), background=self.colors["button_selected"] if option == current_type else self.colors["background"] ) # Show options menu next to main menu options_menu.post( self.root.winfo_pointerx() + 50, self.root.winfo_pointery() ) def _on_datatype_change(self, header: str, new_type: str) -> None: """Update datatype when option is selected.""" self.datatype_vars[header].set(new_type) # Update the label in the main menu for i in range(self.datatype_menu.index('end') + 1): try: label = self.datatype_menu.entrycget(i, "label") if label.startswith(header + ":"): self.datatype_menu.entryconfigure(i, label=f"{header}: {new_type}") break except: continue # Repost the main menu to keep it open self.datatype_menu.post(self.root.winfo_pointerx(), self.root.winfo_pointery()) def _apply_datatype_changes(self) -> None: """Apply the datatype changes and update the table.""" table_name = self.app.current_table_name new_data_types = [self.datatype_vars[header].get() for header in self.app.current_sheet.headers()] self.app.data_manager.tables_data[table_name]["data_types"] = new_data_types self.app.data_manager.save_data() self.app.table_display.apply_column_data_types(new_data_types) self.app.table_display.process_and_save_current_sheet_data() self.app.refresh_table() # Unpost the menu after applying changes self.datatype_menu.unpost()