Although you can see an example of a checkbox being disabled
in the interactive demo on the checkbox documentation page, I could not figure out a way to accomplish this when trying to build out a Dash app
. If you scroll down in that same documentation page, there don't appear to be any keyword arguments for Checkbox
or CheckboxGroup
components that allow checkboxes to be disabled. I am not sure if this is a feature that hasn't been built out yet, or if I am missing something.
When I was trying figure out a solution, I also noticed that for Checkbox
components inside a CheckboxGroup
, callbacks cannot modify whether these checkboxes are clicked or not, even if we try passing the argument checked="True"
or checked=True
. This means that using a callback that replaces the children checkbox components of a CheckboxGroup component will not work as intended.
As far as I can tell, we will need to redesign the app layout so instead of having a CheckboxGroup component with Checkbox children, you'll use three Checkbox
components inside an inline-block
so that they appear horizontally.
In order to prevent multiple selection, if you cannot disable checkboxes, you'll need each selection to be aware of the previous selection and modify the state of all the checkboxes accordingly. For example, if you have checkboxes a,b,c
and you click a
, then when you click b
, the callback will need to know that a
was previously clicked, that b
was currently clicked, and then deselect a
while selecting b
to prevent multiple selection. We can use dcc.Store
to store the information of the last checkbox that was selected.
Here is an example of a working Dash app that prevents multiple selection – it doesn't disable the other boxes as you requested, but that isn't necessarily a bad thing because if the user makes an incorrect selection, they can go ahead and click the intended checkbox.
import numpy as np
import dash_mantine_components as dmc
from dash import Dash, Input, Output, dcc, html
from dash.exceptions import PreventUpdate
app = Dash(__name__)
app.layout = html.Div(
[
html.Div(
[
dmc.Checkbox(label="LABEL_A", value="lbl_a", id="a", style={"display": "inline-block", "width": "120px"},),
dmc.Checkbox(label="LABEL_B", value="lbl_b", id="b", style={"display": "inline-block", "width": "120px"},),
dmc.Checkbox(label="LABEL_C", value="lbl_c", id="c", style={"display": "inline-block", "width": "120px"},)
]
),
dcc.Store(id="last-checkbox-selection")
]
)
@app.callback(
Output("a", "checked"),
Output("b", "checked"),
Output("c", "checked"),
Output("last-checkbox-selection", "data"),
Input("a", "checked"),
Input("b", "checked"),
Input("c", "checked"),
Input("last-checkbox-selection", "data"),
prevent_initial_call=True
)
def select(checkbox_a, checkbox_b, checkbox_c, old_checkbox_value):
default_checkbox_labels = ["LABEL_A", "LABEL_B", "LABEL_C"]
default_checkbox_values = ["lbl_a", "lbl_b", "lbl_c"]
checkbox_values_mask = [bool(val) for val in [checkbox_a, checkbox_b, checkbox_c]]
new_checkboxes = np.array(default_checkbox_values)[checkbox_values_mask].tolist()
## when you uncheck a box
if new_checkboxes == []:
old_checkbox_value = None
## when you check a box
else:
## if no previous box checked, then store current value
if old_checkbox_value is None:
old_checkbox_value = new_checkboxes[0]
## if a box was previously checked, then first remove what was previously checked from current selection
else:
new_checkboxes.remove(old_checkbox_value)
old_checkbox_value = new_checkboxes[0]
checked_labels = [True if val in new_checkboxes else False for val in default_checkbox_values]
return checked_labels + [old_checkbox_value]
if __name__ == "__main__":
app.run_server(debug=True)
