Disable triggering of callbacks temporarily

Hello,

I am writing a dashboard for filtering data. I generate multi selects for categorical features and range sliders for numerical ones. If one of the widgets is updated I want to update the other accordingly to the selection. The problem is, when a multi select is updated this triggers a new update which has some unwanted side effects. I would like to disable the callbacks for a widget temporarily so I can do the updates.
Here is a minimal example:

dummy_data = {"a": [1,1,1,2,2,2,3,3,3], "b": [4,4,4,5,5,6,5,6,6]}
multi_select_1 = pnw.MultiSelect(name="MultiSelect 1", options=[1,2,3], value=[1,2,3])
multi_select_2 = pnw.MultiSelect(name="MultiSelect 2", options=[4,5,6], value=[4,5,6])

def update_selections(event):
    selection_1 = [entry in multi_select_1.value for entry in dummy_data["a"]]
    selection_2 = [entry in multi_select_2.value for entry in dummy_data["b"]]
    and_selection = [i and j for i,j in zip(selection_1, selection_2)]
    multi_select_1.value = list(set([i for i,j in zip(dummy_data["a"], and_selection) if j]))
    multi_select_2.value = list(set([i for i,j in zip(dummy_data["b"], and_selection) if j]))

multi_select_1.param.watch(update_selections, "value")
multi_select_2.param.watch(update_selections, "value")

pn.Row(multi_select_1, multi_select_2)

Hi @fstroth

Welcome to the community and thanks for reaching out.

This might be achieved in different ways. My proposal would be to keep track of whether updating has started using a boolean parameter.

import panel as pn
import panel.widgets as pnw

dummy_data = {"a": [1,1,1,2,2,2,3,3,3], "b": [4,4,4,5,5,6,5,6,6]}
multi_select_1 = pnw.MultiSelect(name="MultiSelect 1", options=[1,2,3], value=[1,2,3])
multi_select_2 = pnw.MultiSelect(name="MultiSelect 2", options=[4,5,6], value=[4,5,6])

UPDATING = False

def _update_selections_org(event):
    print("updating")
    selection_1 = [entry in multi_select_1.value for entry in dummy_data["a"]]
    selection_2 = [entry in multi_select_2.value for entry in dummy_data["b"]]
    and_selection = [i and j for i,j in zip(selection_1, selection_2)]
    multi_select_1.value = list(set([i for i,j in zip(dummy_data["a"], and_selection) if j]))
    multi_select_2.value = list(set([i for i,j in zip(dummy_data["b"], and_selection) if j]))
    print("updating done")

def update_selections(event):
    global UPDATING
    if UPDATING:
        return

    UPDATING = True
    _update_selections_org(event)
    UPDATING = False

multi_select_1.param.watch(update_selections, "value")
multi_select_2.param.watch(update_selections, "value")

pn.Row(multi_select_1, multi_select_2).servable()
1 Like

Hey @Marc,

thank you a lot. By the way great work with Awesome Panel a very helpful resource.

1 Like

I would really like to build a construct for this into Panel because this comes up relatively frequently. Param already bundles a similar thing as param.discard_events however this discards all events, even those internal to Panel, which means the widget doesn’t update. So I’d suggest something like pn.io.discard_user_events or similar. Feature request on that front very welcome.

Alternatively param maybe should make it straightforward to avoid recursive events entirely, i.e. if a particular callback is currently being called it could skip that callback if it gets triggered again while still processing the initial call to it.