Pattern-Matching Callback for Updating the Items Property of Multiple Carousels

Hello everyone!

I have the following dashboard:

Each set of MRIs and their corresponding 3d surfaces are organized by session (You could see all sessions by scrolling up). Each session has a control board (the dark violet card on the upper right corner) with a set of controls. The group of buttons in this control board update the MRIs and 3d surfaces being displayed by the carousels in the session. The sliders control the MRI slices being displayed and the views of the 3d surfaces. All these controls work perfectly for a session.

I have a total of 12 sessions and I want each session to have a control board as described above. I do not want to repeat my code for the control board and the respective callbacks twelve times. Is there a way to use pattern matching callbacks to attain what I have in mind?

I have two days trying different things but nothing works. Any help will be highly appreciated. Let me know if you need more info or access to my code etc. I am more than happy to share. I just do not want to make this post longer. :slight_smile: Thank you for your attention!

Hello @numam,

My recommendation would be to use a function that creates your desired layout, and feed that function the info from a list to create your layout.

Pattern-matching MATCH seems to be what you are after. You’ll have to make sure each one of the card components has the same index. And then setup your callback to use match and the outputs to the index that matches it.

Thank you for your reply @jinnyzor. I have tried with match but I keep getting an error that says

Multiple objects were found for an Output of a callback that only takes one value. The id spec is {“index”:{“wild”:“MATCH”},“type”:“carouselSes01”} with MATCH values [1] and the property is items. The objects we found are: [{“id”:{“index”:1,“type”:“carouselSes01”},“property”:“items”},{“id”:{“index”:1,“type”:“carouselSes01”},“property”:“items”}]

I am sure I have not understood well how to use it.

The following is a very simplified code that you can use to help me understand what you have in mind. The pngs for the carousels are found here: Box

# DASH
from dash import html, dcc
from dash.dependencies import Input, Output, State, ALL, MATCH
import dash_bootstrap_components as dbc
from jupyter_dash import JupyterDash
JupyterDash.infer_jupyter_proxy_config()



button_group_ses01=  html.Div([dbc.RadioItems(options=[
                    {"label": "Run-01", "value": 1},
                    {"label": "Run-03", "value": 2}
                ],
            id= "button_group_ses01",
            value = 1,
            className="btn-group text-center"
        )])

button_group_ses02=  html.Div([dbc.RadioItems(options=[
                    {"label": "Run-01", "value": 1},
                    {"label": "Run-03", "value": 2}
                ],
            id= "button_group_ses02",
            value = 1,
            className="btn-group text-center"
        )])

ses01 = "assets/QA_cs/sub-DZ17BH/ses-01" 
ses02 = "assets/QA_cs/sub-DZ17BH/ses-02"

car01_4_session_01  = dbc.Carousel(
    items=[
        {"src": f"{ses01}/run-01/fv_snaps_surf_annot_dispind_pial_1_lat_lh.png"},
        {"src": f"{ses01}/run-01/fv_snaps_surf_annot_dispind_pial_2_med_lh.png"},
        {"src": f"{ses01}/run-01/fv_snaps_surf_annot_dispind_pial_3_inf_lh.png"}
    ],
    id= {"type":f"carouselSes01", "index": 1},
    indicators=False,
    interval=None,
    slide="off",
    className="carousel-fade",
)

car02_4_session_01 = dbc.Carousel(
    items=[
        {"src": f"{ses01}/run-01/fv_snaps_surf_blnk_dispind_pial_1_lat_lh.png"},
        {"src": f"{ses01}/run-01/fv_snaps_surf_blnk_dispind_pial_2_med_lh.png"},
        {"src": f"{ses01}/run-01/fv_snaps_surf_blnk_dispind_pial_3_inf_lh.png"}
    ],
    id= {"type":f"carouselSes01", "index": 2},
    indicators=False,
    interval=None,
    slide="off",
    className="carousel-fade",
)

car01_4_session_02  = dbc.Carousel(
    items=[
        {"src": f"{ses02}/run-01/fv_snaps_surf_annot_dispind_pial_1_lat_lh.png"},
        {"src": f"{ses02}/run-01/fv_snaps_surf_annot_dispind_pial_2_med_lh.png"},
        {"src": f"{ses02}/run-01/fv_snaps_surf_annot_dispind_pial_3_inf_lh.png"}
    ],
    id= {"type":f"carouselSes02", "index": 1},
    indicators=False,
    interval=None,
    slide="off",
    className="carousel-fade",
)

