NestedSelect#

Open this notebook in Jupyterlite | Download this notebook from GitHub (right-click to download).


import panel as pn
pn.extension()

Discover more on using widgets to add interactivity to your applications in the how-to guides on interactivity. Alternatively, learn how to set up callbacks and (JS-)links between parameters or how to use them as part of declarative UIs with Param.

Parameters:#

For details on other options for customizing the component see the layout and styling how-to guides.

Core#

  • options (dict | callable): The options to select from. The options may be nested dictionaries, lists, or callables that return those types. If callables are used, the callables must accept level and value keyword arguments, where level is the level that updated and value is a dictionary of the current values, containing keys up to the level that was updated.

  • value (dict): The value from all the Select widgets; the keys are the levels names. If no levels names are specified, the keys are the levels indices.

  • layout (ListPanel | dict): The layout type of the widgets. If a dictionary, a “type” key can be provided, to specify the layout type of the widgets, and any additional keyword arguments will be used to instantiate the layout.

  • levels (list): Either a list of strings or a list of dictionaries. If a list of strings, the strings are used as the names of the levels. If a list of dictionaries, each dictionary may have a “name” key, which is used as the name of the level, a “type” key, which is used as the type of widget, and any corresponding widget keyword arguments. Must be specified if options is callable.

Display#

  • disabled (boolean): Whether the widget is editable

  • name (str): The title of the widget


select = pn.widgets.NestedSelect(
    options={
        "GFS": {
            "0.25 deg": ["00Z", "06Z", "12Z", "18Z"],
            "0.5 deg": ["00Z", "12Z"],
            "1 deg": ["00Z", "12Z"],
        },
        "NAME": {
            "12 km": ["00Z", "12Z"],
            "3 km": ["00Z", "12Z"],
        },
    },
    levels=["Model", "Resolution", "Initialization"],
)
select

Like most other widgets, NestedSelect has a value parameter that can be accessed or set:

select.value
{'Model': 'GFS', 'Resolution': '0.25 deg', 'Initialization': '00Z'}

A different layout type can be provided to the NestedSelect to change the layout of the widgets.

select = pn.widgets.NestedSelect(
    options={
        "GFS": {
            "0.25 deg": ["00Z", "06Z", "12Z", "18Z"],
            "0.5 deg": ["00Z", "12Z"],
            "1 deg": ["00Z", "12Z"],
        },
        "NAME": {
            "12 km": ["00Z", "12Z"],
            "3 km": ["00Z", "12Z"],
        },
    },
    levels=["Model", "Resolution", "Initialization"],
    layout=pn.Row
)
select

If a dict is provided, a “type” key can be provided, to specify the layout type of the widgets, and any additional keyword arguments will be used to instantiate the layout.

select = pn.widgets.NestedSelect(
    options={
        "GFS": {
            "0.25 deg": ["00Z", "06Z", "12Z", "18Z"],
            "0.5 deg": ["00Z", "12Z"],
            "1 deg": ["00Z", "12Z"],
        },
        "NAME": {
            "12 km": ["00Z", "12Z"],
            "3 km": ["00Z", "12Z"],
        },
    },
    levels=["Model", "Resolution", "Initialization"],
    layout={"type": pn.GridBox, "ncols": 2}
)
select

If levels names are not set, the value is keyed off the level index.

select = pn.widgets.NestedSelect(
    options={
        "GFS": {
            "0.25 deg": ["00Z", "06Z", "12Z", "18Z"],
            "0.5 deg": ["00Z", "12Z"],
            "1 deg": ["00Z", "12Z"],
        },
        "NAME": {
            "12 km": ["00Z", "12Z"],
            "3 km": ["00Z", "12Z"],
        },
    },
)
select.value
{0: 'GFS', 1: '0.25 deg', 2: '00Z'}

You can also define the default value by providing a dict.

