# -*- coding: utf-8 -*- import tkinter as tk from tkinter import ttk from typing import Any class ScrollableFrame(ttk.Frame): """ A reusable frame with vertical and horizontal scrollbars. Configured to expand and fill the entire available space. """ def __init__(self, parent: tk.Widget, controller: Any, **kwargs): super().__init__(parent, **kwargs) self.controller = controller # Container for canvas and scrollbars self.main_container = ttk.Frame(self) self.main_container.pack(fill=tk.BOTH, expand=True) # Create Canvas self.canvas = tk.Canvas(self.main_container, highlightthickness=0) # Create Scrollbars self.v_scroll = ttk.Scrollbar( self.main_container, orient=tk.VERTICAL, command=self.canvas.yview ) self.h_scroll = ttk.Scrollbar( self, orient=tk.HORIZONTAL, command=self.canvas.xview ) # Content frame inside the canvas self.scrollable_content = ttk.Frame(self.canvas) # Bindings for dynamic resize self.scrollable_content.bind( "", lambda e: self.canvas.configure(scrollregion=self.canvas.bbox("all")) ) # Create window inside canvas self.canvas_window = self.canvas.create_window( (0, 0), window=self.scrollable_content, anchor="nw" ) # Ensure content frame matches canvas width for better layout self.canvas.bind("", self._on_canvas_configure) self.canvas.configure( yscrollcommand=self.v_scroll.set, xscrollcommand=self.h_scroll.set ) # Layout management self.v_scroll.pack(side=tk.RIGHT, fill=tk.Y) self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) self.h_scroll.pack(side=tk.BOTTOM, fill=tk.X) def _on_canvas_configure(self, event): """Adjusts the internal window width to match the canvas.""" self.canvas.itemconfig(self.canvas_window, width=event.width) def add_title_to_frame( frame: tk.Widget, text: str, side: str = 'left', thickness: int = 20, bg_color: str = 'lightgrey', font_color: str = 'black' ): """ Adds a vertical or horizontal title bar to an existing frame. """ def draw_text(event): canvas.delete("all") if side in ['left', 'right']: x_pos = thickness // 2 y_pos = event.height // 2 angle = 90 if side == 'left' else 270 anchor = 'center' else: x_pos = event.width // 2 y_pos = thickness // 2 angle = 0 anchor = 'center' canvas.create_text( x_pos, y_pos, text=text, angle=angle, anchor=anchor, fill=font_color, font=('Helvetica', thickness // 2, 'bold') ) if side in ['left', 'right']: cw, ch = thickness, 10 pack_side = side fill_type = 'y' else: cw, ch = 10, thickness pack_side = 'top' if side == 'top' else 'bottom' fill_type = 'x' canvas = tk.Canvas( frame, width=cw, height=ch, bg=bg_color, bd=0, highlightthickness=0 ) canvas.pack(side=pack_side, fill=fill_type) canvas.bind("", draw_text)