I have a Plotly-Dash dashboard here that is updated using the variable inputs on the left-hand side, as well as a few "fixed" variables.
These fixed variables are shown in the dash_table.DataTable at the bottom of image.
Should the user choose to alter the value in the DataTable, I would like to update the callbacks that used these fixed values.
As of now, the callbacks use the the dropdown and numeric inputs as [Input(' ', ' ')]
and the fixed variables are stored as variables, and used in the relevant equations.
Is there a way to either:
use a callback to define these fixed variable values. And when the table is changed, so are these "fixed" variables,..
or, use a callback to search the table and if changes are made, update the fixed variable value(s).
A bit of a vague question I know, I've googled this just about every which way and most of the info has to do with filtering dataTables and displaying rows, less so selecting and storing cell values as variables.
All I really need is an example of taking a cell numeric value, using as a callback [Input()], and that callback using the input in a basic algebraic formula.
I have attached the code, you will see I've been tinkering with the first callback, the rest of the code works fine.
import dash
import dash_design_kit as ddk
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objects as go
import pandas as pd
import dash_daq as daq
import dash_table
from dash.dependencies import Input, Output
import math
import pandas as pd
import pathlib
from crunch_numbers import *
num_datacards = 5
# theme.js supplies some additional styling, generated using editor
# REQUIRED FOR DEPLOYMENT
app = dash.Dash(__name__, suppress_callback_exceptions=True) # keep suppress_ in production code
server = app.server # expose server variable for Procfile
app.layout = ddk.App(show_editor=True, children=[
ddk.Header([
ddk.Logo("assets/komatsuLogo.png",
style={
"height": "30px",
"margin-right":"0px",
"width": "auto",
},
),
ddk.Title('Drivetrain Selection'),
]), # end of ddk.Header
ddk.Block(width=20,children=[ # left-side (inputs)
ddk.Card([ # machine Configuration
ddk.CardHeader("Machine Configuration"),
html.Br(),
dcc.Dropdown(
id='Platform',
options=[
{'label': 'Badger', 'value': 'Badger'},
{'label': 'Weasel', 'value': 'Weasel'},
],
value='Badger',
clearable=False,
placeholder="Select Machine",
),
html.Br(),
dcc.Dropdown(
id='battery_size',
options=[
{'label': '5S1P ( 66kWh)', 'value': 66},
{'label': '5S2P (132kWh)', 'value': 132},
],
value=66,
clearable=False,
#placeholder="Battery Size (kWh)",
),
html.Br(),
dcc.Dropdown(
id='Motor Selection',
options=[
{'label': 'MD 2200', 'value': 'sumo_md_2200'},
],
value='sumo_md_2200',
clearable=False,
placeholder="Motor Selection",
),
]), # end of Machine Configuration
ddk.Card([ # "Inputs"
ddk.CardHeader("Inputs"),
daq.NumericInput(
id='ramp_angle',
label='% Grade',
labelPosition='top',
value=0,
min=0,
max=18,
size='auto',
),
html.Br(),
daq.NumericInput(
id='ground_speed',
label='Speed (kph)',
labelPosition='top',
value=0,
min=0,
max=15,
size='auto',
),
html.Br(),
daq.NumericInput(
id='parasitics',
label='Parasitic Loads (kw)',
labelPosition='top',
value=0,
min=0,
max=30,
size='auto',
),
#html.Br(),
]), # end of "Inputs"
]), # end of left-side
ddk.Block(width=80, children=[ # right side block
ddk.Card([ # datacards and plot
ddk.DataCard(
width=100/num_datacards, # num_datacards is defined at top of file
id='motor_speed',
value=0,
label="(RPM)",
),
ddk.DataCard(
width=100/num_datacards,
id='motor_torque',
value=0,
label="(NM)",
),
ddk.DataCard(
width=100/num_datacards,
id='traction_efficiency',
value=0,
label="(Tot. %)",
),
ddk.DataCard(
width=100/num_datacards,
id='total_power',
value=0,
label="(kW)",
),
ddk.DataCard(
width=100/num_datacards,
id='autonomy',
value=0,
label="(km)",
),
dcc.Graph(id='plot'),
]), # end datacards and plot
ddk.Card(width=100,children=[ # table card
ddk.CardHeader("Machine Characteristics"),
dcc.Markdown(
"""
Update values in the table to modify machine performance.
""",
style={'textAlign': 'justify'}
),
dash_table.DataTable(
id='machine_spec_table',
data=Badger.to_dict("rows"),
columns=[ # only 'Values' and 'Mechanical Efficiency' are editable!
{"name": i, "id": i,"editable":False,"selectable":True}
if i == "Description" or i == "Units"
else {"name": i, "id": i,"selectable":True}
for i in Badger.columns
],
style_as_list_view=True,
style_header={"fontWeight": "bold", "textTransform": "capitalize"},
style_data_conditional=[
{
"if": {"row_index": "even"},
"backgroundColor": "var(--report_background_page)",
}
],
editable=True,
),
]) # end of table card
]) # end of right side block
]) # end of ddk.App
"""
Example of how to manage column width, should the need arise
style_cell_conditional=[
{
'if': {'column_id': 'Units'},
'width': 25
} for c in ['Units']
],
"""
############################# TABLE CALLBACKS ##################################################
################################################################################################
def find_fixed_variables(dict_list,var):
return dict_list[]
############################# DRIVETRAIN SELECTION CALLBACKS ###################################
################################################################################################
#app.callback(
Output('motor_speed', 'value'),
[Input('ground_speed', 'value'),
Input('machine_spec_table','data')] # , Input('tire_rr', 'value'), Input('diff_ratio', 'value'), Input('transfer_ratio', 'value')
)
def update_output(ground_speed,dict_list): #tire_rr, diff_ratio, transfer_ratio
return math.floor((ground_speed*1000)/60/(2*math.pi*tire_rr)*diff_ratio*transfer_ratio)
#app.callback(
Output('total_power', 'value'),
[Input('ground_speed', 'value'),
Input('ramp_angle', 'value')] #, Input('parasitics', 'value')] # Input('GVW', 'value'), Input('RR', 'value'),, Input('traction_efficiency', 'value')
)
def update_output(ground_speed, ramp_angle): #, traction_efficiency
power = math.floor(((RR/100)*(ground_speed*0.278) * GVW * gravity_cnst * math.cos(math.atan(ramp_angle/100))
/ 0.9 / 1000)
+ ((ground_speed * 0.278) * GVW * gravity_cnst * math.sin(math.atan(ramp_angle / 100))
/ 0.9 / 1000)
)
if ground_speed == 0:
return 0
else:
return power
#app.callback(
Output('motor_torque', 'value'),
[Input('ground_speed', 'value'),
Input('motor_speed', 'value'),
Input('total_power', 'value'),]
)
def update_output(ground_speed, motor_speed, total_power):
if ground_speed == 0:
return 0
elif math.floor(9.5488*total_power*1000/motor_speed) < 50:
return 50
else:
return math.floor(9.5488*total_power*1000/motor_speed)
#app.callback(
Output('plot', 'figure'),
[Input('motor_speed', 'value'),
Input('motor_torque', 'value')] #Input('Motor Selection', 'value')
)
def update_output(motor_speed, motor_torque): # , Motor_Selection
fig = go.Figure(
layout=go.Layout(
# title="Motor Efficiency Plot",
# autosize=False,
# width=500,
paper_bgcolor="rgba(0,0,0,0)",
plot_bgcolor="rgba(0,0,0,0)",
yaxis=dict(title="Motor Torque (Nm)"),
xaxis=dict(title="Motor Speed (RPM)"),
)
)
fig.update_layout(legend=dict(
orientation="h",
yanchor="bottom",
y=1,
xanchor="left",
x=0
)
)
fig.add_trace(go.Scatter(
x=TM4_BoundaryCurve['Speed (rpm)'],
y=TM4_BoundaryCurve['Peak Torque (Nm)'],
name="Peak Torque",
)
)
fig.add_trace(go.Scatter(
x=TM4_BoundaryCurve['Speed (rpm)'],
y=TM4_BoundaryCurve['Continuous Torque (Nm)'],
name="Cont. Torque",
)
)
fig.add_trace(go.Contour(
z=[TM4_EfficiencyMap['0'], TM4_EfficiencyMap['280'], TM4_EfficiencyMap['420'], TM4_EfficiencyMap['560'],
TM4_EfficiencyMap['700'],
TM4_EfficiencyMap['840'], TM4_EfficiencyMap['980'], TM4_EfficiencyMap['1120'], TM4_EfficiencyMap['1260'],
TM4_EfficiencyMap['1400'],
TM4_EfficiencyMap['1540'], TM4_EfficiencyMap['1680'], TM4_EfficiencyMap['1820'], TM4_EfficiencyMap['1960'],
TM4_EfficiencyMap['2100'],
TM4_EfficiencyMap['2240'], TM4_EfficiencyMap['2380'], TM4_EfficiencyMap['2520'], TM4_EfficiencyMap['2660'],
TM4_EfficiencyMap['2800'],
TM4_EfficiencyMap['2940'], TM4_EfficiencyMap['3080'], TM4_EfficiencyMap['3220'], TM4_EfficiencyMap['3360'],
TM4_EfficiencyMap['3500'], ],
x=TM4_EfficiencyMap['Speed'],
y=TM4_EfficiencyMap['Torque'],
transpose=True,
colorscale='Blues',
ncontours=20,
opacity=0.5,
showscale=False,
contours=dict(
showlabels=True, # show labels on contours
labelfont=dict( # label font properties
size=12,
color='white',
)
)
)
)
fig.add_trace(go.Scatter(
x=[motor_speed],
y=[motor_torque],
name="Actual",
mode="markers",
marker=dict(size=20, color='black', symbol="x"),
)
)
return fig
#app.callback(
Output('autonomy', 'value'),
[Input('ground_speed', 'value'),
Input('total_power', 'value'),
Input('battery_size', 'value')]
)
def update_output(ground_speed, total_power, battery_size):
if ground_speed == 0 or total_power == 0:
return 0
else:
return round((battery_size * DOD / total_power) * ground_speed, 2)
#app.callback(
Output('traction_efficiency', 'value'),
[Input('motor_speed', 'value'),
Input('motor_torque', 'value')]
)
def update_output(motor_speed, motor_torque):
df = pd.DataFrame(TM4_EfficiencyMap)
if motor_speed <= 280:
speed = str(0)
torque = 50
else:
speed = str(int((round(motor_speed / 140, 0) / 2) * 280))
torque = round(motor_torque / 50, 0) * 50
z = sum(round(df.loc[df['Torque'] == torque, speed] / 100 * diff_eff * transfer_eff * driveshaft_mt * driveshaft_td, 2))
return z
################################# MANDATORY SERVER CODE ##################################
if __name__ == '__main__':
app.run_server(debug=True)
Alright so easy fix, nothing a few print() test statements can't fix lol.
Essentially depending on the type of callback input you use for Input('machine_spec_table','data')
Here I used 'data', there are plenty of others available and explained in documentation,
the input to the function is as follows:
your_var = [
{'Description': 'Gross Vehicle Weight', 'Values': 29500, 'Units': 'kg', 'Mechanical Efficiency': '-'},
{'Description': 'Weight Distribution', 'Values': '60/40', 'Units': '', 'Mechanical Efficiency': '-'},
{'Description': 'Tire Rolling Radius', 'Values': 0.589, 'Units': 'm', 'Mechanical Efficiency': '-'},
{'Description': 'Differential Ratio', 'Values': 20.65, 'Units': '', 'Mechanical Efficiency': 0.93},
{'Description': 'Transfer Case Ratio', 'Values': 2.48, 'Units': '', 'Mechanical Efficiency': 0.98},
{'Description': 'Rolling Resistance', 'Values': 0.02, 'Units': '', 'Mechanical Efficiency': '-'},
{'Description': 'Drive Shaft', 'Values': '-', 'Units': '', 'Mechanical Efficiency': 0.98}
]
A list of dictionaries! Easy to access, something along the lines of table[0]['Values] does just fine :) (0 being a the list index,'Values' the dictionary key).
Related
I am trying to use the hoverData of a plot with many traces to display a side table of values related to each trace. The main code runs as follows. (note this is not the full code, but i included the relevant info)
def plots(self,):
df_lists = self.df_lists
plots_names = ['weakness', 'std', 'std_average', 'std_weak', 'p_average', 'p_repitition_average', 'p_median','p_median_all', 'p_median_average','p_range', 'p_range_average']
colors = {'background': '#111111', 'text': '#7FDBFF'}
from dash import Dash, dcc, html, Input, Output, State
names = self.names
app = Dash()
app.layout = html.Div( children=[
html.H4('Dieharder Tests Plots'),
html.P('Chose Plot Type'),
dcc.RadioItems(plots_names, plots_names[0], id="plot-picker", ),
html.P('Test Description'),
dcc.Markdown(id='test-explain', link_target="_blank", ),
html.P("Filter by test:"),
dcc.Dropdown(names, names[0], id="test-picker", multi = True),
dcc.Graph(id="plot", style={'width':'75%', 'float': 'left','height': '70vh','display':'inline-block'}),
html.Div([dcc.Graph(id='hover-data', style ={'float':'right'})], style={'width':'20%', 'paddingTop':35}),
])
#app.callback(
Output("plot", "figure"),
[Input("plot-picker", "value"), Input("test-picker", "value")])
def update_bar_chart(plot_picker, picker_test):
i=0
if plot_picker == 'weakness':
data = []
for test in picker_test:
df = df_lists[test]
p_value = [x for x in df.columns if x.startswith('pva')]
n_rounds = len(p_value)
trace = go.Bar(x=df.test_name, y = df.weak_rate, name = '{}, #rounds: {}'.format(test,n_rounds))
data.append(trace)
layout = go.Layout(title = 'Fraction of weak and failed results per each Dieharder test')
fig = go.Figure(data, layout)
fig.update_yaxes(title_text='Failed/weak fractions')
fig.update_layout(legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01))
return fig
The hover data includes the number of the trace not its name, which i need to specify the df source of the data. I am using the following code to get the hover data to generate the table:
#app.callback(Output('hover-data', 'graph'),
[Input('plot', 'hoverData')] )
def hover_data(hoverData):
Die_test = hoverData['points'][0]['x']
curve_number = hoverData['points'][0]['curveNumber']
trace_name = app.layout['plot'].figure['data'][curve_number]['name']
df = df_lists[trace_name]
df = df[df['test_name'] == Die_test]
data = [go.Table(header=dict(values=['p_mean', 'p_median', 'range', 'std'], fill_color='paleturquoise', align='left'), cells=dict(values=[df['p_mean'], df['p_median'], df['range'], df['std']] ))]
fig = go.Figure(data,)
return fig
The problem it is not working. I am not seeing anything when i hover over the data. I am not sure where the problem is coming, but most probably from the trace_name variable as i am getting the error:
Callback error updating hover-data.graph
AttributeError: 'Graph' object has no attribute 'figure'.
I tried to include a [State('plot', 'figure')] in the input of the callback. and then use the .figure['data'][curve_number]['name'] directly (instead of using app.layout['plot'] first), but it also didn't work.
Any help is appreciated.
Thanks
I don't have your dataframe so I think you can refer my code to revise yours:
from dash import Dash, html, dcc, Input, Output
import pandas as pd
import plotly.express as px
import dash_bootstrap_components as dbc
app = Dash(__name__, external_stylesheets=[dbc.themes.LUX])
df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv')
app.layout = html.Div([
dbc.Row([
dbc.Col([
dcc.Dropdown(
df['Indicator Name'].unique(),
'Fertility rate, total (births per woman)',
id='crossfilter-xaxis-column',
),
dcc.RadioItems(
['Linear', 'Log'],
'Linear',
id='crossfilter-xaxis-type',
labelStyle={'display': 'inline-block', 'marginTop': '5px'}
)
], width={'size': 6, "offset": 0, 'order': 1}),
dbc.Col([
dcc.Dropdown(
df['Indicator Name'].unique(),
'Life expectancy at birth, total (years)',
id='crossfilter-yaxis-column'
),
dcc.RadioItems(
['Linear', 'Log'],
'Linear',
id='crossfilter-yaxis-type',
labelStyle={'display': 'inline-block', 'marginTop': '5px'}
)
], width={'size': 6, "offset": 0, 'order': 1})
], style={'padding': '10px 5px'}, className='p-2 align-items-center'),
dbc.Row([
dbc.Col([
dcc.Graph(
id='crossfilter-indicator-scatter',
hoverData={'points': [{'customdata': 'Japan'}]}
)
], width={'size': 6, "offset": 0, 'order': 1}),
dbc.Col([
dash_table.DataTable(id='table',
columns=[{"name": i, "id": i} for i in df.columns],
data=[],
style_table={'height': 550},
style_header={'backgroundColor': 'orange', 'padding': '10px', 'color': '#000000'},
style_cell={'textAlign': 'center', 'font_size': '12px',
'whiteSpace': 'normal', 'height': 'auto'},
editable=True, # allow editing of data inside all cells
filter_action="native", # allow filtering of data by user ('native') or not ('none)
sort_action="native", # enables data to be sorted per-column by user or not ('none')
sort_mode="single", # sort across 'multi' or 'single' columns
column_selectable="multi", # allow users to select 'multi' or 'single' columns
row_selectable="multi", # allow users to select 'multi' or 'single' rows
row_deletable=True, # choose if user can delete a row (True) or not (False)
selected_columns=[], # ids of columns that user selects
selected_rows=[], # indices of rows that user selects
page_action="native",
export_headers='display')
], width={'size': 6, "offset": 0, 'order': 1}),
], className='p-2 align-items-center'),
dbc.Row([
dbc.Col([
dcc.Slider(
df['Year'].min(),
df['Year'].max(),
step=None,
id='crossfilter-year--slider',
value=df['Year'].max(),
marks={str(year): str(year) for year in df['Year'].unique()}
)
], width={'size': 6, "offset": 0, 'order': 1})
], className='p-2 align-items-center')
])
#app.callback(
Output('crossfilter-indicator-scatter', 'figure'),
Input('crossfilter-xaxis-column', 'value'),
Input('crossfilter-yaxis-column', 'value'),
Input('crossfilter-xaxis-type', 'value'),
Input('crossfilter-yaxis-type', 'value'),
Input('crossfilter-year--slider', 'value'))
def update_graph(xaxis_column_name, yaxis_column_name,
xaxis_type, yaxis_type,
year_value):
dff = df[df['Year'] == year_value]
fig = px.scatter(x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'],
y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'],
hover_name=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'])
fig.update_traces(customdata=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'])
fig.update_xaxes(title=xaxis_column_name, type='linear' if xaxis_type == 'Linear' else 'log')
fig.update_yaxes(title=yaxis_column_name, type='linear' if yaxis_type == 'Linear' else 'log')
fig.update_layout(margin={'l': 40, 'b': 40, 't': 10, 'r': 0}, hovermode='closest')
return fig
#app.callback(
Output('table', 'data'),
Input('crossfilter-indicator-scatter', 'hoverData'),
Input('crossfilter-xaxis-column', 'value'),
Input('crossfilter-xaxis-type', 'value'))
def update_y_timeseries(hoverData, xaxis_column_name, axis_type):
country_name = hoverData['points'][0]['customdata']
dff = df[df['Country Name'] == country_name]
dff = dff[dff['Indicator Name'] == xaxis_column_name]
return dff.to_dict(orient='records')
if __name__ == '__main__':
app.run_server(debug=False, port=1414)
I think instead of return go.Table, you can use dash_table in your Div and then return filtered data frame. Hope this help.
I have the written code below. I have two dropdown menus that work based on chained callbacks. the first dropdown menu gets the datasets and reads the columns' names and updates the options in the second dropdown menu. Then, the parameters can be plotted on the chart.
my dataframes look like this:
df={'col1':[12,15,25,33,26,33,39,17,28,25],
'col2':[35,33,37,36,36,26,31,21,15,29],
'col3':['A','A','A','A','B','B','B','B','B','B'],
'col4':[1,2,3,4,5,6,7,8,9,10]
I want to highlight the chart background depending on the categories in col3. I don't understand why when I select the dataset from the first dropdown menu the background color for col3 appears on the chart (before selecting the parameters). I have used Prevent_initial_call = True, but the second callback still triggers.
import dash
from dash import Dash, html, dcc, Output, Input, State, MATCH, ALL
import plotly.express as px
import pandas as pd
import numpy as np
import dash_bootstrap_components as dbc
app = Dash(__name__)
app.layout = html.Div([
html.Div(children=[
html.Button('add Chart', id='add-chart', n_clicks=0)
]),
html.Div(id='container', children=[])
])
#app.callback(
Output('container', 'children'),
[Input('add-chart', 'n_clicks'),
Input({'type': 'remove-btn', 'index': ALL}, 'n_clicks')],
[State('container', 'children')],
prevent_initial_call=True
)
def display_graphs(n_clicks, n, div_children):
ctx = dash.callback_context
triggered_id = ctx.triggered[0]['prop_id'].split('.')[0]
elm_in_div = len(div_children)
if triggered_id == 'add-chart':
new_child = html.Div(
id={'type': 'div-num', 'index': elm_in_div},
style={'width': '25%',
'display': 'inline-block',
'outline': 'none',
'padding': 5},
children=[
dbc.Container([
dbc.Row([
dbc.Col([dcc.Dropdown(id={'type': 'dataset-choice', 'index': n_clicks},
options=['dataset1'],
clearable=True,
value=[]
)], width=6),
dbc.Col([dcc.Dropdown(id={'type': 'feature-choice', 'index': n_clicks},
options=[],
multi=True,
clearable=True,
value=[]
)], width=6)
]),
dbc.Row([
dbc.Col([dcc.Graph(id={'type': 'dynamic-graph','index': n_clicks},
figure={}
)])
]),
dbc.Row([
dbc.Col([html.Button("Remove", id={'type': 'remove-btn', 'index': elm_in_div})
])
]),
])
]
)
div_children.append(new_child)
return div_children
if triggered_id != 'add-chart':
for idx, val in enumerate(n):
if val is not None:
del div_children[idx]
return div_children
#app.callback(
Output({'type': 'feature-choice', 'index': MATCH}, 'options'),
[Input({'type': 'dataset-choice', 'index': MATCH}, 'value')],
prevent_initial_call=True
)
def set_dataset_options(chosen_dataset):
if chosen_dataset is None:
return dash.no_update
else:
path = 'C:/Users/pymnb/OneDrive/Desktop/test/'
df = pd.read_csv(path + chosen_dataset+'.csv')
features = df.columns.values[0:2]
return features
#app.callback(
Output({'type': 'dynamic-graph', 'index': MATCH}, 'figure'),
[Input({'type': 'dataset-choice', 'index': MATCH}, 'value'),
Input({'type': 'feature-choice', 'index': MATCH}, 'value')],
prevent_initial_call=True
)
def update_graph(chosen_dataset1, chosen_feature):
if chosen_feature is None:
return dash.no_update
if chosen_dataset1 is None:
return dash.no_update
path = 'C:/Users/pymnb/OneDrive/Desktop/test/'
df = pd.read_csv(path + chosen_dataset1+'.csv')
Xmin = df[chosen_feature].min().min()
print(Xmin)
Xmax = df[chosen_feature].max().max()
# to find the height of y-axis(col4)
col4_max = df['col4'].max()
col4_min = df['col4'].min()
fig1 = px.line(df, x=chosen_feature, y='col4')
fig1.update_layout({'height': 600,
'legend': {'title': '', 'x': 0, 'y': 1.06, 'orientation': 'h'},
'margin': {'l': 0, 'r': 20, 't': 50, 'b': 0},
'paper_bgcolor': 'black',
'plot_bgcolor': 'white',
}
)
fig1.update_yaxes(range=[col4_max, col4_min], showgrid=False)
fig1.update_xaxes(showgrid=False)
categ_col3 = df.col3.dropna().unique()
colors = ['#54FF9F', '#87CEFF']
for (i,j) in zip(categ_col3, colors):
index_min = df.loc[df.col3 == i].index[0]
index_max = df.loc[df.col3 == i].index[-1]
if index_min == 0:
cat_min = df['col4'][index_min]
else:
cat_min = df['col4'][index_min-1]
cat_max = df['col4'][index_max]
fig1.add_shape(type="rect", x0=Xmin, y0=cat_min, x1=Xmax, y1=cat_max,
fillcolor=j, layer='below', opacity=0.5,
)
return fig1
if __name__ == '__main__':
app.run_server(debug=True)
You can fix it by modifying your code to the following:
#app.callback(
Output({'type': 'dynamic-graph', 'index': MATCH}, 'figure'),
[Input({'type': 'dataset-choice', 'index': MATCH}, 'value'),
Input({'type': 'feature-choice', 'index': MATCH}, 'value')],
prevent_initial_call=True
)
def update_graph(chosen_dataset1, chosen_feature):
if (chosen_feature == []) or (chosen_dataset1 is None): #<--- correct the condition
return dash.no_update
else: #<---- add the else condition to prevent any update
Xmin = df[chosen_feature].min().min()
Xmax = df[chosen_feature].max().max()
The reason behind that because all the elements are created on fly and they are not within the app.layout. Please read the following from the documentation:
In other words, if the output of the callback is already present in
the app layout before its input is inserted into the layout,
prevent_initial_call will not prevent its execution when the input is
first inserted into the layout.
I’m trying to return name of points when clicking on Scatter Plot points but it just return one. How can I do to return multiple value when clicking multiple points. Below is my sample code:
from dash import Dash, html, dcc, Input, Output
import pandas as pd
import plotly.express as px
PCA_table=pd.read_excel('https://github.com/hoatranobita/Jobs/blob/main/PCA_table.xlsx?raw=true')
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = Dash(__name__, external_stylesheets=external_stylesheets)
df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv')
app.layout = html.Div([
html.Div([
html.Div([
dcc.Dropdown(
options=PCA_table.columns.unique(),
value='PC1',
id='crossfilter-xaxis-column',
)],style={'width': '49%', 'display': 'inline-block'}),
html.Div([
dcc.Dropdown(
options=PCA_table.columns.unique(),
value='PC2',
id='crossfilter-yaxis-column'
)], style={'width': '49%', 'float': 'right', 'display': 'inline-block'})
], style={'padding': '10px 5px'}),
html.Div([
dcc.Graph(
id='crossfilter-indicator-scatter',
clickData={'points': [{'hovertext': '184A1'}]}
)], style={'width': '49%', 'display': 'inline-block', 'padding': '0 20'}),
html.Div([
html.Div(id='x-time-series'),
], style={'display': 'inline-block', 'width': '49%'}),
])
#app.callback(
Output('crossfilter-indicator-scatter', 'figure'),
Input('crossfilter-xaxis-column', 'value'),
Input('crossfilter-yaxis-column', 'value'))
def update_graph(xaxis_column_name, yaxis_column_name):
fig = px.scatter(PCA_table,x=PCA_table[xaxis_column_name],
y=PCA_table[yaxis_column_name],
color=PCA_table['Labels'],
labels=PCA_table['Columns'],
hover_name=PCA_table['Columns'])
fig.update_layout(clickmode='event')
fig.update_layout(margin={'l': 40, 'b': 40, 't': 10, 'r': 0}, hovermode='closest')
return fig
#app.callback(
Output('x-time-series', 'children'),
Input('crossfilter-indicator-scatter', 'clickData'))
def update_y_timeseries(clickData):
click_name = clickData['points'][0]['hovertext']
return html.Span(click_name)
if __name__ == '__main__':
app.run_server(debug=False,port=1111)
As you see, I want to return click_name as multiple values not just one.
I’ve read about clickevent but still not get it. Thank you.
Use clickmode='event+select' to enable accumulation and to handle both click and selection events by listening for one single event (selectedData).
If layout.clickmode = 'event+select', selection data also accumulates
(or un-accumulates) selected data if you hold down the shift button
while clicking.
You can also set dragmode='select' if you want to modebar to be ready for selecting points (default mode is 'zoom').
fig.update_layout(
clickmode='event+select',
dragmode='select',
margin={'l': 40, 'b': 40, 't': 10, 'r': 0},
hovermode='closest'
)
Now in the callback, use selectedData instead of clickData :
#app.callback(
Output('x-time-series', 'children'),
Input('crossfilter-indicator-scatter', 'selectedData'),
prevent_initial_call=True)
def update_y_timeseries(selectedData):
selection= [p['hovertext'] for p in selectedData['points']]
return html.Span(', '.join(selection))
I’m quite new to Python and Plotly Dash, currently facing issues when refactoring this dashboard to use pattern matching and circular callback.
Key Features of dashboard
User adjust weightage of each factor via Slider or Input. Each Slider and Input are synchronized
Each city have base score for each factor in excel sheet. Download excel
Index of each city is calculated by weighted average for all factors (base score * weightage)
Dashboard
Issues when refactoring:
When printing value for one factor for testing, values of all factors were printed instead of one.
As all values are triggered instead of one, unable to differentiate for weighted average calculation for each factor.
Only the highlighted components can be adjusted. For example, Slider for Smart mobility can be adjusted, which change value for corresponding input. But Input value can’t be adjusted to change slider value.
Synchronizing issue between slider-input pairs
Attempt to refactor
import dash
from dash.dependencies import Input, Output, MATCH
from dash import dcc, ctx
import dash_bootstrap_components as dbc
import dash_daq as daq
from dash import html
import plotly.express as px
import pandas as pd
df= pd.read_excel("C:\\Desktop\\Smart_City.xlsx",
sheet_name='cities')
factors = []
for i in df.columns[1:]:
factors.append(i)
PLOTLY_LOGO = "https://images.plot.ly/logo/new-branding/plotly-logomark.png"
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.title = "Smart Cities"
# make a reuseable navitem for the different examples
nav_item = dbc.NavItem(dbc.NavLink("Link", href="https://plotly.com"))
# make a reuseable dropdown for the different examples
dropdown = dbc.DropdownMenu(
children=[
dbc.DropdownMenuItem("Entry 1"),
dbc.DropdownMenuItem("Entry 2"),
dbc.DropdownMenuItem(divider=True),
dbc.DropdownMenuItem("Entry 3"),
],
nav=True,
in_navbar=True,
label="Menu",
)
# this example that adds a logo to the navbar brand
logo = dbc.Navbar(
dbc.Container(
[
html.A(
# Use row and col to control vertical alignment of logo / brand
dbc.Row(
[
dbc.Col(html.Img(src=PLOTLY_LOGO, height="30px")),
dbc.Col(dbc.NavbarBrand("Smart Cities Index", className="ms-2")),
],
align="center",
className="g-0",
),
href="https://plotly.com",
style={"textDecoration": "none"},
),
dbc.NavbarToggler(id="navbar-toggler2", n_clicks=0),
dbc.Collapse(
dbc.Nav(
[nav_item, dropdown],
className="ms-auto",
navbar=True,
),
id="navbar-collapse2",
navbar=True,
),
],
),
color="dark",
dark=True,
className="mb-5",
)
app.layout = html.Div([
logo,
dbc.Row(
[
dbc.Col(html.Div([
dbc.Row(dbc.Col(html.Div([
html.Label(f'{name}'),
dcc.Slider(
id={'type': 'slider','index': name},
min=0, max=100,
marks=None,
value=10,
)],
style={
'font-size': '14px',
'height': '38px',
'width': '100%',
'display': 'inline-block',
'vertical-align': 'bottom',
})
))
for name in factors]), width={"size": 3, "offset": 1}),
dbc.Col(html.Div([
dbc.Row(dbc.Col(html.Div([
html.Br(),
daq.NumericInput(
id={'type': 'num_input','index': name},
min=0, max=100, value=10, size=60,
),],
style={
'height': '38px',
'width': '100%',
'display': 'inline-block',
'vertical-align': 'bottom',
})
))
for name in factors]), width=1),
dbc.Col(html.Div(dbc.Card(html.Div([
dcc.Loading([
dcc.Graph(id="chart", )])], style={'horizontal-align': 'middle' ,'vertical-align': 'middle', 'overflowY': 'scroll'},)
, style={'height': '450px', 'display':'inline-block',})), width=7)
]
),
])
#app.callback(
[Output({'type': 'slider', 'index': MATCH}, 'value'),
Output({'type': 'num_input', 'index': MATCH}, 'value')],
inputs={
"all_inputs": {
"slider": Input({'type': 'slider','index': MATCH}, "value"),
"num_input": Input({'type': 'num_input','index': MATCH}, "value")
}
}
)
def callback(all_inputs):
c = ctx.args_grouping.all_inputs
value_Smart_Mobility = 10 #Quick fix to prevent unbounded error
value_Smart_Mobility = c.num_input.value if c.num_input.id.index == 'Smart_Mobility' else
c.slider.value
print(value_Smart_Mobility) #Values for all factors were printed out, instead of one only
value_Smart_Mobility = c.slider.value if c.slider.id.index == 'Smart_Mobility' else
c.num_input.value
print(value_Smart_Mobility) #Values for all factors were printed out, instead of one only
return value_Smart_Mobility, value_Smart_Mobility
if __name__== '__main__':
app.run_server(debug=True)
Original long callback version
The original long callback version worked but its too lengthy and require manual change in App Callback whenever column names in excel changes.
import dash
from dash.dependencies import Input, Output, MATCH
from dash import dcc, ctx
import dash_bootstrap_components as dbc
import dash_daq as daq
from dash import html
import plotly.express as px
import pandas as pd
# Extract column names from excel file
df= pd.read_excel("C:\\Desktop\\Smart_City.xlsx", sheet_name='cities')
factors = []
for i in df.columns[1:]:
factors.append(i)
PLOTLY_LOGO = "https://images.plot.ly/logo/new-branding/plotly-logomark.png"
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
app.title = "Smart Cities"
# make a reuseable navitem for the different examples
nav_item = dbc.NavItem(dbc.NavLink("Link", href="https://plotly.com"))
# make a reuseable dropdown for the different examples
dropdown = dbc.DropdownMenu(
children=[
dbc.DropdownMenuItem("Entry 1"),
dbc.DropdownMenuItem("Entry 2"),
dbc.DropdownMenuItem(divider=True),
dbc.DropdownMenuItem("Entry 3"),
],
nav=True,
in_navbar=True,
label="Menu",
)
# this example that adds a logo to the navbar brand
logo = dbc.Navbar(
dbc.Container(
[
html.A(
# Use row and col to control vertical alignment of logo / brand
dbc.Row(
[
dbc.Col(html.Img(src=PLOTLY_LOGO, height="30px")),
dbc.Col(dbc.NavbarBrand("Smart Cities Index", className="ms-2")),
],
align="center",
className="g-0",
),
href="https://plotly.com",
style={"textDecoration": "none"},
),
dbc.NavbarToggler(id="navbar-toggler2", n_clicks=0),
dbc.Collapse(
dbc.Nav(
[nav_item, dropdown],
className="ms-auto",
navbar=True,
),
id="navbar-collapse2",
navbar=True,
),
],
),
color="dark",
dark=True,
className="mb-5",
)
# Using list comprehension to create slider and input. ID's Index is column name from
dataframe/excel
app.layout = html.Div([
logo,
dbc.Row(
[
dbc.Col(html.Div([
dbc.Row(dbc.Col(html.Div([
html.Label(f'{name}'),
dcc.Slider(
id={'type': 'slider','index': name},
min=0, max=100,
marks=None,
value=10,
)],
style={
'font-size': '14px',
'height': '38px',
'width': '100%',
'display': 'inline-block',
'vertical-align': 'bottom',
})
))
for name in factors]), width={"size": 3, "offset": 1}),
dbc.Col(html.Div([
dbc.Row(dbc.Col(html.Div([
html.Br(),
daq.NumericInput(
id={'type': 'num_input','index': name},
min=0, max=100, value=10, size=60,
),],
style={
'height': '38px',
'width': '100%',
'display': 'inline-block',
'vertical-align': 'bottom',
})
))
for name in factors]), width=1),
dbc.Col(html.Div(dbc.Card(html.Div([
dcc.Loading([
dcc.Graph(id="chart", )])], style={'horizontal-align': 'middle' ,'vertical-align': 'middle', 'overflowY': 'scroll'},)
, style={'height': '500px', 'display':'inline-block',})), width=7)
]
),
])
# Long callback listing every input and output. Trying to use pattern matching, so that index
wont be hardcoded
#app.callback(
[Output({'type': 'slider','index': "Smart_Mobility"}, "value"),
Output({'type': 'num_input','index': "Smart_Mobility"}, "value"),
Output({'type': 'slider','index': "Smart_Environment"}, "value"),
Output({'type': 'num_input','index': "Smart_Environment"}, "value"),
Output({'type': 'slider','index': "Smart_Government"}, "value"),
Output({'type': 'num_input','index': "Smart_Government"}, "value"),
Output({'type': 'slider','index': "Smart_Economy"}, "value"),
Output({'type': 'num_input','index': "Smart_Economy"}, "value"),
Output({'type': 'slider','index': "Smart_People"}, "value"),
Output({'type': 'num_input','index': "Smart_People"}, "value"),
Output({'type': 'slider','index': "Smart_Living"}, "value"),
Output({'type': 'num_input','index': "Smart_Living"}, "value"),
Output("chart", "figure")],
[Input({'type': 'slider','index': "Smart_Mobility"}, "value"),
Input({'type': 'num_input','index': "Smart_Mobility"}, "value"),
Input({'type': 'slider','index': "Smart_Environment"}, "value"),
Input({'type': 'num_input','index': "Smart_Environment"}, "value"),
Input({'type': 'slider','index': "Smart_Government"}, "value"),
Input({'type': 'num_input','index': "Smart_Government"}, "value"),
Input({'type': 'slider','index': "Smart_Economy"}, "value"),
Input({'type': 'num_input','index': "Smart_Economy"}, "value"),
Input({'type': 'slider','index': "Smart_People"}, "value"),
Input({'type': 'num_input','index': "Smart_People"}, "value"),
Input({'type': 'slider','index': "Smart_Living"}, "value"),
Input({'type': 'num_input','index': "Smart_Living"}, "value")]
)
# Circular callback to synchronize each slider to input. Iterate to calculate weighted average
Index for chart.
def callback(slider_Smart_Mobility, num_input_Smart_Mobility, slider_Smart_Environment,
num_input_Smart_Environment, slider_Smart_Government, num_input_Smart_Government,
slider_Smart_Economy, num_input_Smart_Economy, slider_Smart_People, num_input_Smart_People,
slider_Smart_Living, num_input_Smart_Living):
weightage = []
value_Smart_Mobility = num_input_Smart_Mobility if ctx.triggered_id == {'type':
'num_input','index': "Smart_Mobility"} else slider_Smart_Mobility
weightage.append(value_Smart_Mobility / 100 )
value_Smart_Environment = num_input_Smart_Environment if ctx.triggered_id == {'type':
'num_input','index': "Smart_Environment"} else slider_Smart_Environment
weightage.append(value_Smart_Environment / 100 )
value_Smart_Government = num_input_Smart_Government if ctx.triggered_id == {'type':
'num_input','index': "Smart_Government"} else slider_Smart_Government
weightage.append(value_Smart_Government / 100 )
value_Smart_Economy = num_input_Smart_Economy if ctx.triggered_id == {'type':
'num_input','index': "Smart_Economy"} else slider_Smart_Economy
weightage.append(value_Smart_Economy / 100 )
value_Smart_People = num_input_Smart_People if ctx.triggered_id == {'type':
'num_input','index': "Smart_People"} else slider_Smart_People
weightage.append(value_Smart_People / 100 )
value_Smart_Living = num_input_Smart_Living if ctx.triggered_id == {'type':
'num_input','index': "Smart_Living"} else slider_Smart_Living
weightage.append(value_Smart_Living / 100 )
city_index = []
for index, row in df.iterrows():
base_list = [int(row[i]) for i in factors]
index_list = [int(j*k) for j, k in zip(base_list, weightage)]
index = float("{:.2f}".format(sum(index_list) / sum(weightage)))
city_index.append(index)
df['Index'] = city_index
df['Index'] = pd.to_numeric(df['Index'])
df2 = df.sort_values(by=['Index'], ascending=True)
figure = px.bar(df2, x='Index', y='City', height=500, color='Index',
color_continuous_scale='inferno')
return value_Smart_Mobility, value_Smart_Mobility, value_Smart_Environment,
value_Smart_Environment, value_Smart_Government, value_Smart_Government,
value_Smart_Economy, value_Smart_Economy, value_Smart_People, value_Smart_People,
value_Smart_Living, value_Smart_Living, figure
if __name__== '__main__':
app.run_server(debug=True)
I am trying to apply callbacks between different dash pages. I have setup my app components in different files:
Hotspot_App.py
index.py
folder apps
GeoPlot.py
ScatterPlot.py
So the first page contains the layout of app1 and it's callbacks and components. But now I want to create a checklist in the second tab on the values that have been selected on a checklist in tab1. I tried to create the app.validation layout in the index but that does not work. See below my code:
Index.py
import dash_bootstrap_components as dbc
import dash_html_components as html
from dash.dependencies import Input, Output
import dash_auth
import dash_core_components as dcc
from Hotspot_App_Local import app
from apps import GeoPlot, ScatterPlot
app.title = 'Hotspot Analysis'
auth = dash_auth.BasicAuth(
app,
VALID_USERNAME_PASSWORD_PAIRS
)
scr_oord = 'www.animage.com' #dummy
nav_item = dbc.NavItem(dbc.NavLink("website.com", href="https://www.website.com/"))
dropdown_menu = dbc.DropdownMenu(
children=[
dbc.DropdownMenuItem("GeoPlot", href= "/page-1"),
dbc.DropdownMenuItem("ScatterPlot", href= "/page-2"),
],
nav=True,
in_navbar=True,
label="Menu",
)
navbar = html.Div([dbc.Navbar(
dbc.Container(
[
html.A(
# Use row and col to control vertical alignment of logo / brand
dbc.Row(
[
dbc.Col(html.Img(src= scr_oord, height="50px")),
dbc.Col(dbc.NavbarBrand("\tHotspot Analysis", className="ml-2", style= {'color': 'navy', 'font-weight': 'bold'})),
],
align="center",
no_gutters=True,
),
href="https://plot.ly",
),
dbc.NavbarToggler(id="navbar-toggler2"),
dbc.Collapse(
dbc.Nav(
[nav_item, dropdown_menu], className="ml-auto", navbar=True
),
id="navbar-collapse2",
navbar=True,
),
]
),
color="white",
dark= False,
className="mb-5",
),
html.Div(id= 'page_content')
])
app.layout = html.Div([
dcc.Location(id= 'tab', refresh= False, pathname= "/page-1"),
navbar,
html.Div(id= 'page')
])
app.validation_layout = html.Div([navbar, GeoPlot.layout, ScatterPlot.layout])
#app.callback(Output("page", "children"), [Input("tab", "pathname")])
def display_page(pathname):
if pathname == "/page-1":
return GeoPlot.layout
if pathname == "/page-2":
return ScatterPlot.layout
# if not recognised, return 404 message
return html.P("404 - page not found")
if __name__ == '__main__':
app.run_server(debug=True)
app.py
import dash_bootstrap_components as dbc
import dash
app = dash.Dash(__name__, suppress_callback_exceptions=True, external_stylesheets=[dbc.themes.BOOTSTRAP])
server = app.server
Geoplot.py
import dash_bootstrap_components as dbc
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output, State, ALL, MATCH
import plotly.graph_objects as go
import pandas as pd
import flask
import ast
from flask import send_file
import io
import datetime
from Hotspot_App_Local import app
from Cards import Cards
df = pd.read_csv('merged_layers.csv')
df = df.round(2)
available_indicators = ['PercBelowMSL', 'PopDens2015', 'RPC4.5', 'PortDist', 'RPC8.5']
df['text'] = 'TotPop2020: '+ round(df["TotPop2020"], 0).astype(str)
Storedata = dcc.Store(id= 'store_columns')
Dropdown = dcc.Dropdown(id= 'Chose setting',
options= [
{'label': 'Marine', 'value': 'Marine'},
{'label': 'Environmental', 'value': 'Env'},
{'label': 'Urban', 'value': 'Urban'},
{'label': 'Ports', 'value': 'Ports'}
], value= 'Marine', clearable= False)
card2 = Cards('Select The Setting', [Storedata, Dropdown], Header= True)
checkbox = dbc.Checklist(id = 'col_list')
card3 = Cards('Select The Layers', [checkbox], Header= True)
htmlA = html.A([dbc.Button("Store Values", color= 'success', block= True)], id= 'store-values')
#csvbutton = dbc.Button('Store Values', id= 'csv', block= True)
card4 = Cards('', [htmlA])
sizer = 2 * df['PopDens2020'].max() / (30 ** 2)
fig = go.Figure(data=go.Scattermapbox(
lon = df['center_lon'],
lat = df['center_lat'],
text = df['text'],
mode = 'markers',
marker= go.scattermapbox.Marker(
color= df['PopDens2020'],
cmin = df['PopDens2020'].min(),
cmax= df['PopDens2020'].max(),
colorscale = 'Viridis',
colorbar=dict(
title="PopDens2020"
),
showscale= True,
size= df['PopDens2020'],
sizeref= sizer,
sizemode= 'area'
),
))
fig.update_layout(
title= {'text': f'{len(df)} Hotspot Locations',
'font': {
'size': 36,
'color': 'darkblue'
},
'xref': 'container',
'x': 0.5},
geo_scope='world',
mapbox_style= 'open-street-map',
width= 1200,
height= 800
)
hotspotgraph = dcc.Graph(id= 'Hotspot_Plot', figure= fig)
card5 = Cards("", [hotspotgraph], Header= False)
rangeslider = html.Div(id='RangeSlider', children=[])
card6 = Cards("Select the Range", [rangeslider], Header= True)
size = dcc.Dropdown(id= 'markersize')
card7 = Cards('Select Markersize', [size], Header= True)
color = dcc.Dropdown(id= 'markercolor')
card8 = Cards('Select Markercolor', [color], Header= True)
layout = html.Div(
[
dbc.Row(
[
dbc.Col([
dbc.Col(dbc.Card(card2, color="light", outline=True)),
dbc.Col(dbc.Card(card3, color="light", outline=True)),
dbc.Col(dbc.Card(card4, color="light", outline=True)),
dbc.Row([
dbc.Col(dbc.Card(card7, color="light", outline= True)),
dbc.Col(dbc.Card(card8, color="light", outline= True))
])
]),
dbc.Col(dbc.Card(card5, color="light", outline=True)),
],
className="mb-4",
),
dbc.Row(
[
dbc.Col(dbc.Card(card6, color="light", outline=True), md= 12),
],
className="mb-4",
),
]
)
#app.callback(
Output('store_columns', 'data'),
[Input('Chose setting', 'value')]
)
def change_cols(value):
if value == 'Marine':
data = ['PortDist', 'PercBelow-5m', 'RPC4.5', 'RPC8.5']
if value == 'Urban':
data = ['PopDens2020', 'area']
if value == 'Env':
data = ['ManPc2016', 'WWCrAr2018', 'count_PA', 'NearestDist_PA']
if value == 'Ports':
data = ['RPC4.5', 'PortDist']
return data
#app.callback(
Output('col_list', 'options'),
[Input('store_columns','data')],
)
def change_multiselect(data):
if data is not None:
return [{'label': i, 'value': i} for i in sorted(data)]
#app.callback(
Output('col_list', 'value'),
[Input('store_columns','data')]
)
def change_multiselect(data):
return sorted(data)
#app.callback(
Output('markersize', 'options'),
[Input('col_list','value')]
)
def markersize(values):
default = ['PopDens2020']
default.extend(values)
return [{'label': value, 'value': value} for value in default]
#app.callback(
Output('markersize', 'value'),
[Input('col_list','value')]
)
def markersize(values):
return 'PopDens2020'
#app.callback(
Output('markercolor', 'options'),
[Input('col_list','value')]
)
def markersize(values):
default = ['PopDens2020']
default.extend(values)
return [{'label': value, 'value': value} for value in default]
#app.callback(
Output('markercolor', 'value'),
[Input('col_list','value')]
)
def markersize(values):
return 'PopDens2020'
#app.callback(
Output('RangeSlider', 'children'),
[Input('col_list', 'value')],
[State('RangeSlider', 'children')])
def display_dropdowns(value, ranges):
df = pd.read_csv('merged_layers.csv')
lst = []
if value != None:
for v in value:
fig = go.Figure()
fig.add_trace(go.Histogram(x = df[v]))
fig.add_shape(
dict(
type='line',
yref='paper', y0=0, y1=1,
x0=df[v].median(), x1=df[v].median()
)
)
hist = dcc.Graph(id={'type': 'histogram', 'index': v}, figure=fig)
reset_button = dbc.Button('Reset Values',id={'type': 'reset_button', 'index': v}, block= True, color= 'danger')
card_button = Cards("", [reset_button])
min = df[v].min()
max = df[v].max()
range_slider = dcc.RangeSlider(
id={
'type': 'range_slider',
'index': v
}, tooltip={'always_visible': True, 'placement': 'bottomRight'}, min=min, max=max, value=[min, max], step= 0.01)
store_minmax = dcc.Store(id={'type': 'store', 'index': v}, data= [min, max])
card_slider = Cards(f'{v}', [range_slider, store_minmax], Header= True)
marklen = dcc.Markdown(children= f'**{len(df)} Hotspot Locations**', id={'type': 'len_df', 'index': v},
style= {'textAlign': 'center', 'color': 'darkblue', 'font-size': 25})
card_hist = Cards(f"", [marklen, hist])
ap = html.Div([
dbc.Row(
[
dbc.Col([
dbc.Col(dbc.Card(card_slider, color="light", outline=True)),
dbc.Col(dbc.Card(card_button, color= "light", outline= True))], md= 6),
dbc.Col(dbc.Card(card_hist, color="light", outline=True), md=6)
],
className="mb-4",
),
])
lst.append(ap)
return lst
#app.callback(
Output({'type': 'len_df', 'index': ALL}, 'children'),
[Input({'type': 'range_slider', 'index': ALL}, 'value')],
[State('col_list', 'value'),
State({'type': 'len_df', 'index': ALL}, 'children')]
)
def store_len_df(values, cols, text):
df = pd.read_csv('merged_layers.csv')
for i in range(len(cols)):
min = values[i][0]
max = values[i][1]
df = df[df[cols[i]].between(min, max)]
text = [f'**{len(df)} Hotspot Locations**'] * len(text)
return text
#app.callback(
Output({'type': 'histogram', 'index': MATCH}, 'figure'),
[Input({'type': 'range_slider', 'index': MATCH}, 'value')],
[State({'type': 'histogram', 'index': MATCH}, 'figure')])
def add_verts(range, fig):
fig['layout']['shapes'] = [{'type': 'line', 'x0': range[0], 'x1': range[0], 'y0': 0, 'y1': 1, 'yref': 'paper'},
{'type': 'line', 'x0': range[1], 'x1': range[1], 'y0': 0, 'y1': 1, 'yref': 'paper'}]
return fig
#app.callback(
Output({'type': 'range_slider', 'index': MATCH}, 'value'),
[Input({'type': 'reset_button', 'index': MATCH}, 'n_clicks')],
[State({'type': 'store', 'index': MATCH}, 'data')])
def reset(click, data):
return data
#app.callback(
Output('Hotspot_Plot', 'figure'),
[Input({'type': 'range_slider', 'index': ALL}, 'value'),
Input('markersize', 'value'),
Input('markercolor', 'value')],
[State('col_list', 'value'),
State('Hotspot_Plot', 'figure'),]
)
def update_figure(values, col_size, col_color, cols, fig):
if cols is not None and len(values) > 0 and len(cols) > 0:
df = pd.read_csv('merged_layers.csv')
if col_size != col_color:
df['text'] = f'{col_size}: ' + round(df[col_size], 0).astype(str) + '\n' + f'{col_color}: ' + round(df[col_color], 0).astype(str)
else:
df['text'] = f'{col_size}: ' + round(df[col_size], 0).astype(str)
size_series = df[col_size]
for value in df[col_size]:
if value < 0:
size_series += abs(df[col_size].min()) + 0.01 #No Negative Values Allowed
break
color_series = df[col_color]
sizer = 2 * df[col_size].max() / (30 ** 2)
for i in range(len(cols)):
min = values[i][0]
max = values[i][1]
df = df[df[cols[i]].between(min, max)]
fig['data'][0]['lat'] = df['center_lat']
fig['data'][0]['lon'] = df['center_lon']
fig['data'][0]['marker'] = go.scattermapbox.Marker(
color= color_series,
cmin = color_series.min(),
cmax= color_series.max(),
colorscale = 'Viridis',
colorbar=dict(
title= col_color
),
showscale=True,
size= size_series,
sizeref=sizer,
sizemode='area'
)
fig['data'][0]['text'] = df['text']
fig['layout']['title']['text'] = f'{len(df)} Hotspot Locations'
return fig
#app.callback(
Output('store-values', 'href'),
[Input('col_list', 'value'),
Input({'type': 'range_slider', 'index': ALL}, 'value'),
Input('Chose setting', 'value')])
def update_link(columns, ranges, setting):
return '/dash/urlToDownload?value={}*{}*{}'.format(columns, ranges, setting)
#app.server.route('/dash/urlToDownload')
def download_excel():
data = flask.request.args.get('value').split('*')
columns = ast.literal_eval(data[0])
ranges = ast.literal_eval(data[1])
setting = data[2]
minlst, maxlst= [], []
for r in ranges:
min, max = r
minlst.append(min)
maxlst.append(max)
range_df = pd.DataFrame(list(zip(columns, minlst, maxlst)), columns=['Column', 'Min_Value', 'Max_Value'])
df = pd.read_csv('Dash/merged_layers.csv')
for (i, range) in enumerate(ranges):
min, max = range
df = df[df[columns[i]].between(min, max)]
center_lon = df['center_lon'].values
center_lat = df['center_lat'].values
country = df['country'].values
loc_df = pd.DataFrame(list(zip(center_lon, center_lat, country)), columns= ['center_lon', 'center_lat', 'country'])
loc_df = loc_df.sort_values('country')
buf = io.BytesIO()
excel_writer = pd.ExcelWriter(buf, engine="xlsxwriter")
range_df.to_excel(excel_writer, sheet_name="Ranges", index=False)
loc_df.to_excel(excel_writer, sheet_name="Locations", index=False)
excel_writer.save()
excel_data = buf.getvalue()
buf.seek(0)
now = datetime.datetime.now()
today = str(now.year) + '-' + str(now.month) + '-' + str(now.day)+ ' ' + str(now.hour) + str(":") + str(now.minute)
return send_file(
buf,
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
attachment_filename=f"{setting}_{today}.xlsx",
as_attachment=True,
cache_timeout=0
)
Scatterplot.py
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, ALL, MATCH
from Hotspot_App_Local import app
from Cards import Cards
test = dcc.Checklist(id= 'scatterlst')
card1 = Cards('Test', test, Header= True)
layout = html.Div(
dbc.Row(dbc.Card(card1, color="light", outline=True))
)
#app.callback(
Output('scatterlst', 'value'),
[Input('col_list','value')]
)
def change_multiselect(data):
print('hi')
print(data)
return sorted(data)
So the problem is that Scatterplot.py does not recognizes the indexes from Geoplot.py.