Persistence problem for dependent dropdowns in Plotly Dash - python

I have two dependent dropdowns that I want to persist in user session. I noticed that the persistence doesn't work for the second dropdown. It get reset with no possible value.
Here is a code sample :
from dash import Dash, dcc, html
from dash.dependencies import Input, Output
app = Dash(
prevent_initial_callbacks=True,
suppress_callback_exceptions=True,
)
#app.callback(
Output("dd-client-code", "options"),
Input("dd-clients-seg-1", "value")
)
def dd_client_code(client_seg_1):
#any function would do for generate_options
return generate_options(selected_segment=client_seg_1)
dd1 = dcc.Dropdown(
id="dd-clients-seg-1",
options=["record_1", "record_2", "record_3"],
persistence="true",
persistence_type="session",
)
dd2 = dcc.Dropdown(
id="dd-client-code",
persistence="true",
persistence_type="session",
)
app.layout = html.Div(children=[dd1, dd2])
app.run_server(debug=True)
Can anyone help me ?

The persistent values for both dropdowns are stored as expected...
but the value for dd2 is updated to null when the page is reloaded because dd2 has no options at that time.
The callback to update dd2 is not called when the page is reloaded. Even if the callback was called it would be too late because of the first issue.
The following modified code uses dcc.Store to store the value of dd1 each time it is changed. dcc.Interval is used to ensure that the callback is called after the page reload. dd2 is turned into a function that takes the value of dd1 and is called by a new callback that triggers on the interval to update the layout.
import dash.exceptions
from dash import Dash, dcc, html
from dash.dependencies import Input, Output, State
app = Dash(
prevent_initial_callbacks=True,
suppress_callback_exceptions=True,
)
def generate_options(selected_segment):
if selected_segment == "record_1":
return ["A", "B", "C"]
elif selected_segment == "record_2":
return ["One", "Two", "Three"]
else:
return ["Small", "Medium", "Large"]
dd1 = dcc.Dropdown(
id="dd-clients-seg-1",
options=["record_1", "record_2", "record_3"],
persistence="true",
persistence_type="session",
)
def dd2(dd1_value):
"""Return a dd2 dropdown with the appropriate options based on dd1 value"""
options = [] if not dd1_value else generate_options(dd1_value)
return dcc.Dropdown(
id="dd-client-code",
options=options,
persistence="true",
persistence_type="session",
)
#app.callback(
Output("dd-client-code", "options"),
# Store the value of dd1 dropdown when it changes
Output("dd-clients-seg-1-value", "data"),
Input("dd-clients-seg-1", "value")
)
def dd_client_code(client_seg_1):
if not client_seg_1:
raise dash.exceptions.PreventUpdate
return generate_options(client_seg_1), client_seg_1
#app.callback(
Output("dd2-div", "children"),
Input("interval-timer", "n_intervals"),
State("dd-clients-seg-1-value", "data"),
)
def dd2_div_handler(unused, dd1_value):
"""Update the dd2 menu when triggered by dcc.Interval"""
return dd2(dd1_value)
app.layout = html.Div([
# store the latest value of dd-clients-seg-1 dropdown
dcc.Store("dd-clients-seg-1-value", storage_type="session"),
# fires 1ms after page load
dcc.Interval(id="interval-timer", interval=1, max_intervals=1),
# static menu
dd1,
# dynamic menu: don't put dd2 here to avoid persistent value going null
html.Div(id="dd2-div")
])
app.run_server(debug=True)
This was tested with Dash 2.4.1

Related

plotly dash chained callback

