Listbox

A listbox displays a list of single-line text items, and allows users to browse through the list, and selecting one or more items.

Create a listbox the old way

The old way of creating a listbox, was to create an empty listbox widget and then to insert item by item into the listbox.

The listbox methods insert and delete allow to add or remove items a specific position. The label tk.END is used to add items to the end of the listbox. Here three items are added at the top and one item is removed:

lb.insert(0, 'item0', 'item1', 'item2')
lb.delete(1)

To insert multiple items of a list one can iterate through the list:

for item in items:
    lb.insert(tk.END, item)

or hand multiple items directly to the insert method:

lb.insert(tk.END, *items)
../_images/lb1.png
# Create a listbox the old way, with the insert/delete method
import tkinter as tk

root = tk.Tk()

lb = tk.Listbox(root)
lb.grid()

lb.insert(0, 'item0', 'item1', 'item2')
lb.delete(1)

items = dir(tk)
for item in items:
    lb.insert(tk.END, item)
lb.insert(tk.END, *items)

root.mainloop()

lb1.py

Create a listbox the new way

The new and much simpler way of handling the listbox content is to use the listvariable argument which must be set to a StringVar object. The program below displays the attributes of the Tk module as a list:

items = dir(tk)
var = tk.StringVar()
var.set(items)

At creation the listbox sets its listvariable to this list:

tk.Listbox(root, listvariable=var).grid()
../_images/lb2.png
# Display a simple listbox
import tkinter as tk

root = tk.Tk()

items = dir(tk)
var = tk.StringVar()
var.set(items)
tk.Listbox(root, listvariable=var).grid()

root.mainloop()

lb2.py

Single and extended selection mode

The selectmode argument let’s the listbox be configured for

  • single item selection (browse)
  • multiple item selection (extended)
../_images/lb3.png
# Show browse/extended selectmode
import tkinter as tk

root = tk.Tk()

var = tk.StringVar(value=dir(tk))

tk.Listbox(root, listvariable=var, selectmode='browse').pack(side='left')
tk.Listbox(root, listvariable=var, selectmode='extended').pack(side='left')

root.mainloop()
root.mainloop()

lb3.py

ListboxSelect callback function

When the user selects an item, either with a mouse click or with the arrow keys, a virtual <ListboxSelect> event is generated. You can bind to it to a callback function:

lb.bind('<<ListboxSelect>>', cb)

The callback function prints the event descriptor and the current selection as an index list to a label:

def cb(event):
    label['text'] = str(event) + '\n' + str(lb.curselection())
../_images/lb4.png
# <ListboxSelect> callback function and current selection
import tkinter as tk

def cb(event):
    label['text'] = str(event) + '\n' + str(lb.curselection())

root = tk.Tk()
var = tk.StringVar(value=dir(tk))

label = tk.Label(root)
label.grid()

lb = tk.Listbox(root, listvariable=var, selectmode='extended')
lb.grid()
lb.bind('<<ListboxSelect>>', cb)

root.mainloop()

lb4.py

Edit a listbox item

In the following example we display the selected listbox item in an entry field. The entry field can be edited and hitting the return key writes the new item value to the listbox. The screen capture below shows how ANCHOR had been changed to ANCHOR_function.

../_images/lb5.png
# Edit listbox item with an entry widget
import tkinter as tk

def select(event):
    i = lb.curselection()[0]
    item.set(items[i])

def update(event):
    i = lb.curselection()[0]
    items[i] = item.get()
    var.set(items)

root = tk.Tk()
items = dir(tk)
var = tk.StringVar(value=items)

lb = tk.Listbox(root, listvariable=var)
lb.grid()
lb.bind('<<ListboxSelect>>', select)

item = tk.StringVar()
entry = tk.Entry(root, textvariable=item, width=20)
entry.grid()
entry.bind('<Return>', update)

root.mainloop()

lb5.py

Regular expression

../_images/re1.png
"""Regular expression demo."""
from tkinter import *
from tklib import *
import re

class Browser(Listbox):
    def cb(self, event):
        pass

class Demo(App):
    def __init__(self): 
        super().__init__()
        Label('Regular expressions', font='Arial 18')
        Label('Enter a Perl-style regular expression')
        App.re = Entry('regex')
        App.re.bind('<Key>', self.recompile)
        App.status = Label('Status')

        Checkbutton('IGNORECASE;MULITILINE;DOTALL;VERBOSE')

        Label('Enter a string to search')
        Radiobutton('Highlight first;Highligth all')
        
        App.str = Text(height=10)
        App.str.bind('<Key>', self.reevaluate)
        App.str.tag_configure("hit", background="yellow")

        Label('Groups')
        App.groups = Listbox()

        btags = App.re.bindtags()
        App.re.bindtags(btags[1:] + btags[:1])

        btags = App.str.bindtags()
        App.str.bindtags(btags[1:] + btags[:1])
        self.compiled = None

    def recompile(self, event=None):
        print('recompile')
        try:
            self.compiled = re.compile(App.re.get())
        except re.error as msg:
            self.compiled = None
            App.status.config(
                    text="re.error: %s" % str(msg),
                    background="red")
        self.reevaluate()

    def reevaluate(self, event=None):
        print('reevaluate')
        try:
            self.str.tag_remove("hit", "1.0", END)
        except TclError:
            pass
        try:
            self.str.tag_remove("hit0", "1.0", END)
        except TclError:
            pass
        self.groups.delete(0, END)
        if not self.compiled:
            return
        self.str.tag_configure("hit", background="yellow")
        self.str.tag_configure("hit0", background="orange")
        text = self.str.get("1.0", END)
        last = 0

        nmatches = 0
        while last <= len(text):
            m = self.compiled.search(text, last)
            if m is None:
                break
            first, last = m.span()
            if last == first:
                last = first+1
                tag = "hit0"
            else:
                tag = "hit"
            pfirst = "1.0 + %d chars" % first
            plast = "1.0 + %d chars" % last
            self.str.tag_add(tag, pfirst, plast)
            if nmatches == 0:
                self.str.yview_pickplace(pfirst)
                groups = list(m.groups())
                groups.insert(0, m.group())
                for i in range(len(groups)):
                    g = "%2d: %r" % (i, groups[i])
                    App.groups.insert(END, g)
            nmatches = nmatches + 1
            if self.showvar.get() == "first":
                break

        if nmatches == 0:
            self.status.config(text="(no match)",
                                      background="yellow")
        else:
            self.status.config(text="")

Demo().run()

re1.py

Regular expression

../_images/re2.png
from tklib import *
import re
app = App('Regular expression')

Label('Regular expressions', font='Arial 18')
Label('Enter a Perl-style regular expression')
App.re = Entry('regex')

app.run()

re2.py