月份:2020年5月
Dash Python 教程(7):输入状态
某些时候,你需要读取用户输入的值,当不希望用户一按键就读取,而是需要等用户输入所有信息后,再读取用户输入。比如之前我们使用Input,Output的一个例子:
app.layout = html.Div( [ dcc.Input(id="input-1", type="text", value="Montréal"), dcc.Input(id="input-2", type="text", value="Canada"), html.Div(id="number-output"), ] ) @app.callback( Output("number-output", "children"), [Input("input-1", "value"), Input("input-2", "value")], ) def update_output(input1, input2): return u'Input 1 is "{}" and Input 2 is "{}"'.format(input1, input2)
用户在两个输入框输入时,下面的提示都会改变。
针对这种情况,dash.dependencies.State可以把UI组件当前的状态属性作为额外参数传给回调函数,而不会触发回调函数,比如我们把什么两个Input修改成State,添加一个按钮作为Input
app.layout = html.Div([ dcc.Input(id='input-1-state', type='text', value='Montréal'), dcc.Input(id='input-2-state', type='text', value='Canada'), html.Button(id='submit-button-state', n_clicks=0, children='Submit'), html.Div(id='output-state') ]) @app.callback(Output('output-state', 'children'), [Input('submit-button-state', 'n_clicks')], [State('input-1-state', 'value'), State('input-2-state', 'value')]) def update_output(n_clicks, input1, input2): return u''' The Button has been pressed {} times, Input 1 is "{}", and Input 2 is "{}" '''.format(n_clicks, input1, input2)
使用State,用户输入城市和国家时Dash不会调用回调函数,而用户点击按钮时才过调用函数,但Input1和Inpupt2的当前值会作为参数传给回调函数。
Dash Python 教程(6): 多个输出以及串联回调函数
Dash 通用支持多个输出UI组件的属性。比如下面的例子给出一个数x的2次方,3次方,2的指数,3的指数,x的x指数
app.layout = html.Div([ dcc.Input( id='num-multi', type='number', value=5 ), html.Table([ html.Tr([html.Td(['x', html.Sup(2)]), html.Td(id='square')]), html.Tr([html.Td(['x', html.Sup(3)]), html.Td(id='cube')]), html.Tr([html.Td([2, html.Sup('x')]), html.Td(id='twos')]), html.Tr([html.Td([3, html.Sup('x')]), html.Td(id='threes')]), html.Tr([html.Td(['x', html.Sup('x')]), html.Td(id='x^x')]), ]), ]) @app.callback( [Output('square', 'children'), Output('cube', 'children'), Output('twos', 'children'), Output('threes', 'children'), Output('x^x', 'children')], [Input('num-multi', 'value')]) def callback_a(x): return x ** 2, x ** 3, 2 ** x, 3 ** x, x ** x
此外也可以把多个回调函数串联起来,前一个函数的输出做为下个函数的输入:实现某些关联操作,比如选择城市,选择好国家后,城市列表应该和所选择的国家匹配:
Dash Python 教程(5):多个输入用户交互
Dash 定义回调函数时,一个输出可以对应多个输入,比如可以使用多个参数来过滤数据用来显示:比如我们使用两个下来框,两个单选框和一个滑条对应一个Graph的figure属性。
import pandas as pd df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv') available_indicators = df['Indicator Name'].unique() app.layout = html.Div([ html.Div([ html.Div([ dcc.Dropdown( id='xaxis-column', options=[{'label': i, 'value': i} for i in available_indicators], value='Fertility rate, total (births per woman)' ), dcc.RadioItems( id='xaxis-type', options=[{'label': i, 'value': i} for i in ['Linear', 'Log']], value='Linear', labelStyle={'display': 'inline-block'} ) ], style={'width': '48%', 'display': 'inline-block'}), html.Div([ dcc.Dropdown( id='yaxis-column', options=[{'label': i, 'value': i} for i in available_indicators], value='Life expectancy at birth, total (years)' ), dcc.RadioItems( id='yaxis-type', options=[{'label': i, 'value': i} for i in ['Linear', 'Log']], value='Linear', labelStyle={'display': 'inline-block'} ) ],style={'width': '48%', 'float': 'right', 'display': 'inline-block'}) ]), dcc.Graph(id='indicator-graphic'), dcc.Slider( id='year--slider', min=df['Year'].min(), max=df['Year'].max(), value=df['Year'].max(), marks={str(year): str(year) for year in df['Year'].unique()}, step=None ) ]) @app.callback( Output('indicator-graphic', 'figure'), [Input('xaxis-column', 'value'), Input('yaxis-column', 'value'), Input('xaxis-type', 'value'), Input('yaxis-type', 'value'), Input('year--slider', 'value')]) def update_graph(xaxis_column_name, yaxis_column_name, xaxis_type, yaxis_type, year_value): dff = df[df['Year'] == year_value] return { 'data': [dict( x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'], y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'], text=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'], mode='markers', marker={ 'size': 15, 'opacity': 0.5, 'line': {'width': 0.5, 'color': 'white'} } )], 'layout': dict( xaxis={ 'title': xaxis_column_name, 'type': 'linear' if xaxis_type == 'Linear' else 'log' }, yaxis={ 'title': yaxis_column_name, 'type': 'linear' if yaxis_type == 'Linear' else 'log' }, margin={'l': 40, 'b': 40, 't': 10, 'r': 0}, hovermode='closest' ) }
可以看到回调函数使用了5个参数。@app.callback 定义了5个Input,对应于5个不同的UI组件的属性。
Dash Python 教程(4): 使用回调函数实现界面交互
前面我们使用dash_html_components和dash_core_components构造了用户界面,但没有添加任何用户互动。
Dash中是通过回调函数(callback)来实现用户界面用户的。
from dash.dependencies import Input, Output
dash.dependencies 定义了Input,和Output 代表输入和输出,这里的Input和dash_html_components里的Input是不一样的。
dash_html_components里的Input为一文本输入框。而dash.dependencies里的Input,Output用来描述用户界面交互的回调函数。
我们先看一个简单的例子
app.layout = html.Div([ dcc.Input(id='my-id', value='initial value', type='text'), html.Div(id='my-div') ]) @app.callback( Output(component_id='my-div', component_property='children'), [Input(component_id='my-id', component_property='value')] ) def update_output_div(input_value): return 'You\'ve entered "{}"'.format(input_value)
Dash 中,输入输出代表了UI组件的某些属性,在本例中,输入为文本框的value属性,而输出为div的children属性。当输入属性发生变化时,Dash会调用由@app.callback修饰过的函数,如本例update_output_div。 函数的参数为输入文本框的value属性。而函数的输出用来设置输出UI部件对应的属性。component_id和component_property的参数名称是可以省略的。每个UI组件需要指定id来标明。
下面在看一个稍微复杂些的回调函数,用户可以通过拉动Slider来选择显示某一年GDP的图像:
本例的输入为Slider的value属性,而输出为Graph的figure 属性。Figure的transition = {‘duration’: 500},表示动画转换不同年份GDP。
Dash Python 教程(3): 更多的关于用户界面布局
dash_html_components 定义了所有的HTML元素,这包括了这些HTML元素的所有属性,比如CSS风格属性。
比如我们可以定义一些inline CSS属性:
colors = { 'background': '#111111', 'text': '#7FDBFF' } app.layout = html.Div(style={'backgroundColor': colors['background']}, children=[ html.H1( children='Hello Dash', style={ 'textAlign': 'center', 'color': colors['text'] } ), html.Div(children='Dash: A web application framework for Python.', style={ 'textAlign': 'center', 'color': colors['text'] }), dcc.Graph( id='example-graph-2', figure={ 'data': [ {'x': [1, 2, 3], 'y': [4, 1, 2], 'type': 'bar', 'name': 'SF'}, {'x': [1, 2, 3], 'y': [2, 4, 5], 'type': 'bar', 'name': u'Montréal'}, ], 'layout': { 'plot_bgcolor': colors['background'], 'paper_bgcolor': colors['background'], 'font': { 'color': colors['text'] } } } ) ])
这个例子我们修改了Div和H1的 CSS 风格,比如:html.H1(‘Hello Dash’, style={‘textAlign’: ‘center’, ‘color’: ‘#7FDBFF’}) 渲染成
<h1 style=”text-align: center; color: #7fdbff;”>Hello Dash</h1>
要注意的是style风格定义和HTML的style定义稍有不同, 比如css中class在Dash中使用className, 属性的名称使用camelCased,比如 text-align 代替textAlign.
dash_core_components 中的一个Graph可以用来显示各种图表,它是基于plotlt.js开发,支持多达35种不同样式的图表:
df = pd.read_csv('https://gist.githubusercontent.com/chriddyp/5d1ea79569ed194d432e56108a04d188/raw/a9f9e8076b837d541398e999dcbac2b2826a81f8/gdp-life-exp-2007.csv') app.layout = html.Div([ dcc.Graph( id='life-exp-vs-gdp', figure={ 'data': [ dict( x=df[df['continent'] == i]['gdp per capita'], y=df[df['continent'] == i]['life expectancy'], text=df[df['continent'] == i]['country'], mode='markers', opacity=0.7, marker={ 'size': 15, 'line': {'width': 0.5, 'color': 'white'} }, name=i ) for i in df.continent.unique() ], 'layout': dict( xaxis={'type': 'log', 'title': 'GDP Per Capita'}, yaxis={'title': 'Life Expectancy'}, margin={'l': 40, 'b': 40, 't': 10, 'r': 10}, legend={'x': 0, 'y': 1}, hovermode='closest' ) } ) ])
Markdown支持
markdown_text = ''' ### Dash and Markdown Dash apps can be written in Markdown. Dash uses the [CommonMark](http://commonmark.org/) specification of Markdown. Check out their [60 Second Markdown Tutorial](http://commonmark.org/help/) if this is your first introduction to Markdown! ''' app.layout = html.Div([ dcc.Markdown(children=markdown_text) ])
其它一些常用的UI部件
app.layout = html.Div([ html.Label('Dropdown'), dcc.Dropdown( options=[ {'label': 'New York City', 'value': 'NYC'}, {'label': u'Montréal', 'value': 'MTL'}, {'label': 'San Francisco', 'value': 'SF'} ], value='MTL' ), html.Label('Multi-Select Dropdown'), dcc.Dropdown( options=[ {'label': 'New York City', 'value': 'NYC'}, {'label': u'Montréal', 'value': 'MTL'}, {'label': 'San Francisco', 'value': 'SF'} ], value=['MTL', 'SF'], multi=True ), html.Label('Radio Items'), dcc.RadioItems( options=[ {'label': 'New York City', 'value': 'NYC'}, {'label': u'Montréal', 'value': 'MTL'}, {'label': 'San Francisco', 'value': 'SF'} ], value='MTL' ), html.Label('Checkboxes'), dcc.Checklist( options=[ {'label': 'New York City', 'value': 'NYC'}, {'label': u'Montréal', 'value': 'MTL'}, {'label': 'San Francisco', 'value': 'SF'} ], value=['MTL', 'SF'] ), html.Label('Text Input'), dcc.Input(value='MTL', type='text'), html.Label('Slider'), dcc.Slider( min=0, max=9, marks={i: 'Label {}'.format(i) if i == 1 else str(i) for i in range(1, 6)}, value=5, ), ], style={'columnCount': 2})