119 lines
3.4 KiB
Python
119 lines
3.4 KiB
Python
# -*- 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(
|
|
"<Configure>",
|
|
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("<Configure>", 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("<Configure>", draw_text) |