在多页面应用程序的 Dash 回调的“输入”中使用了一个不存在的对象
A nonexistent object was used in an `Input` of a Dash callback an multipage app
我正在尝试在 Dash 和 Plotly 中构建一个基本网页,使用回调根据路径名提供股票图表。
我还希望图表有一个回调,其中包含指定要显示的数据时间段的单选按钮。
图表工作正常,但我也希望其他页面做其他事情 return 不同 html 但是因为我在 @[=23= 中指定了 callback_layout ], 它出现在每一页上。
但是,如果我从@app.layout 中删除 callback_layout,我会在 Dash 中收到一条错误消息:“在多页 Dash 回调的 Input
中使用了一个不存在的对象应用程序”。
我该如何解决这个问题?我希望 'stock-period' 下的回调单选按钮仅在我需要拉出 Plotly 图表时显示
import dash
import dash_bootstrap_components as dbc
from dash import html
from dash import dcc
import plotly.express as px
from dash.dependencies import Input, Output, State
import pandas as pd
import yfinance as yf
external_stylesheets = [dbc.themes.CYBORG] #DARKLY
app = dash.Dash(__name__, external_stylesheets=external_stylesheets, suppress_callback_exceptions=True)
# styling the sidebar
SIDEBAR_STYLE = {
"position": "fixed",
"top": 0,
"left": 0,
"bottom": 0,
"width": "16rem",
"padding": "2rem 1rem",
# "background-color": "#4C4C4C",
}
# padding for the page content
CONTENT_STYLE = {
"margin-left": "18rem",
"margin-right": "2rem",
"padding": "2rem 1rem",
"background-color": "#060606"
}
sidebar = html.Div(
[
html.H2("Sidebard / Stocks", className="display-4"),
html.Hr(),
html.P(
"Number of students per education level", className="lead"
),
dbc.Nav(
[
dbc.NavLink("Home", href="/", active="exact"),
dbc.NavLink("Test", href="/test", active="exact"),
dbc.NavLink("NVDA", href="/NVDA", active="exact"),
dbc.NavLink("MSFT", href="/MSFT", active="exact"),
dbc.NavLink("AAPL", href="/AAPL", active="exact"),
],
vertical=True,
pills=True,
),
],
style=SIDEBAR_STYLE,
)
content = html.Div(id="page-content", children=[], style=CONTENT_STYLE) # Graph goes inside children via call back
callback_layout = html.Div([
dcc.RadioItems(
id='period-selector',
options = [
{'label': '1 mth', 'value': '1mo'},
{'label': '3 mth', 'value': '3mo'},
{'label': '6 mth', 'value': '6mo'},
{'label': '1 yr', 'value': '1y'},
{'label': '5 yr', 'value': '5y'}
],
value = "6mo",
#labelStyle is done in CSS
labelStyle={
'textAlign': 'center',
'display': 'inline-block',
'color': 'white',
'font-family': 'Arial',
'font-size' : '15px',
'padding-right': '30px'
},
style={
'textAlign': 'center'
}
)
])
app.layout = html.Div([
dcc.Location(id="url", refresh=False),
sidebar,
content,
callback_layout
])
homepage_layout = html.Div([
dcc.Link('Go to Page 1', href='/page-1'),
html.Br(),
dcc.Link('Go to Page 2', href='/page-2'),
])
""" Plotly graph """
@app.callback(
Output("page-content", "children"),
Input("url", "pathname"),
Input('period-selector', 'value')
)
def render_page_content(pathname, period_selector):
""" App routing from sidebar to content depending on path """
if pathname == '/':
return homepage_layout
if pathname == '/test':
return 'Test'
else: # If pathname isn't defined above, assume is stock ticker and calls graph below
interval = '1d'
quote = yf.Ticker(pathname)
hist = quote.history(period_selector, interval)
df = hist.round(decimals=2)
last_price = df.iloc[-1, 0] # return first row (-1)
#Performance over period calc
performance_calc = ((df.iloc[-1, 0] / df.iloc[0, 0]) - 1) * 100
if performance_calc >= 0:
performance = '+' + str(round(performance_calc,2))
else:
performance = str(round(performance_calc,2))
#Define color of performance %
performance_int = (round(performance_calc,2))
if performance_int >= 0:
perf_color = 'lime'
else:
perf_color = 'red'
fig = px.line(df,
x=df.index, y=df["Close"],
title='NVDA',
template="plotly_dark",
color_discrete_sequence=['lime'],
labels = {'Date': ''}
)
if interval == '1mo':
d_tick='604800000' #7 days in milliseconds. Datetime format requires ms input.
elif interval == '5y':
d_tick = 'M12'
else:
d_tick='M1'
fig.update_xaxes(
dtick=d_tick,
showgrid=False,
)
fig.update_yaxes(
showgrid=False,
visible=False
)
#Last price annotation
fig.add_annotation(dict(font=dict(color='white',size=40, family='Arial Black'),
x=0,
y=1.2,
showarrow=False,
text=str(last_price),
textangle=0,
xanchor='left',
xref="paper",
yref="paper")
)
#Performance annotation
fig.add_annotation(dict(font=dict(color=perf_color,size=25, family='Arial'),
x=0.14,
y=1.12,
showarrow=False,
text=str(performance) + '%',
textangle=0,
xanchor='left',
xref="paper",
yref="paper")
)
fig.update_traces(hovertemplate=None)
fig.update_layout(
plot_bgcolor='#060606',
paper_bgcolor='#060606',
font_color='white',
title=dict(
y = 0.82, #Adjust location of stock ticker title
x = 0.92,
xanchor = 'center',
yanchor = 'top',
font=dict(
family="Arial",
size=25,
color='white'
)
),
hovermode = 'x',
# xaxis_tickformat = '%b'
)
# Return below into children tag above
return [
html.H1(pathname,
style={'textAlign':'center'}),
dcc.Graph(id='stock-chart',
figure = fig
)
]
if __name__=='__main__':
app.run_server(debug=True, port=3000)
要获得所需的行为,您只需隐藏所有不构建股票价格图表的端点的图表单选按钮。为此,将 period-selector 元素的样式属性作为输出添加到主回调中,如下所示。
app.layout = html.Div([
dcc.Location(id="url", refresh=False),
sidebar,
content,
callback_layout # Leave this here, do not move.
])
@app.callback(
Output("page-content", "children"),
Output("period-selector", "style"), # Add new output here
Input("url", "pathname"),
Input('period-selector', 'value')
)
def render_page_content(pathname, period_selector):
""" App routing from sidebar to content depending on path """
hidden_style = {'display': 'none'}
# It would be best to make visible_style a module level variable.
# Use it for both the app layout and this callback function.
# That way if you want to change its location or look you only have to edit one location.
visible_style = {'textAlign': 'center'}
if pathname == '/':
return homepage_layout, hidden_style
if pathname == '/test':
return 'Test', hidden_style
else:
# Your other build code here.
return [
html.H1(pathname,
style={'textAlign': 'center'}),
dcc.Graph(id='stock-chart',
figure=fig
)
], visible_style
我正在尝试在 Dash 和 Plotly 中构建一个基本网页,使用回调根据路径名提供股票图表。
我还希望图表有一个回调,其中包含指定要显示的数据时间段的单选按钮。
图表工作正常,但我也希望其他页面做其他事情 return 不同 html 但是因为我在 @[=23= 中指定了 callback_layout ], 它出现在每一页上。
但是,如果我从@app.layout 中删除 callback_layout,我会在 Dash 中收到一条错误消息:“在多页 Dash 回调的 Input
中使用了一个不存在的对象应用程序”。
我该如何解决这个问题?我希望 'stock-period' 下的回调单选按钮仅在我需要拉出 Plotly 图表时显示
import dash
import dash_bootstrap_components as dbc
from dash import html
from dash import dcc
import plotly.express as px
from dash.dependencies import Input, Output, State
import pandas as pd
import yfinance as yf
external_stylesheets = [dbc.themes.CYBORG] #DARKLY
app = dash.Dash(__name__, external_stylesheets=external_stylesheets, suppress_callback_exceptions=True)
# styling the sidebar
SIDEBAR_STYLE = {
"position": "fixed",
"top": 0,
"left": 0,
"bottom": 0,
"width": "16rem",
"padding": "2rem 1rem",
# "background-color": "#4C4C4C",
}
# padding for the page content
CONTENT_STYLE = {
"margin-left": "18rem",
"margin-right": "2rem",
"padding": "2rem 1rem",
"background-color": "#060606"
}
sidebar = html.Div(
[
html.H2("Sidebard / Stocks", className="display-4"),
html.Hr(),
html.P(
"Number of students per education level", className="lead"
),
dbc.Nav(
[
dbc.NavLink("Home", href="/", active="exact"),
dbc.NavLink("Test", href="/test", active="exact"),
dbc.NavLink("NVDA", href="/NVDA", active="exact"),
dbc.NavLink("MSFT", href="/MSFT", active="exact"),
dbc.NavLink("AAPL", href="/AAPL", active="exact"),
],
vertical=True,
pills=True,
),
],
style=SIDEBAR_STYLE,
)
content = html.Div(id="page-content", children=[], style=CONTENT_STYLE) # Graph goes inside children via call back
callback_layout = html.Div([
dcc.RadioItems(
id='period-selector',
options = [
{'label': '1 mth', 'value': '1mo'},
{'label': '3 mth', 'value': '3mo'},
{'label': '6 mth', 'value': '6mo'},
{'label': '1 yr', 'value': '1y'},
{'label': '5 yr', 'value': '5y'}
],
value = "6mo",
#labelStyle is done in CSS
labelStyle={
'textAlign': 'center',
'display': 'inline-block',
'color': 'white',
'font-family': 'Arial',
'font-size' : '15px',
'padding-right': '30px'
},
style={
'textAlign': 'center'
}
)
])
app.layout = html.Div([
dcc.Location(id="url", refresh=False),
sidebar,
content,
callback_layout
])
homepage_layout = html.Div([
dcc.Link('Go to Page 1', href='/page-1'),
html.Br(),
dcc.Link('Go to Page 2', href='/page-2'),
])
""" Plotly graph """
@app.callback(
Output("page-content", "children"),
Input("url", "pathname"),
Input('period-selector', 'value')
)
def render_page_content(pathname, period_selector):
""" App routing from sidebar to content depending on path """
if pathname == '/':
return homepage_layout
if pathname == '/test':
return 'Test'
else: # If pathname isn't defined above, assume is stock ticker and calls graph below
interval = '1d'
quote = yf.Ticker(pathname)
hist = quote.history(period_selector, interval)
df = hist.round(decimals=2)
last_price = df.iloc[-1, 0] # return first row (-1)
#Performance over period calc
performance_calc = ((df.iloc[-1, 0] / df.iloc[0, 0]) - 1) * 100
if performance_calc >= 0:
performance = '+' + str(round(performance_calc,2))
else:
performance = str(round(performance_calc,2))
#Define color of performance %
performance_int = (round(performance_calc,2))
if performance_int >= 0:
perf_color = 'lime'
else:
perf_color = 'red'
fig = px.line(df,
x=df.index, y=df["Close"],
title='NVDA',
template="plotly_dark",
color_discrete_sequence=['lime'],
labels = {'Date': ''}
)
if interval == '1mo':
d_tick='604800000' #7 days in milliseconds. Datetime format requires ms input.
elif interval == '5y':
d_tick = 'M12'
else:
d_tick='M1'
fig.update_xaxes(
dtick=d_tick,
showgrid=False,
)
fig.update_yaxes(
showgrid=False,
visible=False
)
#Last price annotation
fig.add_annotation(dict(font=dict(color='white',size=40, family='Arial Black'),
x=0,
y=1.2,
showarrow=False,
text=str(last_price),
textangle=0,
xanchor='left',
xref="paper",
yref="paper")
)
#Performance annotation
fig.add_annotation(dict(font=dict(color=perf_color,size=25, family='Arial'),
x=0.14,
y=1.12,
showarrow=False,
text=str(performance) + '%',
textangle=0,
xanchor='left',
xref="paper",
yref="paper")
)
fig.update_traces(hovertemplate=None)
fig.update_layout(
plot_bgcolor='#060606',
paper_bgcolor='#060606',
font_color='white',
title=dict(
y = 0.82, #Adjust location of stock ticker title
x = 0.92,
xanchor = 'center',
yanchor = 'top',
font=dict(
family="Arial",
size=25,
color='white'
)
),
hovermode = 'x',
# xaxis_tickformat = '%b'
)
# Return below into children tag above
return [
html.H1(pathname,
style={'textAlign':'center'}),
dcc.Graph(id='stock-chart',
figure = fig
)
]
if __name__=='__main__':
app.run_server(debug=True, port=3000)
要获得所需的行为,您只需隐藏所有不构建股票价格图表的端点的图表单选按钮。为此,将 period-selector 元素的样式属性作为输出添加到主回调中,如下所示。
app.layout = html.Div([
dcc.Location(id="url", refresh=False),
sidebar,
content,
callback_layout # Leave this here, do not move.
])
@app.callback(
Output("page-content", "children"),
Output("period-selector", "style"), # Add new output here
Input("url", "pathname"),
Input('period-selector', 'value')
)
def render_page_content(pathname, period_selector):
""" App routing from sidebar to content depending on path """
hidden_style = {'display': 'none'}
# It would be best to make visible_style a module level variable.
# Use it for both the app layout and this callback function.
# That way if you want to change its location or look you only have to edit one location.
visible_style = {'textAlign': 'center'}
if pathname == '/':
return homepage_layout, hidden_style
if pathname == '/test':
return 'Test', hidden_style
else:
# Your other build code here.
return [
html.H1(pathname,
style={'textAlign': 'center'}),
dcc.Graph(id='stock-chart',
figure=fig
)
], visible_style