-
Dash routerplotly 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.pyapp.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