Here's an example of my Scrollable_frame class:
class Scrollable_frame(Frame):
def __init__(self, parent, _height, *args, **kw):
Frame.__init__(self, parent, *args, **kw)
# Create a canvas object and a vertical scrollbar for scrolling it.
vscrollbar = ttk.Scrollbar(parent, orient=VERTICAL)
vscrollbar.grid(row = 0, column = 1, sticky = "nse")
self.canvas = Canvas(self, bd=0, highlightthickness=0,
height = _height,
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 = 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)
parent.bind("<MouseWheel>", self._on_mousewheel)
def _configure_interior(self, *args):
# Update the scrollbars to match the size of the inner frame.
size = (self.interior.winfo_width(), self.interior.winfo_height())
self.canvas.config(scrollregion=(0, 0, size[0], size[1]))
if self.interior.winfo_width() != self.canvas.winfo_width():
# Update the canvas's width to fit the inner frame.
self.canvas.config(width = self.interior.winfo_width())
def _configure_canvas(self, *args):
if self.interior.winfo_width() != 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())
def _on_mousewheel(self, event):
self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")
You can use it and modify it a little so that there is no scrollbar like this:
class Scrollable_frame(Frame):
def __init__(self, parent, _height, *args, **kw):
Frame.__init__(self, parent, *args, **kw)
# Create a canvas object and a vertical scrollbar for scrolling it.
self.canvas = Canvas(self, bd=0, highlightthickness=0,
height = _height,)
self.canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
# Create a frame inside the canvas which will be scrolled with it.
self.interior = 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)
parent.bind("<MouseWheel>", self._on_mousewheel)
def _configure_interior(self, *args):
# Update the scrollbars to match the size of the inner frame.
size = (self.interior.winfo_width(), self.interior.winfo_height())
self.canvas.config(scrollregion=(0, 0, size[0], size[1]))
if self.interior.winfo_width() != self.canvas.winfo_width():
# Update the canvas's width to fit the inner frame.
self.canvas.config(width = self.interior.winfo_width())
def _configure_canvas(self, *args):
if self.interior.winfo_width() != 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())
def _on_mousewheel(self, event):
self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")
Here's an example of how to use this class properly, copy this code and see how it works:
from tkinter import *
class Scrollable_frame(Frame):
def __init__(self, parent, _height, *args, **kw):
Frame.__init__(self, parent, *args, **kw)
# Create a canvas object and a vertical scrollbar for scrolling it.
self.canvas = Canvas(self, bd=0, highlightthickness=0,
height = _height,)
self.canvas.pack(side=LEFT, fill=BOTH, expand=TRUE)
# Create a frame inside the canvas which will be scrolled with it.
self.interior = 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)
parent.bind("<MouseWheel>", self._on_mousewheel)
def _configure_interior(self, *args):
# Update the scrollbars to match the size of the inner frame.
size = (self.interior.winfo_width(), self.interior.winfo_height())
self.canvas.config(scrollregion=(0, 0, size[0], size[1]))
if self.interior.winfo_width() != self.canvas.winfo_width():
# Update the canvas's width to fit the inner frame.
self.canvas.config(width = self.interior.winfo_width())
def _configure_canvas(self, *args):
if self.interior.winfo_width() != 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())
def _on_mousewheel(self, event):
self.canvas.yview_scroll(int(-1*(event.delta/120)), "units")
root = Tk()
scrollable_frame = Scrollable_frame(root, 500)
scrollable_frame.pack()
for row in range(100):
for column in range(10):
Button(scrollable_frame.interior, width = 20, text = row+column).grid(row = row, column = column)
root.mainloop()
What is important to see is that when using this class, the widgets you want to place in the frame have to be children of scrollable_frame.interior which is the the frame inside the Canvas that will scroll. If you make your widgets children of scrollable_frame directly it won't work.
For example if you want to put a label in the scrollable frame you'll proceed like:
scrollable_frame = Scrollable_frame(root, 500)
scrollable_frame.pack()
lab = Label(scrollable_frame.interior, text = "")
lab.pack()
And not:
scrollable_frame = Scrollable_frame(root, 500)
scrollable_frame.pack()
lab = Label(scrollable_frame, text = "")
lab.pack()