Hi,
Here is an overview of what I am trying to do:
I have a top level tab group which has a few forms.
The ‘Load Testbench’ form looks like this:
When the user clicks ‘Load’, I want to add a new tab next to the HOME tab.
Here is the code for the layout of where the tab-group is setup:
def banner():
return dbc.Row(
[
dbc.Col(
[
html.H1("Run Dao")
],
),
],
className="rd-banner align-middle",
)
def body():
return dbc.Row(
[
dbc.Container(
[
dbc.Card(
[
dbc.CardHeader(
dbc.Tabs(
[
dbc.Tab(
label=" Home",
tab_id="home-tab",
labelClassName=icon_home,
),
],
id=get_id("base", "home-tabs"),
active_tab="home-tab",
)
),
dbc.CardBody(html.P(id="main-content", className="tab-body border")),
]
)
],
id="tab-container",
fluid=True,
)
]
)
def layout():
return dbc.Container(
[
banner(),
body(),
],
id="home-layout",
fluid=True,
)
@callback(
Output('main-content', 'children'),
Input(get_id("base", "home-tabs"), 'active_tab')
)
def display_main_content(active_tab):
match active_tab:
case "home-tab":
return home_tab.layout()
And here is the code of the forms and the callback that I am planning to use to add the new tab in.
def add_card(title, text, icon):
# Fix up the title for display
title_fmt = re.sub('-', ' ', title.strip()).title()
modal = html.Div(
[
dbc.Modal(
[
dbc.ModalHeader(dbc.ModalTitle(title_fmt)),
dbc.ModalBody(id=get_id("modal-body", title)),
dbc.ModalFooter(
dbc.Button(
"Load",
id=get_id("modal-btn-load", title),
n_clicks=0
)
),
],
id=get_id("modal-top", title),
is_open=False,
backdrop='static',
keyboard=False,
scrollable=True,
centered=True,
size="lg",
),
]
)
return dbc.Col(
[
dbc.Card(
[
dbc.CardHeader(html.H5(title_fmt)),
dbc.CardBody(
[
html.P(text, className="card-text"),
# The button id is base on the title string
dbc.Button(class_name=icon, id=get_id("modal-btn-open", title)),
modal,
]
)
],
),
],
width=3,
)
def layout_home_settings():
return html.P("Settings not populated yet")
def layout_load_testbench():
run_path = dbc.Row(
[
dbc.Label("Run Path"),
dbc.Input(
id=get_id("run-path", "load-testbench"),
placeholder="/home/",
),
dbc.FormText(
"Path to the testbench's run/ folder",
color="secondary",
),
dbc.FormFeedback("Given path doesn't exist", type="invalid"),
],
class_name='g-1 mb-3',
)
dash_name = dbc.Row(
[
dbc.Label("Testbench Alias"),
dbc.Input(
id=get_id("alias", "load-testbench"),
placeholder=randomname.get_name(),
),
dbc.FormText("A helpful nick name for the TB in the RunDao dashboard"),
],
class_name='g-1 mb-3',
)
focus_tb_tab = dbc.Row(
[
dbc.Switch(
id="load-testbench-focus-tab",
label="Bring the new testbench tab into active focus",
value=False,
),
],
class_name='g-1 mb-3',
)
return dbc.Container(
[
run_path,
dash_name,
# focus_tb_tab,
],
)
def layout():
return dbc.Container(
[
dbc.Row(
[
dbc.CardGroup(
[
add_card("settings",
"Adjust app wide preferences",
icon_settings
),
add_card("load-testbench",
"Import existing testbench into the dashboard",
icon_import
),
],
),
],
id="home-tab-row",
align='center',
justify='start',
class_name='g-1',
),
],
id="home-tab-container",
fluid=True,
)
@callback(
Output({'type': 'modal-top', 'index': MATCH}, 'is_open'),
Input({'type': 'modal-btn-open', 'index': MATCH}, 'n_clicks'),
Input({'type': 'modal-btn-load', 'index': MATCH}, 'n_clicks'),
State({'type': 'modal-top', 'index': MATCH}, 'is_open'),
)
def toggle_modal(open, load, is_open):
if open or load:
return not is_open
return is_open
@callback(
Output({'type': 'modal-body', 'index': MATCH}, 'children'),
Input({'type': 'modal-top', 'index': MATCH}, 'is_open'),
State({'type': 'modal-top', 'index': MATCH}, 'id'),
)
def modal_body(is_open, id):
if is_open:
# remove the component id suffix
match id['index']:
case "settings":
return layout_home_settings()
case "load-testbench":
return layout_load_testbench()
# Feedback about whether the given path is accessible or not
@callback(
Output(get_id("run-path", "load-testbench"), 'valid'),
Output(get_id("run-path", "load-testbench"), 'invalid'),
Input(get_id("run-path", "load-testbench"), 'value'),
)
def check_path_exists(path):
if path:
valid = os.path.isdir(path)
return valid, not valid
return False, False
@callback(
Output(get_id("base", "home-tabs"), 'children'),
Input(get_id('modal-btn-load', 'load-testbench'), 'n_clicks'),
State(get_id('modal-btn-load', 'load-testbench'), 'id'),
State(get_id("run-path", "load-testbench"), 'valid'),
State(get_id("run-path", "load-testbench"), 'value'),
State(get_id("alias", "load-testbench"), 'value'),
prevent_initial_call=True,
)
def add_new_home_tab(clicked, id, run_path_valid, run_path, alias):
print(f"{clicked=} {id=} {run_path_valid=} {run_path=} {alias=}")
ctx = callback_context
if not ctx.triggered:
raise PreventUpdate
print(f"{ctx.__dir__()}")
raise PreventUpdate
where
def get_id(type, index):
return {
'type': type,
'index': index
}
As you can see, I am getting the exception for the missing elements from the ‘load-testbench’ arguments.
Can you please help how to avoid this exception?
Or refactor the structure to achieve my objective?
Thank you!