There is an application I am working on using Python and Tkinter, and this application requires a variety of input forms. Each input form needs a scrollbar in case the form’s parent is too short to display everything, and so after some help from Google, this is what I currently have:
import tkinter as tk
def get_vertically_scrollable_frame(parent_frame: tk.Frame or tk.Tk) -> tk.Frame:
"""
:param parent_frame: The frame to place the canvas and scrollbar onto.
:return: A scrollable tk.Frame object nested within the parent_frame object.
"""
assert isinstance(parent_frame, tk.Frame) or isinstance(parent_frame, tk.Tk)
# Create the canvas and scrollbar.
canvas = tk.Canvas(master=parent_frame, bg="blue")
scrollbar = tk.Scrollbar(master=parent_frame, orient=tk.VERTICAL, command=canvas.yview)
# Let the canvas and scrollbar resize to fit the parent_frame object.
parent_frame.rowconfigure(0, weight=1)
parent_frame.columnconfigure(0, weight=1)
canvas.grid(row=0, column=0, sticky='news')
scrollbar.grid(row=0, column=1, sticky='nes')
# Link the canvas and scrollbar together.
canvas.configure(yscrollcommand=scrollbar.set)
canvas.bind('<Configure>', lambda x: canvas.configure(scrollregion=canvas.bbox("all")))
# Create the tk.Frame that is within the canvas.
canvas_frame = tk.Frame(master=canvas, bg="red")
canvas.create_window((0, 0), window=canvas_frame, anchor="nw")
# TODO: Let the canvas_frame object resize to fit the parent canvas.
canvas.columnconfigure(0, weight=1)
canvas.rowconfigure(0, weight=1)
# canvas_frame.grid(row=0, column=0, sticky="news") # Resizes the frame, but breaks the scrollbar.
return canvas_frame
if __name__ == "__main__":
window = tk.Tk()
window.rowconfigure(0, weight=1)
window.columnconfigure(0, weight=1)
parent_frame = tk.Frame(master=window)
parent_frame.grid(row=0, column=0, sticky="news")
scrollable_frame = get_vertically_scrollable_frame(parent_frame)
# Add the widgets to the new frame.
scrollable_frame.columnconfigure(0, weight=1) # Resize everything horizontally.
tk.Label(master=scrollable_frame, text="First name").grid(row=0, column=0, sticky="w")
tk.Entry(master=scrollable_frame).grid(row=1, column=0, sticky="ew")
tk.Label(master=scrollable_frame, text="").grid(row=2, column=0, sticky="w")
tk.Label(master=scrollable_frame, text="Last name").grid(row=3, column=0, sticky="w")
tk.Entry(master=scrollable_frame).grid(row=4, column=0, sticky="ew")
tk.Label(master=scrollable_frame, text="").grid(row=5, column=0, sticky="w")
tk.Label(master=scrollable_frame, text="Email").grid(row=6, column=0, sticky="w")
tk.Entry(master=scrollable_frame).grid(row=7, column=0, sticky="ew")
tk.Label(master=scrollable_frame, text="").grid(row=8, column=0, sticky="w")
tk.Label(master=scrollable_frame, text="Favorite color").grid(row=9, column=0, sticky="w")
tk.Entry(master=scrollable_frame).grid(row=10, column=0, sticky="ew")
tk.Label(master=scrollable_frame, text="").grid(row=11, column=0, sticky="w")
tk.Frame(master=scrollable_frame).grid(row=12, column=0, sticky="news")
scrollable_frame.rowconfigure(12, weight=1) # Vertically resize filler frame from last line.
tk.Button(master=scrollable_frame, text="Clear").grid(row=13, column=0, sticky="ews")
tk.Button(master=scrollable_frame, text="Submit").grid(row=14, column=0, sticky="ews")
window.mainloop()
This function takes an empty Tkinter frame, places a working canvas and scrollbar on that frame, places a new frame into the canvas, and then returns the frame inside the canvas.
While the scrollbar works fine with the above code, the returned nested Tkinter frame does not resize to fit the height and width of its parent canvas. If the parent canvas is too large, it looks like this:
(The blue area is the canvas, and red is the frame inside the canvas.)
In an attempt to fix this, I manually placed the nested frame on the canvas using grid (see the commented code just before the return statement). The frame inside the canvas started resizing itself, but the scrollbar stopped working.
Is there a simple way to allow the frame inside the canvas to resize itself without breaking the scrollbar?



