Я пытаюсь применить обратные вызовы между разными страницами da sh. Я установил компоненты своего приложения в разных файлах:
- Hotspot_App.py
- index.py
- папка apps
- GeoPlot.py
- ScatterPlot.py
Итак, первая страница содержит макет app1, его обратные вызовы и компоненты. Но теперь я хочу создать контрольный список на второй вкладке для значений, которые были выбраны в контрольном списке на вкладке1. Я попытался создать макет app.validation в индексе, но это не сработало. См. Ниже мой код:
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)
Проблема в том, что Scatterplot.py не распознает индексы из Geoplot.py.