在多页应用程序的 Dash 回调的“输入”中使用了不存在的对象
我正在尝试在 Dash 和 Plotly 中构建一个基本网页,使用回调根据路径名提供股票图表。
我还希望图表有一个带有单选按钮的回调,指定要显示的数据的时间段。
这些图表工作正常,但我也希望其他页面执行其他操作并返回不同的 html,但因为我在 @app.layout 中指定了callback_layout,所以它显示在每个页面上。
但是,如果我从@app.layout中删除callback_layout,我会在Dash中收到错误消息,提示“在多页应用程序的Dash回调的输入
中使用了不存在的对象”。
我该如何解决这个问题?我希望“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)
I am trying to build a basic webpage in Dash and Plotly, using callbacks to deliver stock graphs based on the pathname.
I would also like the graph to have a callback with radio buttons that specify the time period of data to display.
The graphs work fine however I would also like other pages to do other things and return different html but because I have specified the callback_layout in the @app.layout, it shows up on every page.
However, If I remove the callback_layout from @app.layout, I get an error in Dash saying "A nonexistent object was used in an Input
of a Dash callback an multipage app".
How would I solve this? I want the callback radio buttons under 'stock-period' to only show up when I need to pull up the Plotly graph
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)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
要获得所需的行为,您只需隐藏不构建股票价格图表的所有端点的图表单选按钮即可。为此,将 period-selector 元素的 style 属性添加为主回调的输出,如下所示。
To get the desired behavior you can just hide the chart radio buttons for all endpoints that do not build the stock price graph. To do this add the style attribute of the period-selector element as an output to your main callback as shown below.
来自
dash-extensions
MultiplexerTransform > 就是为了解决这个问题而设计的。要启用它,请将从Dash
导入的内容替换为dash-extensions.enrich
中的导入内容,错误就会消失。您可以在文档中阅读更多详细信息。
The
MultiplexerTransform
fromdash-extensions
was designed to solve this problem. To enable it, replace you imports fromDash
with their equivalents fromdash-extensions.enrich
,and the error should disappear. You can read more about the details in the documentation.