Hi,
I was solving another issue (thanks @pdh for the help) and we came across this strange thing with selectedData and clickData.
If I don’t have dropdowns in the dashboard, the events in the barchart work fine. However, if I have a dropdown, when I load the page and click in the graph it doesn’t record the clicked point. I need to first filter using the dropdown and from that moment it starts recording the clicks, it doesn’t matter if I clear the dropdown filter after, it keeps working after the first time. When I click it does go into the callback, but it just returns an empty object.
Here’s the items2.txt file and below is my code.
At the end of this post you can see the output in the different scenarios. It shows how clickData is empty until the dropdown is used for the first time.
Any idea on why this is happening?
Thanks!
import dash
from dash.dependencies import Input, Output
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd
df = pd.read_csv("item2.txt", sep=',')
cols = df.columns
cols = cols.map(lambda x: x.replace(' ', '_') if isinstance(x, (str, unicode)) else x)
df.columns = cols
app = dash.Dash()
app.css.append_css({'external_url': 'https://codepen.io/chriddyp/pen/bWLwgP.css'})
app.config['suppress_callback_exceptions']=False
app.layout = html.Div(
html.Div(
children=[
#First row
html.Div(
children=[
# Location Dropdown
html.Div(
children=[
html.Label('Location'),
dcc.Dropdown(id='location',
options=[{'label': i, 'value': i} for i in df.Location.unique()],
)
], className="one columns"),
# Site Dropdown
html.Div(
children=[
html.Label('Site'),
dcc.Dropdown(id='site',
options=[{'label': i, 'value': i} for i in df.Site.unique()],
)
], className="one columns"),
# Items
html.Div(
children=[
html.Label('Items'),
html.Div(id='items_container')
#df.shape[0])
], className="one columns"),
# Quantity
html.Div(
children=[
html.Label('Quantity'),
html.Div(id='quantity_container')
], className="one columns"),
html.Div(
children=[
html.Label('Placeholder'),
html.Div(id='placeholder')
], className="eight columns", style={'width':'100%'}),
]),
html.Div(
children=[
html.Div(
children=[
# BarChart with stock by type
html.Div(id='bar_current_stock_by_type_container',
children=[dcc.Graph(id='bar_stock_by_type', clickData={'points': []}
)]
),
], className="four columns"),
]),
html.Div(
children=[
html.Div(
children=[
# Table with data
html.Div(id='table-container')
], className="twelve columns"),
])
])
)
def get_filtered_df(location, site, clickData):
if location is None and site is None:
dff = df
elif site is None:
dff = df[df.Location == location]
elif location is None:
dff = df[df.Site == site]
else:
dff = df[(df.Location == location) & (df.Site == site)]
print (clickData["points"])
if clickData["points"] != []:
dataselected = clickData["points"][0]["y"]
print dataselected
for item in dff.Stock_Type:
if item == dataselected:
dff2 = dff[(dff.Stock_Type == dataselected)]
else:
dff2=dff
return dff2
def generate_table(df, max_rows=10):
return html.Table(
[html.Tr([html.Th(col) for col in df.columns])] +
[html.Tr([html.Td(df.iloc[i][col]) for col in df.columns])
for i in range(min(len(df), max_rows))], id='full_table'
)
def generate_bar_stock_by_type(df, byStockType):
x = byStockType.sum()['Inventory_Value']/1000
line = go.Scatter(
x=x,
y=x.index,
name='Inventory value (k)'
)
x = byStockType.count()['Current_Balance']
bar = go.Bar(
x=x,
y=x.index,
orientation='h',
name='Num Stock Units'
)
graph_stock = dcc.Graph(
id='bar_stock_by_type',
figure={'data': [bar,line]},
clickData={'points': [], 'range': None},
config={'displayModeBar': True},
style={}
)
return graph_stock
# Items Metric
@app.callback(
dash.dependencies.Output('items_container', 'children'),
[dash.dependencies.Input('location', 'value'),
dash.dependencies.Input('site', 'value'),
dash.dependencies.Input('bar_stock_by_type', 'clickData')
])
def display_metric_items(location, site, clickData):
dff = get_filtered_df(location, site, clickData)
return html.H3(dff.shape[0])
# Quantity Metric
@app.callback(
dash.dependencies.Output('quantity_container', 'children'),
[dash.dependencies.Input('location', 'value'),
dash.dependencies.Input('site', 'value'),
dash.dependencies.Input('bar_stock_by_type', 'clickData')
])
def display_metric_quantity(location, site, clickData):
dff = get_filtered_df(location, site, clickData)
return html.H3(dff['Current_Balance'].sum())
# Table
@app.callback(
dash.dependencies.Output('table-container', 'children'),
[dash.dependencies.Input('location', 'value'),
dash.dependencies.Input('site', 'value'),
dash.dependencies.Input('bar_stock_by_type', 'clickData')
])
def display_table(location, site, clickData):
dff = get_filtered_df(location, site, clickData)
return generate_table(dff)
# Horizontal Bar Chart
@app.callback(
dash.dependencies.Output('bar_current_stock_by_type_container', 'children'),
[dash.dependencies.Input('location', 'value'),
dash.dependencies.Input('site', 'value')])
def display_bar_current_stock_by_type(location, site):
dff = get_filtered_df(location, site, clickData={'points': [], 'range': None})
byStockType = dff.groupby('Stock_Type')
return generate_bar_stock_by_type(dff, byStockType)
if __name__ == '__main__':
app.run_server(debug=True)