select = pn.widgets.NestedSelect(
    options={
        "GFS": {
            "0.25 deg": ["00Z", "06Z", "12Z", "18Z"],
            "0.5 deg": ["00Z", "12Z"],
            "1 deg": ["00Z", "12Z"],
        },
        "NAME": {
            "12 km": ["00Z", "12Z"],
            "3 km": ["00Z", "12Z"],
        },
    },
    value={"Model": "NAME", "Resolution": "12 km", "Initialization": "12Z"},
    levels=["Model", "Resolution", "Initialization"],
)
select

Not all keys of the value need to be specified, and the keys can be specified in any order.

select = pn.widgets.NestedSelect(
    options={
        "GFS": {
            "0.25 deg": ["00Z", "06Z", "12Z", "18Z"],
            "0.5 deg": ["00Z", "12Z"],
            "1 deg": ["00Z", "12Z"],
        },
        "NAME": {
            "12 km": ["00Z", "12Z"],
            "3 km": ["00Z", "12Z"],
        },
    },
    value={"Initialization": "12Z", "Resolution": "0.5 deg"},
    levels=["Model", "Resolution", "Initialization"],
)
select

An incomplete definition of options can also be used. The corresponding subsequent widgets will be hidden.

select = pn.widgets.NestedSelect(
    options={
        "NAME": {},
        "GFS": {
            "0.25 deg": ["00Z", "06Z", "12Z", "18Z"],
            "0.5 deg": ["00Z", "12Z"],
            "1 deg": ["00Z", "12Z"],
        },
    },
    levels=["Model", "Resolution", "Initialization"],
)
select

The value for the hidden widgets will be None.

select.value = {"Model": "NAME"}

Alternatively, options can be a callable. Its nested levels too: e.g. options={"Daily": list_options, "Monthly": list_options}.

If callables are used, the callables must accept level and value keyword arguments, where level is the level that updated and value is a dictionary of the current values, containing keys up to the level that was updated.

Note, the callable can vary across options, and levels must be provided if any of the options is callable.

def list_options(level, value):
    if level == "time_step":
        options = {"Daily": list_options, "Monthly": list_options}
    elif level == "level_type":
        options = {f"{value['time_step']}_upper": list_options, f"{value['time_step']}_lower": list_options}
    else:
        options = [f"{value['level_type']}.json", f"{value['level_type']}.csv"]

    return options

pn.widgets.NestedSelect(
    options=list_options,
    levels=["time_step", "level_type", "file_type"],
)

This is useful if you are trying to use options from a hosted source.

Using pn.cache here can help improve user experience and reduce the risk of rate limits.

import panel as pn
pn.extension()

@pn.cache()
def list_options(level, value):
    value_path = "/".join(list(value.values()))
    url = f"https://downloads.psl.noaa.gov/Datasets/ncep.reanalysis/{value_path}"

    options = [var.rstrip("/") for var in pd.read_html(url)[0]["Name"].dropna()[1:]]
    if level == "time_step":
        options = {option: list_options for option in options if option[0].isupper()}
    elif level == "level_type":
        options = {option: list_options for option in options if option[0].islower()}
    else:
        options = [option for option in options if option.endswith(".nc")]

    return options


select = pn.widgets.NestedSelect(
    options=list_options,
    levels=["time_step", "level_type", "file"],
)
select

levels also accepts a list of dicts, where each dict contains the type of widget and its corresponding kwargs.

select = pn.widgets.NestedSelect(
    options={
        "GFS": {
            "0.25 deg": ["00Z", "06Z", "12Z", "18Z"],
            "0.5 deg": ["00Z", "12Z"],
            "1 deg": ["00Z", "12Z"],
        },
        "NAME": {
            "12 km": ["00Z", "12Z"],
            "3 km": ["00Z", "12Z"],
        },
    },
    value={"Model": "NAME", "Resolution": "12 km", "Initialization": "00Z"},
    levels=[
        {"name": "Model", "type": pn.widgets.RadioButtonGroup, "button_type": "primary"},
        {"name": "Resolution", "type": pn.widgets.Select, "width": 100},
        {"name": "Initialization", "type": pn.widgets.DiscreteSlider, "width": 100},
    ],
)
select

Open this notebook in Jupyterlite | Download this notebook from GitHub (right-click to download).