I already searched a lot on Stackoverflow, and a lot of blogs, docs... And I don't find the way to make a "PanedWindow" to be scrollable AND expand + fill.
Check this example
"""Test"""
# pylint: disable=unused-wildcard-import,wildcard-import
from tkinter import Canvas, Scrollbar, Tk, Wm
from tkinter.constants import BOTH, CENTER, HORIZONTAL, LEFT, NSEW, X
from tkinter.ttk import Button, Frame, Label, PanedWindow, Style
STYLE = {
"TButton": {
"configure": {
"background": "#333333",
"foreground": "white",
"padding": 8,
"relief": "flat",
"border": 2,
},
"map": {
"background": [("active", "#777777")],
"foreground": [("active", "white")],
},
},
}
class ScrollableFrame(Frame):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
scrollbar = Scrollbar(self, orient="vertical")
scrollbar.pack(side="right", fill="y")
canvas = Canvas(self, borderwidth=2, background="red")
canvas.pack(side=LEFT, fill=BOTH, expand=True)
canvas.configure(yscrollcommand=scrollbar.set)
scrollbar.configure(command=canvas.yview)
frame = Frame(canvas, style="DEBUG.TFrame")
canvas.create_window((0, 0), window=frame, anchor=CENTER)
# COMMENT THIS, and the paned is scrollable, but frame does NOT expand
# frame.pack(expand=1, fill=BOTH)
frame.bind("<Configure>", self.on_configure)
self.scollbar = scrollbar
self.canvas = canvas
self.frame = frame
def on_configure(self, event):
self.canvas.configure(
scrollregion=self.canvas.bbox("all"),
)
def create_right_pane(master):
frame = Frame(master)
label = Label(frame, text="This is a cool text")
label.pack()
return frame
def create_left_pane(master):
frame = ScrollableFrame(master)
title = Label(frame.frame, text="Hello !!!")
title.pack(expand=True, fill=BOTH, anchor=CENTER)
for i in range(100):
Button(frame.frame, text=f"Channel {i} ❱").pack(expand=True, fill=BOTH)
return frame
app = Tk(className="MyApp")
style = Style()
style.theme_create("app", None, STYLE)
style.theme_use("app")
app.title("MyApp")
app.geometry("1120x550")
splitpane = PanedWindow(app, orient=HORIZONTAL)
splitpane.pack(expand=True, fill=BOTH)
leftframe = create_left_pane(splitpane)
rightframe = create_right_pane(splitpane)
splitpane.add(leftframe, weight=1)
splitpane.add(rightframe, weight=5)
app.mainloop()
There is a comment in the ScrollableFrame class
comment the frame.pack() => it scrolls, but the content is not expanded and doesn't fill X
uncomment the frame.pack() => now the content fill the space, but it's not scrollable
What I want to do is simply to make the content "fill the space" and to be "scrollable".
If you find the problem, and if you can fix it (or provide a solution)... thanks !
By default, the inner frame will expand or shrink to the optimal size for its contents. Since the contents of the frame aren't as wide as the canvas, the frame will not be as wide as the canvas.
If you want it to fill the canvas, you need to explicitly set the width to be the same as the width of the canvas whenever the canvas changes size.
First, let's make sure we can identify the inner frame by giving it a tag. You could just as easily capture the identifier returned by window_create
.
canvas.create_window((0, 0), window=frame, anchor=CENTER, tags=("inner_frame",))
Next, add a binding to call a function whenever the canvas configuration changes.
canvas.bind("<Configure>", self.resize_inner_frame)
Finally, define the resize function. On <Configure>
events, the event
object will contain a width
parameter which represents the width of the object. We can use this to determine the width of the inner frame.
def resize_inner_frame(self, event):
self.canvas.itemconfigure("inner_frame", width=event.width)
You might need to adjust the width to accommodate canvas borders.