Creating a scrollable frame

There will come a time when writing applications with Tkinter when you will want to add a Scrollbar to the application as a whole, rather than an individual widget (such as the Text widget, which we have already covered). This is not a trivial task, since Tkinter will usually expand itself to show all of its containing widgets, or if the geometry is already set, will simply hide them underneath the bottom border of the window.

Let's take a look at what I mean. Open a new Python script and type the following short snippet:

import tkinter as tk

win = tk.Tk()

for _ in range(30):
tk.Label(win, text="big label").pack(pady=20)

win.mainloop()

If you run this file you should see a very tall window open up. You will see a certain number of Label widgets (depending on your monitor's resolution) but will likely not see all 30.

If you have the ability to move the window above the top of your screen (on my Linux machine I can hold Alt and drag the middle of the window upwards) you can then expose the bottom of the window. Drag the bottom of the window downwards (thus increasing the window's height) and you will see more Label widgets lie underneath:

To solve this problem, we obviously just need a Scrollbar! Let's try adding one:

import tkinter as tk

win = tk.Tk()
f = tk.Frame(win)
for _ in range(30):
tk.Label(win, text="big label").pack(pady=20)

scroll = tk.Scrollbar(win, orient='vertical', command=win.yview)
scroll.pack(side=tk.RIGHT, fill=tk.Y)

win.mainloop()

This code should allow us to scroll the window with the Scrollbar. However, if you try executing this file you will be shown the following error:

Traceback (most recent call last):
File "demo/demo.py", line 8, in <module>
scroll = tk.Scrollbar(win, orient='vertical', command=win.yview)
File "/usr/lib64/python3.6/tkinter/__init__.py", line 2095, in __getattr__
return getattr(self.tk, attr)
AttributeError: '_tkinter.tkapp' object has no attribute 'yview'

How about a Frame widget instead?

Traceback (most recent call last):
File "demo/demo.py", line 8, in <module>
scroll = tk.Scrollbar(win, orient='vertical', command=f.yview)
AttributeError: 'Frame' object has no attribute 'yview'

No, that didn't work either. There must be some way we can get our window to scroll though, right?

The solution is to use the Canvas widget to embed a Frame widget as a window. Since the Canvas widget can handle scrolling, we can use this trick to get ourselves a scrollable Frame:

import tkinter as tk

win = tk.Tk()
canvas = tk.Canvas(win)
frame = tk.Frame(canvas)

scroll = tk.Scrollbar(win, orient='vertical', command=canvas.yview)
scroll.pack(side=tk.RIGHT, fill=tk.Y)

canvas.configure(yscrollcommand=scroll.set)
canvas.pack(fill=tk.BOTH, expand=1)
canvas.create_window((0, 0), window=frame, anchor='nw')

for _ in range(30):
tk.Label(frame, text="big label").pack(pady=20)

win.mainloop()

This time we create a Canvas first, then a Frame inside it. Our Scrollbar's command can then be set to the yview of our Canvas.

To create the Frame widget as a window inside our Canvas, the create_window method is used. The first argument to this method is the coordinates within the Canvas at which to place this window. (0, 0) puts the window at the upper-left corner of the Canvas. Next, the window argument is what we want to draw as the window, so we pass this our Frame. Finally, the anchor allows us to align the window, and we use nw to keep everything aligned in the upper-left corner.

When using the create_window method, do not also use a geometry manager (such as pack) on the widget that you are using as a window. This will prevent the widget from displaying at all!

Now that our window is created, we can add our 30 Label widgets to it.

Give this version of the code a try. You should now see we have a normal-sized window with a scrollbar. However, the scrollbar still doesn't seem to work!

In order to make the Frame widget inside the Canvas scrollable we have one final thing left to do—configure its scrollregion. As the name suggests, this defines where in the Canvas can be scrolled.

Add this function and binding to your code and give it another try:

def on_frame_resized(self, event=None):
canvas.configure(scrollregion=canvas.bbox("all"))


win.bind('<Configure>', on_frame_resized)

The on_frame_resized function will configure the scrollregion of the Canvas. The bbox("all") grabs the bounding box coordinates of the whole Canvas, and we pass this as the scrollregion to allow us to scroll the entire Canvas.

This function is bound to the <Configure> event, which is called when the window is created and resized. This ensures that, as our Canvas grows and shrinks along with the main window, we will still be able to scroll all of it.

If you run this version of the code, you should now be presented with a completely scrollable window.

This is the basic recipe for creating a window that can be scrolled via a Scrollbar. You can also use the mouse wheel bindings we have practiced in previous chapters to allow the window to be scrolled using the mouse wheel.

Now that we know how to make a scrollable window, let's begin by creating one. We will be using this to create our friends list window, which will feature information about all of the people we are able to chat to.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset