Tkinter 15 – Add a scrollbar to tkinter’s listbox

How to add a scrollbar to a widget

If you want to know everything about tkinker you can take a look at Effbot site.

The listbox widget can be very handy for a lot of purposes, expecially in Python that has this wonderful sintax for handling lists (that are the Python’s arrays). So, Learning to create listbox can take your scripts to the next level, making them so easy to use also for users different from the programmer and for the programmer too.

Example of a listbox in tkinter

Scrollbars

To add a scrollbar to a listbox can be a little tricky at first, but it’s not so difficult, once you get it. For lists scrollbars are just necessary, if the items are not few as often happens. Once you created the two widget, the listbox and the scrollbars, you have to join them. You must use for the listbox the config method and pass to the yscrollcommand the set method of the scrollbar. For the scrollbar you must give the listbox.yview method as value of the argument command, always in the config method. Do not forget to pack the scrollbar to the RIGHT, to make it appear on the right of the listbox and use fill=Y.

Another stuff: the binding

Another important stuff is the binding of an action to the listbox. You can use the <Button-1> eventlistener or the <<ListBoxSelection>> eventlistener to attach some action when the user selects an item.

#listbox scrollbar

from tkinter import *
root = Tk()
scrollbar = Scrollbar(root)
scrollbar.pack(side=RIGHT, fill=Y)
listbox = Listbox(root)
listbox.pack()
for i in range(100):
    listbox.insert(END, i)
# attach listbox to scrollbar
listbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=listbox.yview)

root.mainloop()

In case you do not want to see the scrollbar, but still have the chance to scroll the items in the list you can avoid packing the scrollbar. It will be not visible on the screen, but it will be active when the mouse will be over the listbox.

#listbox scrollbar

from tkinter import *
root = Tk()
scrollbar = Scrollbar(root)
#scrollbar.pack(side=RIGHT, fill=Y) # Now it's active, but not visible
listbox = Listbox(root)
listbox.pack()
for i in range(100):
    listbox.insert(END, i)
# attach listbox to scrollbar
listbox.config(yscrollcommand=scrollbar.set)

listbox2 = Listbox(root)
listbox2.pack(side="right")
for i in range(100):
    listbox2.insert(END, i+100)
listbox2.config(yscrollcommand=scrollbar.set)

scrollbar.config(command=listbox.yview)

root.mainloop()

Use the same scrollbar for 2 listbox

You can take as reference the same scrollbar widget for two different listbox. In this example I commented out the pack() method, so that the rulers fo the scrollbars are not visible. If you want them to be visible, uncomment that line

#scrollbar.pack(side=RIGHT, fill=Y)

Here is the whole code:

#listbox scrollbar

from tkinter import *
root = Tk()
scrollbar = Scrollbar(root)
#scrollbar.pack(side=RIGHT, fill=Y)
listbox = Listbox(root)
listbox.pack()
for i in range(100):
    listbox.insert(END, i)
# attach listbox to scrollbar
listbox.config(yscrollcommand=scrollbar.set)

listbox2 = Listbox(root)
listbox2.pack()
for i in range(100):
    listbox2.insert(END, i+100)
listbox2.config(yscrollcommand=scrollbar.set)

#scrollbar.config(command=listbox.yview)

root.mainloop()
2 listbox linked to the same scrollbar widgets

Scrolling 2 listboxes in sync

Someone asked me if it was possible to scroll two lisboxes in sync, so I made this code

#listbox scrollbar

from tkinter import *
root = Tk()

def scrolllistbox2(event):
	listbox2.yview_scroll(int(-4*(event.delta/120)), "units")
def scrolllistbox1(event):
	listbox1.yview_scroll(int(-4*(event.delta/120)), "units")

scrollbar = Scrollbar(root)
#scrollbar.pack(side=RIGHT, fill=Y)
listbox1 = Listbox(root)
listbox1.pack()
for i in range(100):
    listbox1.insert(END, i)
# attach listbox to scrollbar
listbox1.config(yscrollcommand=scrollbar.set)
listbox1.bind("<MouseWheel>", scrolllistbox2)

listbox2 = Listbox(root)
listbox2.pack()
for i in range(100):
    listbox2.insert(END, i+100)
listbox2.config(yscrollcommand=scrollbar.set)
listbox2.bind("<MouseWheel>", scrolllistbox1)
#scrollbar.config(command=listbox.yview)

root.mainloop()

A possible case of real use of the sync listboxes scrolling

I thought at some cases in which it could be useful to scroll in sync two listboxes and I thought to this example.

P.S.: use the number 4 in the function to scroll the other listbox to make the 2 listboxes scroll the same number of lines each time, otherwise the listbox that the mouse is overing will scroll more lines and reach the bottom or the top faster.

#listbox scrollbar
from random import randint
from tkinter import *
root = Tk()
root.geometry("400x400")
def scrolllistbox2(event):
	listbox2.yview_scroll(int(-4*(event.delta/120)), "units")
def scrolllistbox1(event):
	listbox1.yview_scroll(int(-4*(event.delta/120)), "units")

scrollbar = Scrollbar(root)
scrollbar.pack(side=RIGHT, fill=Y)
listbox1 = Listbox(root)
listbox1.pack(expand=1, fill="both")
for i in range(100):
    rnd = str(randint(1,100))
    listbox1.insert(END, f"OUR INCOMES day {i}:" + rnd)
# attach listbox to scrollbar
listbox1.config(yscrollcommand=scrollbar.set)
listbox1.bind("<MouseWheel>", scrolllistbox2)

listbox2 = Listbox(root)
listbox2.pack(expand=1, fill="both")
for i in range(100):
    rnd = str(randint(1,100))
    listbox2.insert(END, f"THEIR INCOMES day {i}: " + rnd)
listbox2.config(yscrollcommand=scrollbar.set)
listbox2.bind("<MouseWheel>", scrolllistbox1)
#scrollbar.config(command=listbox.yview)

root.mainloop()

Here is the window

You can see days in sync at 1st and last line

Scrolling 2 listboxes in sync, side by side for better visual comparison

It would be even better to have them side by side, so that you can make a faster comparison with your referring data for the evaluation that we ipotized in the example above.

just put this on the pack method of both:

listbox1.pack(expand=1, fill="both", side="left")

for the first listbox

and…

listbox2.pack(expand=1, fill="both", side="left")

for the second one

The whole code would be this:

#listbox scrollbar
from random import randint
from tkinter import *
root = Tk()
root.title("2 Listbox scolling in sync")
root.geometry("400x400")
def scrolllistbox2(event):
	listbox2.yview_scroll(int(-4*(event.delta/120)), "units")
def scrolllistbox1(event):
	listbox1.yview_scroll(int(-4*(event.delta/120)), "units")

scrollbar = Scrollbar(root)
scrollbar.pack(side=RIGHT, fill=Y)
listbox1 = Listbox(root)
listbox1.pack(expand=1, fill="both", side="left")
for i in range(100):
    rnd = str(randint(1,100))
    listbox1.insert(END, f"OUR INCOMES day {i}:" + rnd)
# attach listbox to scrollbar
listbox1.config(yscrollcommand=scrollbar.set)
listbox1.bind("<MouseWheel>", scrolllistbox2)

listbox2 = Listbox(root)
listbox2.pack(expand=1, fill="both", side="left")
for i in range(100):
    rnd = str(randint(1,100))
    listbox2.insert(END, f"THEIR INCOMES day {i}: " + rnd)
listbox2.config(yscrollcommand=scrollbar.set)
listbox2.bind("<MouseWheel>", scrolllistbox1)
#scrollbar.config(command=listbox.yview)

root.mainloop()

The result is this:

Turn on and off the syncronizetion of the listboxes

