pythoncallbackplotlyplotly-dashselectall

Combining a select all function into a single component - plotly dash


There are a few questions regarding the issue of adding a select all option for filtering data with numerous values using plotly dash. The link list below performs the function but it adds every value into the dropdown bar. Rather than a single tab or component that represents all the values. This makes the app layout unworkable as the page is filled with values in the dropdown bar.

It would be constructive if a single label (all/select all etc) could be used in-place of listing all individual values.

Python Plotly Dash dropdown Adding a "select all" for scatterplot

Using below, when select all is chosen, every value appears in the dropdown-bar. Ideally, when select all is chosen, the dropdown bar should only show a select all label, while all data should be visible.

Further, if select all as a single tab is present and the user wants to view a unique id, then select all should be dropped for that id. Conversely, if a unique id is actioned and select all is executed, then that unique id should be dropped for the single select all tab. This should be done in one step. Not two steps, where you need to clear the bar before selecting a subsequent option.

import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output
import dash_mantine_components as dmc
import pandas as pd
import plotly.express as px
import numpy as np
import random
from string import ascii_letters

df = pd.DataFrame(data=list(range(100)), columns=["tcd"])
df['humidity'] = pd.DataFrame(np.random.rand(100, 1) * 254)
df['Capsule_ID'] = [''.join(random.choice(ascii_letters) for x in range(10)) for _ in range(len(df))]

capsuleID = df['Capsule_ID'].unique()
capsuleID_names = list(capsuleID)

capsuleID_names_1 = [{'label': k, 'value': k } for k in sorted(capsuleID)]
capsuleID_names_2 = [{'label': '(Select All)', 'value': 'All'}]
capsuleID_names_all = capsuleID_names_1 + capsuleID_names_2


app = dash.Dash(__name__)

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([

    html.H1("Web Application Dashboards with Dash", style={'text-align': 'center'}),

    dcc.Dropdown(id="capsule_select",
             options=capsuleID_names_all,
             optionHeight=25,
             multi=True,
             searchable=True,
             placeholder='Please select...',
             clearable=True,
             value=[''],
             style={'width': "40%"}
             ),
    html.Div([
        dcc.Graph(id="the_graph")
    ]),

])

@app.callback(
    Output("the_graph", "figure"),
    Output("capsule_select", "value"),
    Input("capsule_select", "value"),
)
def update_graph(capsules_chosen):
    dropdown_values = capsules_chosen

    if "All" in capsules_chosen:
        dropdown_values = df["Capsule_ID"]
        dff = df
    else:
        dff = df[df["Capsule_ID"].isin(capsules_chosen)]

    scatterplot = px.scatter(
        data_frame=dff,
        x="tcd",
        y="humidity",
        color = "Capsule_ID"
    )

    scatterplot.update_traces(textposition="top center")

    return scatterplot, dropdown_values 

if __name__ == '__main__':
    app.run_server(debug=True)

enter image description here

Edit 2:

Configuring select all to be the only label appears to work but the functionality doesn't make sense. Attached is another screenshot. select all should exist in isolation. You can't have all the values and another value.

Two conditions should be met:

  1. If select all is present in the dropdown bar and a separate unique id is chosen, then select all should be dropped for that id (you shouldn't have select all and a unique id).

  2. Conversely, if a unique id is actioned and select all is executed, then that unique id should be dropped for the single select all tab.

    def update_graph(capsules_chosen):
    
        dropdown_values = capsules_chosen
    
        if "All" in capsules_chosen:
            dff = df
        else:
            dff = df[df["Capsule_ID"].isin(capsules_chosen)]
    
        scatterplot = px.scatter(
            data_frame=dff,
            x="tcd",
            y="humidity",
            color = "Capsule_ID"
        )
    
        scatterplot.update_traces(textposition="top center")
    
        return scatterplot, dropdown_values 
    

enter image description here


Solution

  • You can modify your callback so that you check the most recent selection and all selections. If the user selects All when there's already one or more selections, All replaces everything else, and when the user selects something else when All was previously selected, this selection overwrites All

    @app.callback(
        Output("the_graph", "figure"),
        Output("capsule_select", "value"),
        Input("capsule_select", "value"),
    )
    def update_graph(capsules_chosen):
        dropdown_chosen = capsules_chosen
    
        ## if user selects 'All' after one or more other dropdowns were selected, overwrite dropdown selection with 'All'
        ## if user selects another dropdown after 'All' was selected, overwrite dropdown selection with most recent
        if len(dropdown_chosen) > 1:
            if dropdown_chosen[-1] == 'All':
                dropdown_chosen = ["All"]
            if ('All' in dropdown_chosen) & (dropdown_chosen[-1] != 'All'):
                dropdown_chosen = dropdown_chosen[-1]
    
        if "All" in capsules_chosen:
            dff = df
        else:
            dff = df[df["Capsule_ID"].isin(capsules_chosen)]
    
        scatterplot = px.scatter(
            data_frame=dff,
            x="tcd",
            y="humidity",
            color = "Capsule_ID"
        )
    
        scatterplot.update_traces(textposition="top center")
    
        return scatterplot, dropdown_chosen 
    

    enter image description here