ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Dash router
    plotly dash 2022. 5. 30. 21:57

    문제

    dash-2.5.0 이상 버전 부터 Dash Pages 기능을 제공하고 있다. - 참고

    https://stackoverflow.com/a/62337544 에 언급된 것 처럼, 두개의 dcc.Location 를 이용해야 한다.

    dash-2.5.0 이상 버전의 Dash Pages 를 사용하면, 한개의 dcc.Location 으로 구현할 수 있다.

     

    • fired when the url (dcc.Location) is changed and updates the selected tab: Dash Page 이용
    • fired when the selected tab changes (dcc.Tabs) and updates the displayed url: dcc.Location 이용

    https://dash.plotly.com/urls 의 Example 을 바탕으로 

    http://127.0.0.1:8050/analytics?city=Montreal 이 동작하는 application 을 개발해 보자

     

    코드

    소스 구조

    ├── app.py
    └── pages
        ├── analytics.py
        ├── archive.py
        └── home.py

     

     

    app.py

    import dash
    from dash import Dash, html, dcc  # type: ignore
    import dash_bootstrap_components as dbc
    
    app = Dash(__name__, use_pages=True, external_stylesheets=[dbc.themes.BOOTSTRAP])
    
    app.layout = html.Div([
        html.H1('Multi-page app with Dash Pages'),
    
        html.Div(
            [
                html.Div(
                    dcc.Link(
                        f"{page['name']} - {page['path']}", href=page["relative_path"]
                    )
                )
                for page in dash.page_registry.values()
            ]
        ),
    
        dash.page_container
    ])
    
    if __name__ == '__main__':
        app.run_server(debug=True)

     

    pages/analytics.py

    from urllib import parse
    
    import dash
    from dash import html, dcc, callback, Input, Output
    
    dash.register_page(__name__)
    
    
    def layout(city=None):
        selected = 'Montreal' if city is None else city
    
        return html.Div([
            dcc.Location(id='url', refresh=True),
            html.H1("Analytics Page"),
            html.Div([
                "Select a city: ",
                dcc.RadioItems(['NewYork', 'Montreal'], selected, id='analytics-input')
            ]),
            html.Br(),
            html.Div(f'You selected: {selected}')
        ])
    
    
    @callback(
        Output('url', 'search'),
        Input('analytics-input', 'value')
    )
    def update_city_selected(input):
        return f"?city={parse.quote(input, encoding='utf-8')}"

     

    pages/archive.py

    import dash
    from dash import html
    
    dash.register_page(__name__)
    
    layout = html.Div(children=[
        html.H1(children='This is our Archive page'),
    
        html.Div(children='''
            This is our Archive page content.
        '''),
    
    ])

     

    pages/home.py

    import dash
    from dash import html
    
    dash.register_page(__name__, path='/')
    
    layout = html.Div(children=[
        html.H1(children='This is our Home page'),
    
        html.Div(children='''
            This is our Home page content.
        '''),
    
    ])

     

    ---------------------------------------------------------------------------

     

     

    dash-2.5.0 이전 버전 일때

    문제1

    아래와 같이 Router 를 구현해 보자. 코드2 에서 구현한 방법보다 코드1 방법이 더 나은 것 같다.

    코드1

    from typing import Optional
    from urllib import parse
    
    import dash_bootstrap_components as dbc
    from dash import Dash, html, dcc, Input, Output, State
    
    app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])
    
    app.layout = html.Div([
        dcc.Location(id='url', refresh=False),
        dbc.Form([
            dbc.RadioItems(id='country-radio',
                           name='country',
                           options=[{'label': 'Korea', 'value': 'ko'}, {'label': 'USA', 'value': 'usa'}]),
            dbc.Button(id='submit-button', n_clicks=0, children='Submit', type='submit', color='primary'),
    
        ], prevent_default_on_submit=False),
        html.Br(),
        html.Br(),
        html.Div(id='selected-div')
    ], style={'margin': '10px'})
    
    
    @app.callback(
        Output('country-radio', 'value'),
        Output('submit-button', 'n_clicks'),  # submit 버튼 클릭 효과
        Input('url', 'href')
    )
    def apply_url_to_form(href: str) -> (Optional[str], int):
        parsed_url = parse.urlparse(href)
        queries = parse.parse_qs(parsed_url.query, encoding='utf-8')
        selected_country = queries.get('country')[0] if queries.get('country') else None
        return selected_country, 0
    
    
    @app.callback(
        Output('selected-div', 'children'),
        Input('submit-button', 'n_clicks'),
        State('country-radio', 'value')
    )
    def show_result(clicked, country) -> str:
        return f'You selected country={country}'
    
    
    if __name__ == '__main__':
        app.run_server(debug=True)

     

     

    문제2

    ctx.triggered_id 를 이용해 Router 를 구현해 보자

     

    예1: 입력된 URL 로 최종 결과까지 노출되게 한다. (마치 submit 버튼을 누른 것 처럼)

    예2: Submit 버튼을 누른 후에야 선택한 country 가 URL 이나 country 결과에 반영되어야 한다.

    코드2

    from dash import Dash, html, dcc, Input, Output, State, ctx
    
    app = Dash()
    
    country_list = ['korea', 'usa']
    
    app.layout = html.Div([
        dcc.Location(id='location', refresh=False),
        dcc.RadioItems(id='country-radio', options=country_list),
        html.Button(id='submit-button', n_clicks=0, children='Submit'),
        html.Br(),
        html.Br(),
        html.Div(id='selected-div')
    ])
    
    
    @app.callback(
        Output('country-radio', 'value'),
        Input('location', 'pathname')
    )
    def apply_location(pathname: str):
        country = pathname.lstrip('/').lower()
        return country
    
    
    @app.callback(
        Output('selected-div', 'children'),
        Output('location', 'pathname'),  # 브라우저의 URL 을 변경
        Input('submit-button', 'n_clicks'),
        Input('location', 'pathname'),
        State('country-radio', 'value')
    )
    def show_result(clicked, pathname: str, country):
        cntry = country if ctx.triggered_id == 'submit-button' else pathname.lstrip('/').lower()
        path = f'/{country}' if ctx.triggered_id == 'submit-button' else pathname
        return f'You selected country={cntry}', path
    
    
    if __name__ == '__main__':
        app.run_server(debug=True)

     

    설명

     

    'plotly dash' 카테고리의 다른 글

    RangeSlider  (0) 2022.06.14
    Flask route 추가하기  (0) 2022.05.31
    Grid Layout  (0) 2022.05.30
    CSS 적용하기  (0) 2022.05.30
    Callbacks with State  (0) 2022.05.29

    댓글

Designed by Tistory.