car02_4_session_02 = dbc.Carousel(
    items=[
        {"src": f"{ses02}/run-01/fv_snaps_surf_blnk_dispind_pial_1_lat_lh.png"},
        {"src": f"{ses02}/fv_snaps_surf_blnk_dispind_pial_2_med_lh.png"},
        {"src": f"{ses02}/fv_snaps_surf_blnk_dispind_pial_3_inf_lh.png"}
    ],
    id= {"type":f"carouselSes02", "index": 2},
    indicators=False,
    interval=None,
    slide="off",
    className="carousel-fade",
)

app = JupyterDash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

app.layout  = dbc.Container([
                            dbc.Row([
                                dbc.Col([
                                    html.H1("SESSION 01"),
                                    button_group_ses01
                                ], width = 4)]),
                            dbc.Row([
                                    dbc.Col([
                                        car01_4_session_01,
                                    ]),
                                    dbc.Col([
                                        car02_4_session_01,
                                        ]),
                                    ]),
    
                                dbc.Row([
                                dbc.Col([
                                    html.H1("SESSION 02"),
                                    button_group_ses02
                                ], width = 4)]),
                            dbc.Row([
                                    dbc.Col([
                                        car01_4_session_02,
                                    ]),
                                    dbc.Col([
                                        car02_4_session_02,
                                        ]),
                                    ])
                                ])

# SLIDERS 
@app.callback(
    Output({"type":"carouselSes01", "index": ALL}, "items"),
    Input("button_group_ses01", "value"),
    prevent_initial_call=True
)

def update_subjectdata(run_chosen):
    runList = ["run-01", "run-03"]
    run = runList[run_chosen - 1]

    car1=[
    {"src": f"{ses01}/{run}/fv_snaps_surf_thickness_dispind_pial_1.0_3.5_1_lat_rh.png"},
    {"src": f"{ses01}/{run}/fv_snaps_surf_thickness_dispind_pial_1.0_3.5_2_med_rh.png"},
    {"src": f"{ses01}/{run}/fv_snaps_surf_thickness_dispind_pial_1.0_3.5_3_inf_rh.png"}]

    car2=[
    {"src": f"{ses01}/{run}/fv_snaps_surf_thickness_disptmpl_inflated_1.0_3.5_1_lat_rh.png"},
    {"src": f"{ses01}/{run}/fv_snaps_surf_thickness_disptmpl_inflated_1.0_3.5_med_rh.png"},
    {"src": f"{ses01}/{run}/fv_snaps_surf_thickness_disptmpl_inflated_1.0_3.5_3_inf_rh.png"}]
    return [car1,car2]

@app.callback(
    Output({"type":"carouselSes02", "index": ALL}, "items"),
    Input("button_group_ses02", "value"),
    prevent_initial_call=True
)

def update_subjectdata(run_chosen):
    runList = ["run-01", "run-03"]
    run = runList[run_chosen - 1]

    car1=[
    {"src": f"{ses01}/{run}/fv_snaps_surf_thickness_dispind_pial_1.0_3.5_1_lat_rh.png"},
    {"src": f"{ses01}/{run}/fv_snaps_surf_thickness_dispind_pial_1.0_3.5_2_med_rh.png"},
    {"src": f"{ses01}/{run}/fv_snaps_surf_thickness_dispind_pial_1.0_3.5_3_inf_rh.png"}]

    car2=[
    {"src": f"{ses01}/{run}/fv_snaps_surf_thickness_disptmpl_inflated_1.0_3.5_1_lat_rh.png"},
    {"src": f"{ses01}/{run}/fv_snaps_surf_thickness_disptmpl_inflated_1.0_3.5_med_rh.png"},
    {"src": f"{ses01}/{run}/fv_snaps_surf_thickness_disptmpl_inflated_1.0_3.5_3_inf_rh.png"}]
    return [car1,car2]

app.run_server(mode ="external", debug=True)

The goal is to be able to update the items property of one set of carrousels using the respective radio buttons and one callback only.

I highly appreciate your insights.

Hello @numam,

Here is some adjustments that I made, I think they should work in jupyter, I got the working with dash:

# DASH
from dash import html, dcc, Input, Output, State, ALL, MATCH, Dash, ctx
#from dash.dependencies import
import dash_bootstrap_components as dbc
# from jupyter_dash import JupyterDash
#
# JupyterDash.infer_jupyter_proxy_config()

def carButton(sess):
    return html.Div([dbc.RadioItems(options=[
        {"label": "Run-01", "value": 'run-01'},
        {"label": "Run-03", "value": 'run-03'}
    ],
        id={'type':"button_group_ses",'index':f'{sess}'},
        value=1,
        className="btn-group text-center"
    )])

def carInfo(sess, run):
    info = sessions[sess]['data']
    return dbc.Carousel(
    items=[
        {"src": f"{info}/{run}/fv_snaps_surf_annot_dispind_pial_1_lat_lh.png"},
        {"src": f"{info}/{run}/fv_snaps_surf_annot_dispind_pial_2_med_lh.png"},
        {"src": f"{info}/{run}/fv_snaps_surf_annot_dispind_pial_3_inf_lh.png"}
    ],
    id={"type": f"carouselSes", "index": sess},
    indicators=False,
    interval=None,
    slide="off",
    className="carousel-fade",
    )

def carInfo2(sess, run):
    info = sessions[sess]['data']
    return dbc.Carousel(
        items=[
            {"src": f"{info}/{run}/fv_snaps_surf_blnk_dispind_pial_1_lat_lh.png"},
            {"src": f"{info}/{run}/fv_snaps_surf_blnk_dispind_pial_2_med_lh.png"},
            {"src": f"{info}/{run}/fv_snaps_surf_blnk_dispind_pial_3_inf_lh.png"}
        ],
        id={"type": f"carouselSes2", "index": sess},
        indicators=False,
        interval=None,
        slide="off",
        className="carousel-fade",
    )

sessions = {
'ses01': {'data': "assets/QA_cs/sub-DZ17BH/ses-01", 'name':'SESSION 01'},
'ses02': {'data': "assets/QA_cs/sub-DZ17BH/ses-02", 'name': 'SESSION 02'}
}

def buildCarousel(sess, run):
    return html.Div([dbc.Row([
            dbc.Col([
                html.H1(sessions[sess]['name']),
                carButton(sess)
            ], width=4)]),
            dbc.Row([
                dbc.Col([
                    carInfo(sess, run),
                ]),
                dbc.Col([
                    carInfo2(sess, run),
                ]),
            ])])

app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

app.layout = dbc.Container([buildCarousel(i, 'run-01') for i in sessions.keys()])


# SLIDERS
@app.callback(
    Output({"type": "carouselSes", "index": MATCH}, "items"),
    Output({"type": "carouselSes2", "index": MATCH}, "items"),
    Input({"type": "button_group_ses", "index": MATCH}, "value"),
    prevent_initial_call=True
)
def update_subjectdata(run):
    sess = ctx.triggered_id.index
    info = sessions[sess]['data']

    car1 = [
        {"src": f"{info}/{run}/fv_snaps_surf_thickness_dispind_pial_1.0_3.5_1_lat_rh.png"},
        {"src": f"{info}/{run}/fv_snaps_surf_thickness_dispind_pial_1.0_3.5_2_med_rh.png"},
        {"src": f"{info}/{run}/fv_snaps_surf_thickness_dispind_pial_1.0_3.5_3_inf_rh.png"}]

    car2 = [
        {"src": f"{info}/{run}/fv_snaps_surf_thickness_disptmpl_inflated_1.0_3.5_1_lat_rh.png"},
        {"src": f"{info}/{run}/fv_snaps_surf_thickness_disptmpl_inflated_1.0_3.5_med_rh.png"},
        {"src": f"{info}/{run}/fv_snaps_surf_thickness_disptmpl_inflated_1.0_3.5_3_inf_rh.png"}]
    return [car1, car2]



app.run_server(debug=True, port='12345')
1 Like

Thank you so much @jinnyzor. This works perfectly. I highly appreciate you taking the time to show me how. Have a very good day!

1 Like