在多页应用程序的 Dash 回调的“输入”中使用了不存在的对象

发布于 2025-01-12 00:09:57 字数 6690 浏览 0 评论 0原文

我正在尝试在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

树深时见影 2025-01-19 00:09:57

要获得所需的行为,您只需隐藏不构建股票价格图表的所有端点的图表单选按钮即可。为此,将 period-selector 元素的 style 属性添加为主回调的输出,如下所示。

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

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.

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
俏︾媚 2025-01-19 00:09:57

来自 dash-extensionsMultiplexerTransform > 就是为了解决这个问题而设计的。要启用它,请将从 Dash 导入的内容替换为 dash-extensions.enrich 中的导入内容,

from dash import Dash, Input, Output, html, ...  # before
from dash_extensions.enrich import Dash, Input, Output, html, ...  # after

错误就会消失。您可以在文档中阅读更多详细信息。

The MultiplexerTransform from dash-extensions was designed to solve this problem. To enable it, replace you imports from Dash with their equivalents from dash-extensions.enrich,

from dash import Dash, Input, Output, html, ...  # before
from dash_extensions.enrich import Dash, Input, Output, html, ...  # after

and the error should disappear. You can read more about the details in the documentation.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文