This is my code
app = Dash(__name__)
app.layout = html.Div([
dcc.Location(id='url', refresh=False),
dcc.Dropdown(options=['bar', 'pie'], id='dropdown', multi=False, value='bar', placeholder='Select graph type'),
html.Div(id='page-content'),
])
#app.callback(
Output('see1', 'options'),
Input('url', 'search')
)
def ex_projecKey(search):
return re.search('project_key=(.*)', search).group(1)
#app.callback(
Output('page-content', 'children'),
Input('see1', 'options'),
Input('dropdown', 'value')
)
def update_page(options, value):
return f'{options}.{value}'
if __name__ == '__main__':
app.run_server(debug=True, port=4444)
I receive something URL and search project_key
after choice dropdown menu like bar or pie
Then i receive two object (project_key, graph_type )
But two error occur
Property "options" was used with component ID:
"see1"
in one of the Input items of a callback.
This ID is assigned to a dash_html_components.Div component
in the layout, which does not support this property.
This ID was used in the callback(s) for Output(s):
page-content.children
Property "options" was used with component ID:
"see1"
in one of the Output items of a callback.
This ID is assigned to a dash_html_components.Div component
in the layout, which does not support this property.
This ID was used in the callback(s) for Output(s):
see1.options
first callback Output see1, options
Than second callback Input receive that options inst it?
You get an error because you did not define see1 in the layout. I don't know which type of object you want see1 to be, but I think if it is juste to pass data bewteen callback, you should use dcc.Store. So you would call it with Output('see1', 'data') instead of Output('see1', 'options').
Full code :
from dash import dcc, html, Dash, Input, Output
import re
app = Dash(__name__)
app.layout = html.Div([
dcc.Location(id='url', refresh=False),
dcc.Dropdown(options=['bar', 'pie'], id='dropdown', multi=False, value='bar', placeholder='Select graph type'),
html.Div(id='page-content'),
dcc.Store(id="see1", storage_type='memory'), # define see1 in layout as dcc.Store component
])
#app.callback(
Output('see1', 'data'), # use 'data' property
Input('url', 'search')
)
def ex_projecKey(search):
return re.search('project_key=(.*)', search).group(1)
#app.callback(
Output('page-content', 'children'),
Input('see1', 'data'),
Input('dropdown', 'value')
)
def update_page(options, value):
return f'{options}.{value}'
if __name__ == '__main__':
app.run_server(debug=True, port=4444)

dash DataTable reset on page reload

The following code reads a simple model (3 columns 50 rows) from a CSV file for editing in a table in my (larger) dash app. Editing a cell writes the whole table back to file as expected. However, reloading the page displays the table as it was originally loaded from file, thus losing any edits. Any clues about how to keep the edits between page reloads?
df_topic_list=pd.read_csv(model_file)
app.layout = html.Div([
dcc.Store(id='memory-output'),
html.Div([
dash_table.DataTable(df_topic_list.to_dict('records'),
id='memory-table',
columns=[{"name": i, "id": i} for i in df_topic_list.columns],editable=True
),
])
])
#app.callback(Output('memory-output', 'data'),
Input('memory-table', 'data'))
def on_data_set_table(data):
pd.DataFrame(data).to_csv(model_file,index=False)
return data
app.run_server(port=8052)
When you refresh page then it doesn't run all code again but it only sends again app.layout which it generated only once and which has original data from file. And when you update data in cell in table then it updates only values in browser (using JavaScript) but not in code app.layout.
But it has options to presist values in browser memory and it should use these values after reloading.
app.layout = html.Div([
dcc.Store(id='memory-output'),
html.Div([
dash_table.DataTable(
df_topic_list.to_dict('records'),
id='memory-table',
columns=[{"name": i, "id": i} for i in df_topic_list.columns],
editable=True,
persistence=True, # <---
persisted_props=["data"], # <---
)
])
])
It works for me but it seems some people had problem with this.
See issues: Dash table edited data not persisting · Issue #684 · plotly/dash-table
But I found other method to keep it.
I assign table to separated variable - ie. table - and in callback I replace table.data in this table.
from dash import Dash, Input, Output, callback
from dash import dcc, html, dash_table
import pandas as pd
model_file = 'data.csv'
df_topic_list = pd.read_csv(model_file)
app = Dash(__name__)
table = dash_table.DataTable(
df_topic_list.to_dict('records'),
id='memory-table',
columns=[{"name": i, "id": i} for i in df_topic_list.columns],
editable=True,
)
app.layout = html.Div([
dcc.Store(id='memory-output'),
html.Div([table])
])
#app.callback(
Output('memory-output', 'data'),
Input('memory-table', 'data')
)
def on_data_set_table(data):
pd.DataFrame(data).to_csv(model_file, index=False)
table.data = data # <--- replace data
return data
app.run_server(port=8052)

Passing a value from a page to another in dash plotly

I have two pages in dash plotly, and an index file who run the pages :
Index.py
Page 1
Page 2
I have a variable in page 1 which I want to pass it in page 2 and work with it. ( example of code)
Is it possible in Dash plotly ?
I saw somewhere that I can use Dcc.store, but how can I stock this variable in it ?
First_page = html.Div([
html.Div([dcc.Input(id='date_start', type='text', value='2021')]),
...
...
])
#app.callback(
Output('someoutput', 'children'),
[Input('someinput', 'value')])
#Uploading a certain table
#app.callback(
Output('datatable', 'data'),
Input('someinput', 'value'))
def function(somedata):
#Get the VARIABLE
...
...
variable= 12 (result of the operation in funtion)
How can I get the VARIABLE value (12) and inject it in page 2 please ?
Second_page = html.Div([
html.Div([dcc.Input(id='', type='text', value='')]),
...
...
])
#app.callback ()
def function_page2(somedata_page2):
#Work with the VARIABLE in first page
...
Thank you .

How to keep the dropdown value after button click in Dash?

I have an application in which I want to update the dropdown values when the user inputs some text into the dropdown. So far, I managed to get the text from an Input text that the user inputs, but not from the dropdown. The dropdown keeps reseting after clicking in any part of the screen or on the button.
import dash
import dash_html_components as html
import dash_core_components as dcc
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
app = dash.Dash(
external_stylesheets=[
dbc.themes.BOOTSTRAP])
app.layout = html.Div(
[
dcc.Input(id='helper'),
dcc.Dropdown(id="my-dynamic-dropdown", options=[]),
html.Br(),
html.Br(),
html.Br(),
html.Br(),
html.Br(),
html.Br(),
html.Br(),
dbc.Button('Add to Dropdown', id='dropdown_button', n_clicks=0, color="info",
className="mr-1")
],
)
options = []
#app.callback(
Output("my-dynamic-dropdown", "options"),
Input('dropdown_button', 'n_clicks'),
[State('my-dynamic-dropdown', 'options'),
# State('helper', 'value')
State('my-dynamic-dropdown', 'search_value')
]
)
def update_options(n_clicks,existing_options,helper_value):
if helper_value is None:
print("none value")
return options
else:
print("helper value ",helper_value)
options.append({'label': helper_value, 'value': helper_value})
return options
if __name__ == "__main__":
app.run_server(debug=True)
The dropdown allows a user to search through the existing options, but what you're trying to do is not what the dropdown was designed for. You would either need to write a custom component, use some combination of dropdown and input components, or find a different solution to whatever problem you're trying to solve.

Plotly: Dash button with live graph not working

i developed python dash based application for monitoring. as a part of the project i want to display live graph and change the live graph value based on user input. i am stuck in this part. the live graph getting update when i start typing in input box (eg:temp) the graph keep on updating for each letter like t,te,tem,temp. so i created a button to submit the input value. still the graph updating for each letter.
the code for the same:
app = dash.Dash(__name__)
app.layout = html.Div(
[
dcc.Input(id='input-value', value='example', type='text'),
html.Button('Submit', id="submit-button"),
dcc.Graph(id='live-graph', animate=False),
dcc.Interval(
id='graph-update',
interval=1*1000
),
]
)
call back function is like below
#app.callback(Output('live-graph', 'figure'),
[Input('submit-button','n_clicks')],
state=[State(component_id='sentiment_term', component_property='value')],
events=[Event('graph-update', 'interval')])
def update_graph_scatter(n_clicks, input_value):
conn = sqlite3.connect('database.db')
c = conn.cursor()
df = pd.read_sql("SELECT * FROM table WHERE colume LIKE ? ORDER BY unix DESC LIMIT 1000", conn ,params=('%' + input_value+ '%',))
df.sort_values('unix', inplace=True)
df['date'] = pd.to_datetime(df['unix'],unit='ms')
df.set_index('date', inplace=True)
X = df.index
Y = df.column
data = plotly.graph_objs.Scatter(
x=X,
y=Y,
name='Scatter',
mode= 'lines+markers'
)
return {'data': [data],'layout' : go.Layout(xaxis=dict(range= [min(X),max(X)]),
yaxis=dict(range=[min(Y),max(Y)])}
Note: If i removed the interval the button start working.but i want to be live update as well as button
You could use an intermediate component like a div to save the input from the text field and update that div only on button click.
So you would have
app = dash.Dash(__name__)
app.layout = html.Div([
dcc.Input(id='input-value', value='example', type='text'),
html.Div(['example'], id='input-div', style={'display': 'none'}),
html.Button('Submit', id="submit-button"),
dcc.Graph(id='live-graph', animate=False),
dcc.Interval(
id='graph-update',
interval=1*1000
),
])
Now you update the div only on button click:
#app.callback(Output('input-div', 'children'),
[Input('submit-button', 'n_clicks')],
state=[State(component_id='input-value', component_property='value')])
def update_div(n_clicks, input_value):
return input_value
And the Graph always uses the the div content to query your database (either when the interval triggers or the div changes):
#app.callback(Output('live-graph', 'figure'),
[Input('graph-update', 'interval'),
Input('input-div', 'children')])
def update_graph_scatter(n, input_value):
...
Are you sure the updates on each letter are because of the input? Sounds like the interval updates the graph with the not finished input you are typing.
P.S.: I guess you have a name discrepancy between you Input and State id.

Categories

Resources