Still WIP
This commit is contained in:
151
classes/custom/scrollable_frame.py
Normal file
151
classes/custom/scrollable_frame.py
Normal file
@@ -0,0 +1,151 @@
|
||||
from tkinter import NSEW, Canvas, Event, ttk
|
||||
|
||||
from classes.custom.themed_frame import ThemedFrame
|
||||
|
||||
FIT_WIDTH = "fit_width"
|
||||
FIT_HEIGHT = "fit_height"
|
||||
|
||||
|
||||
class ScrollableFrame(ThemedFrame):
|
||||
"""
|
||||
There is no way to scroll <tkinter.Frame> so we are
|
||||
going to create a canvas and place the frame there.
|
||||
Scrolling the canvas will give the illusion of scrolling
|
||||
the frame
|
||||
Partly taken from:
|
||||
https://blog.tecladocode.com/tkinter-scrollable-frames/
|
||||
https://stackoverflow.com/a/17457843/11106801
|
||||
master_frame---------------------------------------------------------
|
||||
| dummy_canvas----------------------------------------- y_scroll-- |
|
||||
| | self--------------------------------------------- | | | |
|
||||
| | | | | | | |
|
||||
| | | | | | | |
|
||||
| | | | | | | |
|
||||
| | ------------------------------------------------ | | | |
|
||||
| ---------------------------------------------------- --------- |
|
||||
| x_scroll--------------------------------------------- |
|
||||
| | | |
|
||||
| ---------------------------------------------------- |
|
||||
--------------------------------------------------------------------
|
||||
"""
|
||||
def __init__(self, master=None, scroll_speed:int=2, hscroll:bool=False, vscroll:bool=True, scrollbar_kwargs={}, **kwargs):
|
||||
assert isinstance(scroll_speed, int), "`scroll_speed` must be an int"
|
||||
self.scroll_speed = scroll_speed
|
||||
|
||||
self.master_frame = ThemedFrame(master)
|
||||
self.master_frame.grid_rowconfigure(0, weight=1)
|
||||
self.master_frame.grid_columnconfigure(0, weight=1)
|
||||
self.dummy_canvas = Canvas(self.master_frame, highlightthickness=0, **kwargs)
|
||||
super().__init__(self.dummy_canvas)
|
||||
|
||||
# Create the 2 scrollbars
|
||||
if vscroll:
|
||||
self.v_scrollbar = ttk.Scrollbar(self.master_frame, orient="vertical", command=self.dummy_canvas.yview, **scrollbar_kwargs)
|
||||
self.v_scrollbar.grid(row=0, column=1, sticky=NSEW)
|
||||
self.dummy_canvas.configure(yscrollcommand=self.v_scrollbar.set)
|
||||
if hscroll:
|
||||
self.h_scrollbar = ttk.Scrollbar(self.master_frame, orient="horizontal", command=self.dummy_canvas.xview, **scrollbar_kwargs)
|
||||
self.h_scrollbar.grid(row=1, column=0, sticky=NSEW)
|
||||
self.dummy_canvas.configure(xscrollcommand=self.h_scrollbar.set)
|
||||
|
||||
# Bind to the mousewheel scrolling
|
||||
self.dummy_canvas.bind_all("<MouseWheel>", self.scrolling_windows, add=True)
|
||||
self.dummy_canvas.bind_all("<Button-4>", self.scrolling_linux, add=True)
|
||||
self.dummy_canvas.bind_all("<Button-5>", self.scrolling_linux, add=True)
|
||||
self.bind("<Configure>", self.scrollbar_scrolling, add=True)
|
||||
|
||||
# Place `self` inside `dummy_canvas`
|
||||
self.dummy_canvas.create_window((0, 0), window=self, anchor="nw")
|
||||
# Place `dummy_canvas` inside `master_frame`
|
||||
self.dummy_canvas.grid(row=0, column=0, sticky=NSEW)
|
||||
|
||||
self.pack = self.master_frame.pack
|
||||
self.grid = self.master_frame.grid
|
||||
self.place = self.master_frame.place
|
||||
self.pack_forget = self.master_frame.pack_forget
|
||||
self.grid_forget = self.master_frame.grid_forget
|
||||
self.place_forget = self.master_frame.place_forget
|
||||
|
||||
def scrolling_windows(self, event:Event) -> None:
|
||||
assert event.delta != 0, "On Windows, `event.delta` should never be 0"
|
||||
y_steps = int(-event.delta/abs(event.delta)*self.scroll_speed)
|
||||
self.dummy_canvas.yview_scroll(y_steps, "units")
|
||||
|
||||
def scrolling_linux(self, event:Event) -> None:
|
||||
y_steps = self.scroll_speed
|
||||
if event.num == 4:
|
||||
y_steps *= -1
|
||||
self.dummy_canvas.yview_scroll(y_steps, "units")
|
||||
|
||||
def scrollbar_scrolling(self, event:Event) -> None:
|
||||
region = list(self.dummy_canvas.bbox("all"))
|
||||
region[2] = max(self.dummy_canvas.winfo_width(), region[2])
|
||||
region[3] = max(self.dummy_canvas.winfo_height(), region[3])
|
||||
self.dummy_canvas.configure(scrollregion=region)
|
||||
|
||||
def resize(self, fit:str=None, height:int=None, width:int=None) -> None:
|
||||
"""
|
||||
Resizes the frame to fit the widgets inside. You must either
|
||||
specify (the `fit`) or (the `height` or/and the `width`) parameter.
|
||||
Parameters:
|
||||
fit:str `fit` can be either `FIT_WIDTH` or `FIT_HEIGHT`.
|
||||
`FIT_WIDTH` makes sure that the frame's width can
|
||||
fit all of the widgets. `FIT_HEIGHT` is simmilar
|
||||
height:int specifies the height of the frame in pixels
|
||||
width:int specifies the width of the frame in pixels
|
||||
To do:
|
||||
ALWAYS_FIT_WIDTH
|
||||
ALWAYS_FIT_HEIGHT
|
||||
"""
|
||||
if height is not None:
|
||||
self.dummy_canvas.config(height=height)
|
||||
if width is not None:
|
||||
self.dummy_canvas.config(width=width)
|
||||
if fit == FIT_WIDTH:
|
||||
super().update()
|
||||
self.dummy_canvas.config(width=super().winfo_width())
|
||||
elif fit == FIT_HEIGHT:
|
||||
super().update()
|
||||
self.dummy_canvas.config(height=super().winfo_height())
|
||||
else:
|
||||
raise ValueError("Unknow value for the `fit` parameter.")
|
||||
fit = resize
|
||||
|
||||
# import tkinter as tk
|
||||
# from tkinter import ttk
|
||||
# from classes.custom.themed_frame import ThemedFrame
|
||||
|
||||
# class ScrollableFrame(ThemedFrame):
|
||||
|
||||
# def __init__(self, container, *args, **kwargs):
|
||||
|
||||
# super().__init__(container, *args, **kwargs)
|
||||
|
||||
# self.canvas = tk.Canvas(self)
|
||||
# scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.canvas.yview)
|
||||
|
||||
# self.canvas.bind_all("<MouseWheel>", self._on_mousewheel)
|
||||
|
||||
# self.scrollable_frame = ThemedFrame(self.canvas)
|
||||
|
||||
# self.scrollable_frame.bind(
|
||||
# "<Configure>",
|
||||
# lambda e: self.canvas.configure(
|
||||
# scrollregion=self.canvas.bbox("all")
|
||||
# )
|
||||
# )
|
||||
|
||||
# self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw")
|
||||
|
||||
# self.canvas.configure(yscrollcommand=scrollbar.set)
|
||||
|
||||
# self.canvas.pack(side="left", fill="both", expand=True)
|
||||
# scrollbar.pack(side="right", fill="y")
|
||||
|
||||
# def _on_mousewheel(self, event):
|
||||
# shift = (event.state & 0x1) != 0
|
||||
# scroll = -1 if event.delta > 0 else 1
|
||||
# if shift:
|
||||
# self.canvas.xview_scroll(scroll, "units")
|
||||
# else:
|
||||
# self.canvas.yview_scroll(scroll, "units")
|
@@ -1,9 +1,11 @@
|
||||
from tkinter import Misc
|
||||
import sv_ttk
|
||||
from tkinter.ttk import Frame
|
||||
|
||||
import sv_ttk
|
||||
|
||||
from modules.utils import get_string_mode
|
||||
|
||||
|
||||
class ThemedFrame(Frame):
|
||||
|
||||
def __init__(self, master: Misc, **kwargs) -> None:
|
||||
|
Reference in New Issue
Block a user