Skip to content

Instantly share code, notes, and snippets.

@pbugnion
Last active July 9, 2021 11:21
Show Gist options
  • Save pbugnion/5bb7878ff212a0116f0f1fbc9f431a5c to your computer and use it in GitHub Desktop.
Save pbugnion/5bb7878ff212a0116f0f1fbc9f431a5c to your computer and use it in GitHub Desktop.
Multiple checkbox selection with searching with ipywidgets

Multiple selection with checkboxes and search field

Often, you want the user to choose n options (where n is small-ish) from a very large (hundreds or thousands) number of possibilities. Good UX around this dictates that the user should be able to search for the options they want.

This gist puts together a minimal example of binding a search field with multiple checkboxes using ipywidgets.

Usage

widget = multi_checkbox_widget(['hello', 'world'])
widget # Display the widget

To get the selected options:

selected_options = [widget.description for widget in w.children[1].children if widget.value]

Possible improvements

  • This is in need of some layout/CSS TLC.

  • At the moment, we just use difflib from the standard library for searching through the options. It mostly works, but I don't think it's what difflib is supposed to be used as. Using a different search algorithm might lead to better results.

import difflib
import random
import requests
import ipywidgets as widgets
def multi_checkbox_widget(descriptions):
""" Widget with a search field and lots of checkboxes """
search_widget = widgets.Text()
options_dict = {description: widgets.Checkbox(description=description, value=False) for description in descriptions}
options = [options_dict[description] for description in descriptions]
options_widget = widgets.VBox(options, layout={'overflow': 'scroll'})
multi_select = widgets.VBox([search_widget, options_widget])
# Wire the search field to the checkboxes
def on_text_change(change):
search_input = change['new']
if search_input == '':
# Reset search field
new_options = [options_dict[description] for description in descriptions]
else:
# Filter by search field using difflib.
close_matches = difflib.get_close_matches(search_input, descriptions, cutoff=0.0)
new_options = [options_dict[description] for description in close_matches]
options_widget.children = new_options
search_widget.observe(on_text_change, names='value')
return multi_select
# Example of using the widget
# Get lots of words for our options
words_url = 'https://svnweb.freebsd.org/csrg/share/dict/words?view=co&content-type=text/plain'
response = requests.get(words_url)
response.raise_for_status()
words = response.text
words = set([word.lower() for word in words.splitlines()])
descriptions = random.sample(words, 100)
multi_checkbox_widget(descriptions)
@agg437
Copy link

agg437 commented Nov 7, 2020

Hey! @pbugnion
I don't know if it is too late, but I think there is a simple way to improve the search algorithm. It would be to rewrite the close_matches
definition as it follows:
close_matches = sorted(list(filter(lambda x: search_input in x, descriptions)))
With that code you manage to extract all the words that contain the search_input, and besides, words are showed in alphabetical order.
I hope it helps.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment