How to make a Scrollable Frame in Tkinter

Why would anyone want to make a Scrollable Frame in Tkinter? Let’s briefly discuss some benefits and situations in which something like this would be useful.

  1. It allows you to display more content than would fit within the dimensions of the frame, without having to make the frame larger and potentially clutter up the user interface.
  2. It allows the user to easily view the full content of the frame by using the scrollbars, without having to manually resize the frame or use other workarounds.

So basically if you are spaced constrained, and have alot of widgets to be showing, then making a Scrollable Frame is a good idea.


Create a Scrollable Frame

Here we have our VerticalScrolledFrame Class. As you can see, our approach has been to create a separate Class (just like the widgets that we use), which we can reuse over and over again. In-fact, you can just copy paste this into your own program and use it like a regular frame!

class VerticalScrolledFrame(ttk.Frame):
    def __init__(self, parent, *args, **kw):
        ttk.Frame.__init__(self, parent, *args, **kw)

        # Create a canvas object and a vertical scrollbar for scrolling it.
        vscrollbar = ttk.Scrollbar(self, orient=VERTICAL)
        vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
        self.canvas = tk.Canvas(self, bd=0, highlightthickness=0, 
                                width = 200, height = 300,
                                yscrollcommand=vscrollbar.set)
        self.canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
        vscrollbar.config(command = self.canvas.yview)


        # Create a frame inside the canvas which will be scrolled with it.
        self.interior = ttk.Frame(self.canvas)
        self.interior.bind('<Configure>', self._configure_interior)
        self.interior_id = self.canvas.create_window(0, 0, window=self.interior, anchor=NW)
        self.canvas.bind('<Configure>', self._configure_canvas)

Let’s do a quick breakdown of what is in the above code.

  1. We initialized our Class as a Frame.
  2. Created a Scrollbar and a Canvas (and connected both of them). Creating a Canvas is necessary for the scrolling feature, as a Frame cannot do this natively.
  3. We created another frame inside this Class, called “interior”, which we also added to the Canvas using the create_window() method. This is the actual frame in which we will be placing our widgets.
  4. We binded this “interior” frame and the “canvas” to the “Configure” event, which triggers whenever the window is resized. The functions called by the “Configure” event will be defined by us in the next step of this tutorial.

Two important things to note here.

First of all, the “Configure” event is being used here in the event that window is resized. Because by default, if you create a Frame inside a Canvas, and then resize the Canvas, the Frame will not resize along with it. (This rule applies to any object drawn inside a canvas, so we need to do this part manually).

If you do not want your Frame/Window to be resizable (e.g. you have disabled resizing on it), then you can probably just skip this step.

Secondly, you need to remember we are placing widgets inside the interior frame. So when we create an object for the VerticalScrolledFrame Class, with the name “scrolledframe” for example, instead of this:

frame = VerticalScrolledFrame(root)
button = tk.Button(frame, text="Click Me")

we need to do this:

frame = VerticalScrolledFrame(root)
button = tk.Button(frame.interior, text="Click Me")

Configuring the Frame

Here is the function which resizes the frame whenever the window/frame is resized. We basically update the frame to the size of the Canvas (because in reality, the canvas is the one being resized when we drag the window, not the frame).

    def _configure_interior(self, event):
        # Update the scrollbars to match the size of the inner frame.
        size = (self.interior.winfo_reqwidth(), self.interior.winfo_reqheight())
        self.canvas.config(scrollregion=(0, 0, size[0], size[1]))
        if self.interior.winfo_reqwidth() != self.canvas.winfo_width():
            # Update the canvas's width to fit the inner frame.
            self.canvas.config(width = self.interior.winfo_reqwidth())

Here we update the canvas too.

    def _configure_canvas(self, event):
        if self.interior.winfo_reqwidth() != self.canvas.winfo_width():
            # Update the inner frame's width to fill the canvas.
            self.canvas.itemconfigure(self.interior_id, width=self.canvas.winfo_width())

Here is the full working code that you can try out!

import tkinter as tk
import tkinter.ttk as ttk
from tkinter.constants import *

class VerticalScrolledFrame(ttk.Frame):
    def __init__(self, parent, *args, **kw):
        ttk.Frame.__init__(self, parent, *args, **kw)

        # Create a canvas object and a vertical scrollbar for scrolling it.
        vscrollbar = ttk.Scrollbar(self, orient=VERTICAL)
        vscrollbar.pack(fill=Y, side=RIGHT, expand=FALSE)
        self.canvas = tk.Canvas(self, bd=0, highlightthickness=0, 
                                width = 200, height = 300,
                                yscrollcommand=vscrollbar.set)
        self.canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
        vscrollbar.config(command = self.canvas.yview)

        # Reset the view
        self.canvas.xview_moveto(0)
        self.canvas.yview_moveto(0)

        # Create a frame inside the canvas which will be scrolled with it.
        self.interior = ttk.Frame(self.canvas)
        self.interior.bind('<Configure>', self._configure_interior)
        self.canvas.bind('<Configure>', self._configure_canvas)
        self.interior_id = self.canvas.create_window(0, 0, window=self.interior, anchor=NW)


    def _configure_interior(self, event):
        # Update the scrollbars to match the size of the inner frame.
        size = (self.interior.winfo_reqwidth(), self.interior.winfo_reqheight())
        self.canvas.config(scrollregion=(0, 0, size[0], size[1]))
        if self.interior.winfo_reqwidth() != self.canvas.winfo_width():
            # Update the canvas's width to fit the inner frame.
            self.canvas.config(width = self.interior.winfo_reqwidth())
        
    def _configure_canvas(self, event):
        if self.interior.winfo_reqwidth() != self.canvas.winfo_width():
            # Update the inner frame's width to fill the canvas.
            self.canvas.itemconfigure(self.interior_id, width=self.canvas.winfo_width())
        

class Window():
    def __init__(self, master, *args, **kwargs):
        self.frame = VerticalScrolledFrame(master)
        self.frame.pack(expand = True, fill = tk.BOTH)
        self.label = ttk.Label(master, text="Shrink the window to activate the scrollbar.")
        self.label.pack()

        for i in range(10):
            ttk.Button(self.frame.interior, text=f"Button {i}").pack(padx=10, pady=5)

root = tk.Tk()
window = Window(root)
root.mainloop()
How to make a Scrollable Frame in Tkinter

This marks the end of the How to make a Scrollable Frame in Tkinter Tutorial. Any suggestions or contributions for CodersLegacy are more than welcome. Questions about the tutorial content can be asked in the comments section below.

Subscribe
Notify of
guest
6 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments