I am creating a subplot of a maximum of 4 graphs. They are vertically stacked. I would like the user to have the ability to manually change the yaxis ranges of the individual graphs. Here is a callback of the figure creation and then I was making a separate callback to update the graph but I get an error this way.
Any thoughts of how to do this easier or how to change a part of a graph in a callback?
layout = dbc.Container([
dbc.Row([
dbc.Button(
'Create a View',
id='diag-view-btn',
n_clicks=0,
style={'width': '125px'},
color='secondary'
),
dbc.Modal([
dbc.ModalHeader(dbc.ModalTitle('Settings'), close_button=True),
dbc.ModalBody([
html.Div([
html.Center([
html.H5('Select a Plant'),
dcc.Dropdown(
id='diag-plant-drop',
options=np.sort(all_with_areas_names_df.Area_0.unique()),
placeholder='Plant',
style={'width': '300px', 'text-align': 'left'},
clearable=True,
)
],
style={'margin-top': '10px', 'margin-bottom': '25px'}
),
]),
html.Div([
dbc.Row([
html.Center([
html.H5('Select up to 4 points to display'),
],
style={'margin-bottom': '10px'}
)
]),
dbc.Row([
dbc.Input(
id='search-points',
placeholder='Search',
type='text',
style={'width': '30%', 'margin-left': '5%'}
),
],
style={'margin-bottom': '10px'}
),
dbc.Row([
dbc.Col([
dash_table.DataTable(
id={
'type': 'table',
'index': 'modal-point-table-left'
},
data=[],
columns=[{'id': 'points', 'name': 'Points'}],
style_cell={'text-align': 'left',
'border': '1px solid rgb(200,200,200)',
'maxHeight': '8px', 'padding-left': '5px',
'font-family': 'Arial'},
style_table={'height': '400px', 'overflowY': 'auto',
'border': '1px solid grey'},
style_data={
'font-size': '12px',
'whiteSpace': 'normal',
'height': '10px',
'lineHeight': '10px',
'color': 'black',
'backgroundColor': 'white',
},
)
],
width=5
),
dbc.Col([
html.Div([
dbc.Button([
html.I(
className="fa-solid fa-arrow-right",
style={'font-size': '24px'}
),
],
color='secondary',
outline=True,
style={'width': '70px'},
id='add-modal-select',
disabled=False,
),
dbc.Button([
html.I(
className="fa-solid fa-arrow-left",
style={'font-size': '24px'}
),
],
color='secondary',
outline=True,
style={'width': '70px'},
id='remove-modal-select',
disabled=False
)
],
style={'display': 'flex', 'flex-direction': 'column',
'gap': '100px', 'justify-content': 'space-around',
'align-items': 'center', 'height': '400px'}
)
],
width=2
),
dbc.Col([
dash_table.DataTable(
id={
'type': 'table',
'index': 'modal-point-table-right'
},
data=[],
columns=[{'id': 'Selections', 'name': 'Selections'}],
style_cell={'text-align': 'left',
'border': '1px solid rgb(200,200,200)',
'maxHeight': '8px', 'padding-left': '5px',
'font-family': 'Arial'},
style_table={'height': '400px', 'border': '1px solid grey'},
style_data={
'font-size': '12px',
'whiteSpace': 'normal',
'height': '10px',
'lineHeight': '10px',
'color': 'black',
'backgroundColor': 'white',
},
)
],
width=5
),
]),
dbc.Row([
html.Div(
html.Center(
html.H5('Select a Date Range')
)
),
html.Div([
dmc.DatePicker(
id='modal-start-date',
maxDate=dt.today(),
style={'width': '200px'},
clearable=True,
inputFormat='MM-DD-YYYY'
),
html.P('to'),
dmc.DatePicker(
id='modal-end-date',
maxDate=dt.today(),
style={'width': '200px'},
clearable=True,
inputFormat='MM-DD-YYYY',
value=dt.today()
)
],
style={'display': 'flex', 'justify-content': 'center',
'gap': '20px', 'align-items': 'baseline'}
)
],
style={'margin-top': '35px'}
)
],
id='wizard-div',
)
],
style={'height': '750px'}
),
dbc.ModalFooter(
dbc.Button(
'Create View',
id='modal-create-btn',
n_clicks=0,
style={'width': '125px'},
color='secondary'
)
)
],
id='view-modal',
centered=True,
is_open=False,
size='xl',
),
dbc.Button(
'Save as HTML Plot',
id='subplot-html-save',
n_clicks=0,
style={'display': 'none'},
color='success'
)
],
style={'margin-top': '15px', 'display': 'flex', 'justify-content': 'space-between'}
),
dbc.Row([
dbc.Col([
html.Div([
dcc.Graph(
figure={},
id='subplot',
style={'display': 'none'}
)
],
id='graph-container',
style={'height': '700px'}
)
],
width=10
),
dbc.Col([
html.Div(
id='y-axis-col',
children=[],
style={'height': '700px'}
)
],
width=2
),
],
style={'display': 'flex'}
)
],
fluid=True,
)
@app.callback(
Output('subplot', 'figure'),
Output('y-axis-col', 'children'),
Output('subplot', 'style'),
Output('subplot-html-save', 'style'),
Input('modal-create-btn', 'n_clicks'),
State('diag-plant-drop', 'value'),
State({'type': 'table', 'index': 'modal-point-table-right'}, 'data'),
State('modal-start-date', 'value'),
State('modal-end-date', 'value'),
Input('diag-view-btn', 'n_clicks'),
State('store', 'data'),
State('y-axis-col', 'children'),
prevent_initial_call=True
)
def create_subplot(n1, plant, data, start, end, n2, main_data, yaxis_col):
all_df = pd.DataFrame(main_data['df'])
def create_y_axis_range_change(n, total):
div = html.Div([
html.Div([
html.Center(html.P('Change Y-Axis Range'))
]),
html.Div([
dbc.Input(
id=f'yaxis-min-{n}',
type='number',
style={'width': '80px'},
placeholder='Min'
),
dbc.Input(
id=f'yaxis-max-{n}',
type='number',
style={'width': '80px'},
placeholder='Max'
)
],
style={'display': 'flex', 'justify-content': 'center', 'gap': '15px'}
)
],
style={'display': 'flex', 'flex-direction': 'column', 'justify-content': 'center', 'height': f'{100/total}%'}
)
return div
i = 0
if ctx.triggered_id == 'modal-create-btn':
titles = []
j = 0
while j < len(data):
titles.append(f'{plant} - {data[j]["Selections"]}')
j += 1
fig = make_subplots(rows=len(data), cols=1, subplot_titles=titles, shared_xaxes=True, vertical_spacing=0.05)
while i < len(data):
mask = (all_df['Area_0'] == plant) & (all_df['Short_Description'] == data[i]['Selections'])
spec_df = all_df[mask]
query_str = spec_df['Query_Filter_str'].iloc[0]
graph_data_df = rk.get_rounds_data(query_str)
date_mask = (graph_data_df['Timestamp'] >= start) & (graph_data_df['Timestamp'] <= end)
date_cut_df = graph_data_df[date_mask]
fig.add_trace(go.Scatter(
x=date_cut_df['Timestamp'],
y=date_cut_df['Value'],
mode='lines+markers'
), row=(i + 1), col=1)
l1_min = spec_df['L1_Min'].iloc[0]
l1_max = spec_df['L1_Max'].iloc[0]
l2_min = spec_df['L2_Min'].iloc[0]
l2_max = spec_df['L2_Max'].iloc[0]
norm_min = spec_df['Norm_Min'].iloc[0]
norm_max = spec_df['Norm_Max'].iloc[0]
y = date_cut_df['Value']
if l2_max > y.max():
l2_max = y.max()
if l1_max > y.max():
l1_max = y.max()
if norm_max > y.max():
norm_max = y.max()
if l2_min < y.min():
l2_min = y.min()
if l1_min < y.min():
l1_min = y.min()
if norm_min < y.min():
norm_min = y.min()
if not math.isnan(l2_min) and not math.isnan(l2_max):
fig.add_hrect(y0=y.min(), y1=y.max(), fillcolor='#ffcccc', opacity=1, line_width=0, layer='below',
row=(i + 1), col=1)
if math.isnan(norm_min) and math.isnan(norm_max):
norm_min = 0
norm_max = 0
if math.isnan(l1_min) and math.isnan(l1_max):
l1_min = 0
l1_max = 0
if math.isnan(l2_min) and math.isnan(l2_max):
l2_min = 0
l2_max = 0
fig.add_hrect(y0=l2_min, y1=l2_max, fillcolor='#ffe1c2', opacity=1, line_width=0, layer='below',
row=(i + 1), col=1)
fig.add_hrect(y0=l1_min, y1=l1_max, fillcolor='#fffdb8', opacity=1, line_width=0, layer='below',
row=(i + 1), col=1)
fig.add_hrect(y0=norm_min, y1=norm_max, fillcolor='#ccffc2', opacity=1, line_width=0, layer='below',
row=(i + 1), col=1)
fig.update_yaxes(title=f"<span style='font-size: 12px'>{spec_df['Unit'].iloc[0]}</span>", row=(i + 1), col=1)
yaxis_col.append(create_y_axis_range_change((i + 1), len(data)))
i += 1
fig.update_xaxes(range=[start, end])
fig.update_layout(template='seaborn', height=700, showlegend=False,
margin=dict(autoexpand=False,
b=30,
r=30,
l=50,
t=30),
xaxis_rangeslider_visible=False
)
return fig, yaxis_col, {'display': 'flex'}, {'width': '175px', 'margin-right': '2%'}
return {}, [], {'display': 'none'}, {'display': 'none'}
# @app.callback(
#
# )
@app.callback(
Output('subplot', 'figure'),
Input('yaxis-min-1', 'value'),
Input('yaxis-max-1', 'value'),
Input('yaxis-min-2', 'value'),
Input('yaxis-max-2', 'value'),
Input('yaxis-min-3', 'value'),
Input('yaxis-max-3', 'value'),
Input('yaxis-min-4', 'value'),
Input('yaxis-max-4', 'value'),
State('subplot', 'figure'),
prevent_initial_call=True
)
def update_yaxes(ymin1, ymax1, ymin2, ymax2, ymin3, ymax3, ymin4, ymax4, fig):
if ymin1 and ymax1:
fig.update_yaxes(range=[ymin1, ymax1], row=1, col=1)
if ymin1 and not ymax1:
fig.update_yaxes(range=[ymin1, None], row=1, col=1)
if ymax1 and not ymin1:
fig.update_yaxes(range=[None, ymax1], row=1, col=1)
return fig