Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MultiChoice widget #1140

Merged
merged 3 commits into from
Mar 11, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions examples/reference/widgets/MultiChoice.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import panel as pn\n",
"pn.extension()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The ``MultiChoice`` widget allows selecting multiple values from a list of options. It falls into the broad category of multi-value, option-selection widgets that provide a compatible API and include the [``MultiSelect``](MultiSelect.ipynb), [``CrossSelector``](CrossSelector.ipynb), [``CheckBoxGroup``](CheckBoxGroup.ipynb) and [``CheckButtonGroup``](CheckButtonGroup.ipynb) widgets. The ``MultiChoice`` widget provides a much more compact UI than [``MultiSelect``](MultiSelect.ipynb).\n",
"\n",
"For more information about listening to widget events and laying out widgets refer to the [widgets user guide](../../user_guide/Widgets.ipynb). Alternatively you can learn how to build GUIs by declaring parameters independently of any specific widgets in the [param user guide](../../user_guide/Param.ipynb). To express interactivity entirely using Javascript without the need for a Python server take a look at the [links user guide](../../user_guide/Param.ipynb).\n",
"\n",
"#### Parameters:\n",
"\n",
"For layout and styling related parameters see the [customization user guide](../../user_guide/Customization.ipynb).\n",
"\n",
"##### Core\n",
"\n",
"* **``options``** (list or dict): List or dictionary of options\n",
"* **``max_items``** (int): Maximum number of options that can be selected\n",
"* **``value``** (list): Currently selected option values\n",
"\n",
"##### Display\n",
"\n",
"* **``delete_button``** (boolean): Whether to display a button to delete a selected option\n",
"* **``disabled``** (boolean): Whether the widget is editable\n",
"* **``name``** (str): The title of the widget\n",
"* **``option_limit``** (int): Maximum number of options to display at once.\n",
"* **``placeholder``** (str): String displayed when no selection has been made.\n",
"* **``solid``** (boolean): Whether to display widget with solid or light style.\n",
"\n",
"___"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"multi_choice = pn.widgets.MultiChoice(name='MultiSelect', value=['Apple', 'Pear'],\n",
" options=['Apple', 'Banana', 'Pear', 'Strawberry'])\n",
"\n",
"pn.Column(multi_choice, height=200)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"``MultiChoice.value`` returns a list of the currently selected options:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"multi_choice.value"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `solid` option controls the style of the widget:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pn.Column(multi_choice.clone(solid=False), height=200)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Controls\n",
"\n",
"The `MultiChoice` widget exposes a number of options which can be changed from both Python and Javascript. Try out the effect of these parameters interactively:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pn.Row(multi_choice.controls(jslink=True), multi_choice)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.5"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
27 changes: 26 additions & 1 deletion panel/tests/widgets/test_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import numpy as np
import pytest

from panel.widgets import Select, MultiSelect, CrossSelector, ToggleGroup
from panel.widgets import (
CrossSelector, MultiChoice, MultiSelect, Select, ToggleGroup
)
from panel.util import as_unicode


Expand Down Expand Up @@ -153,6 +155,29 @@ def test_multi_select(document, comm):
assert widget.value == ['C', 'A']


def test_multi_choice(document, comm):
choice = MultiChoice(options=OrderedDict([('A', 'A'), ('1', 1), ('C', object)]),
value=[object, 1], name='MultiChoice')

widget = choice.get_root(document, comm=comm)

assert isinstance(widget, choice._widget_type)
assert widget.title == 'MultiChoice'
assert widget.value == ['C', '1']
assert widget.options == ['A', '1', 'C']

widget.value = ['1']
choice._comm_change({'value': ['1']})
assert choice.value == [1]

widget.value = ['A', 'C']
choice._comm_change({'value': ['A', 'C']})
assert choice.value == ['A', object]

choice.value = [object, 'A']
assert widget.value == ['C', 'A']


def test_multi_select_change_options(document, comm):
select = MultiSelect(options=OrderedDict([('A', 'A'), ('1', 1), ('C', object)]),
value=[object, 1], name='Select')
Expand Down
3 changes: 2 additions & 1 deletion panel/widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
)
from .select import (# noqa
AutocompleteInput, CheckBoxGroup, CheckButtonGroup, CrossSelector,
MultiSelect, RadioButtonGroup, RadioBoxGroup, Select, ToggleGroup
MultiChoice, MultiSelect, RadioButtonGroup, RadioBoxGroup, Select,
ToggleGroup
)
from .tables import DataFrame # noqa
39 changes: 30 additions & 9 deletions panel/widgets/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
AutocompleteInput as _BkAutocompleteInput, CheckboxGroup as _BkCheckboxGroup,
CheckboxButtonGroup as _BkCheckboxButtonGroup, MultiSelect as _BkMultiSelect,
RadioButtonGroup as _BkRadioButtonGroup, RadioGroup as _BkRadioBoxGroup,
Select as _BkSelect)
Select as _BkSelect, MultiChoice as _BkMultiChoice)

from ..layout import Column, VSpacer
from ..util import as_unicode, isIn, indexOf
Expand Down Expand Up @@ -115,16 +115,10 @@ def _get_embed_state(self, root, max_opts=3):
lambda x: x.value, 'value', 'cb_obj.value')


class MultiSelect(Select):

size = param.Integer(default=4, doc="""
The number of items displayed at once (i.e. determines the
widget height).""")
class _MultiSelectBase(Select):

value = param.List(default=[])

_widget_type = _BkMultiSelect

_supports_embed = False

def _process_param_change(self, msg):
Expand All @@ -150,6 +144,34 @@ def _process_property_change(self, msg):
return msg


class MultiSelect(_MultiSelectBase):

size = param.Integer(default=4, doc="""
The number of items displayed at once (i.e. determines the
widget height).""")

_widget_type = _BkMultiSelect


class MultiChoice(_MultiSelectBase):

delete_button = param.Boolean(default=True, doc="""
Whether to display a button to delete a selected option.""")

max_items = param.Integer(default=None, bounds=(1, None), doc="""
Maximum number of options that can be selected.""")

option_limit = param.Integer(default=None, bounds=(1, None), doc="""
Maximum number of options to display at once.""")

placeholder = param.String(default='', doc="""
String displayed when no selection has been made.""")

solid = param.Boolean(default=True, doc="""
Whether to display widget with solid or light style.""")

_widget_type = _BkMultiChoice


class AutocompleteInput(Widget):

Expand All @@ -168,7 +190,6 @@ class AutocompleteInput(Widget):
_rename = {'name': 'title', 'options': 'completions'}



class _RadioGroupBase(Select):

_supports_embed = False
Expand Down