In this code I added a button and a label to turn on and off the syncronization

#listbox scrollbar
from random import randint
from tkinter import *
root = Tk()
root.title("2 Listbox scolling in sync")
root.geometry("400x400")

switch = 1
def scrolllistbox2(event):
	global switch
	if switch==1:
		listbox2.yview_scroll(int(-4*(event.delta/120)), "units")
def scrolllistbox1(event):
	global switch
	if switch == 1:
		listbox1.yview_scroll(int(-4*(event.delta/120)), "units")

def do_switch():
	global switch
	if switch:
		switch = 0
		label['text'] = "Not in sync"
	else:
		switch = 1
		label['text'] = "In sync"

frame1 = Frame(root)
frame1.pack(expand=1, fill="both")
scrollbar = Scrollbar(frame1)
scrollbar.pack(side=RIGHT, fill=Y)
listbox1 = Listbox(frame1)
listbox1.pack(expand=1, fill="both", side="left")
for i in range(100):
    rnd = str(randint(1,100))
    listbox1.insert(END, f"OUR INCOMES day {i}:" + rnd)
# attach listbox to scrollbar
listbox1.config(bg = "yellow", yscrollcommand=scrollbar.set)
listbox1.bind("<MouseWheel>", scrolllistbox2)

listbox2 = Listbox(frame1)
listbox2.pack(expand=1, fill="both", side="left")
for i in range(100):
    rnd = str(randint(1,100))
    listbox2.insert(END, f"THEIR INCOMES day {i}: " + rnd)
listbox2.config(bg="cyan",yscrollcommand=scrollbar.set)
listbox2.bind("<MouseWheel>", scrolllistbox1)
#scrollbar.config(command=listbox.yview)
frame2 = Frame(root)
frame2.pack()
button = Button(frame2, text= "Sync/unsync", command=do_switch)
button.pack()
label = Label(frame2, text = "In sync")
label.pack()
root.mainloop()

I added two frame widget, one for the listboxes and another for the button and the label, for a better layout of the widgets. To the button there is attached a function that just makes 1 or 0 a boolean label called switch through the do_switch function. When its value is 0 each listbox scrolling does not activate the one of the other.

We can unsync or sync with the button below

Using the scrollbars clicking and dragging the mouse on the bars

On scrollbar draggable for each list

Ok, we need to be able to drag the scrollbar with the right click of the mouse pressed. That is the code. I also added one scrollbar for each list. At this point they go in sync with the mousewheel, but if you drag the scrollbar with the mouse they will scroll indipendently.

The code to drag the bars is:

scrollbar1.config(command=listbox1.yview)
scrollbar2.config(command=listbox2.yview)

The final version of the code. Some abstractions and some missing line of codes added.

#listbox scrollbar
from random import randint
import tkinter as tk


root = tk.Tk()
root.title("2 Listbox scolling in sync")
root.geometry("400x400")

switch = 1
def scrolllistbox(event, lb):
	global switch
	if switch==1:
		lb.yview_scroll(int(-4*(event.delta/120)), "units")
		print(event)

def do_switch():
	global switch
	if switch:
		switch = 0
		label['text'] = "Not in sync"
	else:
		switch = 1
		label['text'] = "In sync"


def def_listbox():
	scrollbar1 = tk.Scrollbar(frame1)
	scrollbar1.pack(side=tk.LEFT, fill=tk.Y)
	listbox1 = tk.Listbox(frame1)
	scrollbar1.config(command=listbox1.yview)
	listbox1.pack(expand=1, fill="both", side="left")
	for i in range(100):
	    rnd = str(randint(1,100))
	    listbox1.insert(tk.END, f"INCOMES day {i}:" + rnd)
	listbox1.config(bg = "yellow", yscrollcommand=scrollbar1.set)
	return listbox1


# ====================== LISTBOXES =======================================
frame1 = tk.Frame(root)
frame1.pack(expand=1, fill="both")
listbox1 = def_listbox()
listbox2 = def_listbox()
listbox1.bind("<MouseWheel>", lambda event: scrolllistbox(event, listbox2))
listbox2.bind("<MouseWheel>", lambda event: scrolllistbox(event, listbox1))
# ================== SWITCH BUTTON =========================
frame2 = tk.Frame(root)
frame2.pack()
button = tk.Button(frame2, text= "Sync/unsync", command=do_switch)
button.pack()
label = tk.Label(frame2, text = "In sync")
label.pack()
# ==========================================================
root.mainloop()
Now you can drag the scrollbars

The video with an example

Another post like this (20/08/2019)

Scrolling bars into a text box in tkinter

This code adds a scrollbar to a Text widget in tkinter. Nothing different from attaching a scrollbar to a listbox, like we’ve seen above.

from tkinter import *
root = Tk()
scrollbar = Scrollbar(root)
scrollbar.pack(side=RIGHT, fill=Y)
textbox = Text(root)
textbox.pack()
for i in range(100):
    textbox.insert(END, f"This is an example line {i}\n")
# attach textbox to scrollbar
textbox.config(yscrollcommand=scrollbar.set)
scrollbar.config(command=textbox.yview)
 
root.mainloop()

Tkinter test for students

Tkinter articles

A little bit of restyling

#listbox scrollbar
from random import randint
import tkinter as tk


def scrolllistbox(event, lb):
	global switch
	if switch==1:
		lb.yview_scroll(int(-4*(event.delta/120)), "units")
		print(event)

def do_switch():
	global switch
	if switch:
		switch = 0
		label['text'] = "Not in sync"
	else:
		switch = 1
		label['text'] = "In sync"

def create_data(listbox1):
	for i in range(100):
	    rnd = str(randint(1,100))
	    listbox1.insert(tk.END, f"INCOMES day {i}:" + rnd)


def def_listbox():
	scrollbar1 = tk.Scrollbar(frame1)
	scrollbar1.pack(side=tk.LEFT, fill=tk.Y)
	listbox1 = tk.Listbox(frame1)
	scrollbar1.config(command=listbox1.yview)
	create_data(listbox1)
	listbox1.pack(expand=1, fill="both", side="left")
	listbox1.config(bg = "yellow", yscrollcommand=scrollbar1.set)
	return listbox1


def create_root():
	"Returns the main windows"
	root = tk.Tk()
	root.title("2 Listbox scolling in sync")
	root.geometry("400x400")
	return root


def create_frames():
	"Creates frames for widgets"

	# listboxes frame
	frame1 = tk.Frame(root)
	frame1.pack(expand=1, fill="both")
	# button and label frame
	frame2 = tk.Frame(root)
	frame2.pack()
	return frame1, frame2


def create_widgets():
	"Returns listboxes, button and label"
	# listboxes
	listbox1 = def_listbox()
	listbox2 = def_listbox()
	listbox1.bind("<MouseWheel>", lambda event: scrolllistbox(event, listbox2))
	listbox2.bind("<MouseWheel>", lambda event: scrolllistbox(event, listbox1))
	# ================== SWITCH BUTTON =========================
	button = tk.Button(frame2, text= "Sync/unsync", command=do_switch)
	button.pack()
	label = tk.Label(frame2, text = "In sync")
	label.pack()
	# ==========================================================
	pack = listbox1, listbox2, button, label
	return pack


switch = 1
root = create_root()
frame1, frame2 = create_frames()
listbox1, listbox2, button, label = create_widgets()
root.mainloop()

Subscribe to the newsletter for updates
Tkinter templates
Avatar My youtube channel

Twitter: @pythonprogrammi - python_pygame

Videos

Speech recognition game

Pygame's Platform Game

Other Pygame's posts

Published by pythonprogramming

Started with basic on the spectrum, loved javascript in the 90ies and python in the 2000, now I am back with python, still making some javascript stuff when needed.