Python的Web可视化框架Dash(8)---核心组件

【Dash系列】Python的Web可视化框架Dash(1)---简介
【Dash系列】Python的Web可视化框架Dash(2)---安装
【Dash系列】Python的Web可视化框架Dash(3)---布局设置
【Dash系列】Python的Web可视化框架Dash(4)---基本回调
【Dash系列】Python的Web可视化框架Dash(5)---状态和预更新
【Dash系列】Python的Web可视化框架Dash(6)---交互和过滤
【Dash系列】Python的Web可视化框架Dash(7)---回调共享
【Dash系列】Python的Web可视化框架Dash(8)---核心组件

本节介绍Dash应用的常用核心组件,导入本节用到的所有包
import pandas as pd
import plotly.graph_objs as go
import dash
import dash_core_components as dcc                  # 交互式组件
import dash_html_components as html                 # 代码转html
from dash.dependencies import Input, Output         # 回调
from jupyter_plotly_dash import JupyterDash         # Jupyter中的Dash


一、下拉列表

(一) 默认下拉列表

没有任何额外属性的默认下拉列表的示例
  • 代码
app = JupyterDash('dropdown01', height = 120)
app.layout = html.Div([
    dcc.Dropdown(
        id = 'my_dropdown',
        options = [{'label': '北京', 'value': '北京'}, 
                   {'label': '上海', 'value': '上海'}, 
                   {'label': '深圳', 'value': '深圳'}],
        value = '北京'),
    html.Div(id = 'output-01')
])

@app.callback(Output('output-01', 'children'), [Input('my_dropdown', 'value')])
def update_output(value):
    return f"您已经选择:【{value}】"

app
  • 效果图

(二) 多值下拉列表

multi 属性设置为True时,允许用户一次选择多个值
  • 代码
app = JupyterDash('dropdown01', height = 120)
app.layout = html.Div([
    dcc.Dropdown(
        id = 'my_dropdown',
        options = [{'label': '北京', 'value': '北京'}, 
                   {'label': '上海', 'value': '上海'}, 
                   {'label': '深圳', 'value': '深圳'}],
        value = ['北京', '上海'],
        multi = True),
    html.Div(id = 'output-01')
])

@app.callback(Output('output-01', 'children'), [Input('my_dropdown', 'value')])
def update_output(value):
    return f"您已经选择:【{'、'.join([val for val in value])}】"

app
  • 效果图

(三) 禁用搜索

searchable 属性,设置是否允许在下拉列表中搜索,默认为True,若要禁止搜索则需要设置为False。

  • 代码
app = JupyterDash('dropdown01', height = 120)
app.layout = html.Div([
    dcc.Dropdown(
        id = 'my_dropdown',
        options = [{'label': '北京', 'value': '北京'}, 
                   {'label': '上海', 'value': '上海'}, 
                   {'label': '深圳', 'value': '深圳'}],
        searchable=False),
    html.Div(id = 'output-01')
])

@app.callback(Output('output-01', 'children'), [Input('my_dropdown', 'value')])
def update_output(value):
    pass

app
  • 效果图

(四) 下拉清除

clearable 属性,设置是否可以清空下拉按钮的选值,默认为True,若要禁止清空则需要设置为False。

  • 代码
app = JupyterDash('dropdown01', height = 120)
app.layout = html.Div([
    dcc.Dropdown(
        id = 'my_dropdown',
        options = [{'label': '北京', 'value': '北京'}, 
                   {'label': '上海', 'value': '上海'}, 
                   {'label': '深圳', 'value': '深圳'}],
        value = '北京',
        clearable = False),
    html.Div(id = 'output-01')
])

@app.callback(Output('output-01', 'children'), [Input('my_dropdown', 'value')])
def update_output(value):
    return f"您已经选择:【{value}】"

app
  • 效果图

(五) 占位符文本

placeholder 属性,设置未选择任何值时,显示的默认文本。

  • 代码
app = JupyterDash('dropdown01', height = 120)
app.layout = html.Div([
    dcc.Dropdown(
        id = 'my_dropdown',
        options = [{'label': '北京', 'value': '北京'}, 
                   {'label': '上海', 'value': '上海'}, 
                   {'label': '深圳', 'value': '深圳'}],
        placeholder = '选择一个城市名称'),
    html.Div(id = 'output-01')
])

@app.callback(Output('output-01', 'children'), [Input('my_dropdown', 'value')])
def update_output(value):
    pass

app
  • 效果图

(六) 禁用下拉列表

通过设置 disabled=True 可以禁止选取下拉列表。

  • 代码
app = JupyterDash('dropdown01', height = 120)
app.layout = html.Div([
    dcc.Dropdown(
        id = 'my_dropdown',
        options = [{'label': '北京', 'value': '北京'}, 
                   {'label': '上海', 'value': '上海'}, 
                   {'label': '深圳', 'value': '深圳'}],
        disabled = True),
    html.Div(id = 'output-01')
])

@app.callback(Output('output-01', 'children'), [Input('my_dropdown', 'value')])
def update_output(value):
    pass

app
  • 效果图

(七) 禁用选项

在下拉菜单中,禁用特定的选项,需要在 options 属性下,设置 disabled = True

  • 代码
app = JupyterDash('dropdown01', height = 150)
app.layout = html.Div([
    dcc.Dropdown(
        id = 'my_dropdown',
        options = [{'label': '北京', 'value': '北京'}, 
                   {'label': '上海', 'value': '上海', 'disabled': True}, 
                   {'label': '深圳', 'value': '深圳'}],
        value = '北京'),
    html.Div(id = 'output-01')
])

@app.callback(Output('output-01', 'children'), [Input('my_dropdown', 'value')])
def update_output(value):
    return f"您已经选择:【{value}】"

app
  • 效果图


二、滑块

(一) 简单的滑块

绑定到回调的基本滑块的示例
  • 代码
app = JupyterDash('slider', height = 120)

app.layout = html.Div([
    dcc.Slider(
        id = 'my-slider',
        min = 0,
        max = 20,
        step = 0.5,
        value = 10),
    html.Div(id = 'slider-output-container')
])

@app.callback(Output('slider-output-container', 'children'), [Input('my-slider', 'value')])
def update_output(value):
    return f"你已选择到:{value}"

app
  • 效果图

(二) 标记和步骤

属性 marks ,字典类型,dict键为滑块的值、dict值为滑块的标签。如果用属性 marks 进行定义滑块,则必须将属性 step 设置为 None,否则仍然会按默认的 step = 1 进行滑动。
  • 代码
app = JupyterDash('slider', height = 120)
app.layout = html.Div([
    dcc.Slider(
        min = 0,
        max = 10,
        step = None,
        marks = {0: '0 °F', 3: '3 °F', 5: '5 °F', 7.65: '7.65 °F', 10: '10 °F'},
        value = 7.65
    )],
    style = dict(padding = '5px'),
)

app
  • 效果图

(三) 滑块突显和标记风格

默认情况下 included=True,表示突出显示选择的滑块区间。marks 属性下的子属性 style 可以设置标记风格。
  • 代码
app = JupyterDash('slider', height = 120)
app.add_external_link = False
app.layout = html.Div([
    dcc.Slider(
        min = 0, 
        max = 100,
        value = 65,
        included=False,
        marks = {
            0: {'label': '0 °C', 'style': {'color': '#77b0b1'}},
            26: {'label': '26 °C'},
            37: {'label': '37 °C'},
            100: {'label': '100 °C', 'style': {'color': '#f50'}}
        })],
    style = dict(padding = '5px')
)

app
  • 效果图

(四) 非线性滑块和更新模式

设置 marks 属性的标签为对数,在回调中调整滑块的输出值,创建对数滑块。属性updatemode,为drag时表示每次移动句柄时都会触发回调,默认为mouseup,表示滑块释放鼠标时触发回调。
  • 代码
app = JupyterDash('slider', height = 120)
app.add_external_link = False
app.layout = html.Div([
    dcc.Slider(
        id = 'slider-updatemode',
        max = 3, 
        step = 0.01,
        value = 2,
        updatemode = 'drag',
        marks = {i: f"{str(10 ** i)}" for i in range(4)}),
    html.Div(id = 'updatemode-output-container', style = dict(marginTop = 20,))],
    style = dict(padding = '5px'))

@app.callback(Output('updatemode-output-container', 'children'), [Input('slider-updatemode', 'value')])
def display_value(value):
    return f"线性值:{value} | 对数值:{value ** 10}"

app
  • 效果图


三、范围滑块

(一) 简单的RangeSlider

绑定到回调的基本RangeSlider的示例
  • 代码
app = JupyterDash('range_slider', height = 120)
app.add_external_link = False
app.layout = html.Div([
    dcc.RangeSlider(
        id = 'my-range-slider',
        min = 0,
        max = 20,
        step = 0.5,
        value = [5, 15]),
    html.Div(id = 'output-container-range-slider')
])

@app.callback(Output('output-container-range-slider', 'children'), [Input('my-range-slider', 'value')])
def update_output(value):
    return f"你选择的范围:{value}"

app
  • 效果图

(二) 标记和步骤

marks定义了滑块,并将step其设置为None,滑块将只能选择已由其预定义的值marks。
  • 代码
app = JupyterDash('range_slider', height = 120)
app.add_external_link = False
app.layout = html.Div([
    dcc.RangeSlider(
        min = 0,
        max = 10,
        step = None,
        marks = {0: '0 °F', 3: '3 °F', 5: '5 °F', 7.65: '7.65 °F', 10: '10 °F'},
        value = [3, 7.65]
    )],
    style = dict(padding = '5px'),
)

app
  • 效果图

(三) 标记和步骤

marks定义了滑块,并将step其设置为None,滑块将只能选择已由其预定义的值marks。
  • 代码
app = JupyterDash('range_slider', height = 120)
app.add_external_link = False
app.layout = html.Div([
    dcc.RangeSlider(
        min = 0, 
        max = 100,
        value = [10, 65],
        included=False,
        marks = {
            0: {'label': '0 °C', 'style': {'color': '#77b0b1'}},
            26: {'label': '26 °C'},
            37: {'label': '37 °C'},
            100: {'label': '100 °C', 'style': {'color': '#f50'}}
        })],
    style = dict(padding = '5px')
)

app
  • 效果图

(四) 多手柄

要创建多个手柄,只需为其定义更多值value
  • 代码
app = JupyterDash('range_slider', height = 120)
app.add_external_link = False
app.layout = html.Div([
    dcc.RangeSlider(
        min=0,
        max=30,
        value=[1, 3, 4, 5, 12, 17])],
    style = dict(padding = '5px')
)

app
  • 效果图

(五) 可推动手柄

属性pushable属性可以是数值,也可以是布尔值。数值确定手柄之间的最小距离
  • 代码
app = JupyterDash('range_slider', height = 120)
app.add_external_link = False
app.layout = html.Div([
    dcc.RangeSlider(
        min=0,
        max=30,
        value=[8, 10, 15, 17, 20],
        pushable=2)],
    style = dict(padding = '5px')
)

app
  • 效果图

(六) 穿越手柄

如果allowCross=False,手柄将不允许相互交叉
  • 代码
app = JupyterDash('range_slider', height = 120)
app.add_external_link = False
app.layout = html.Div([
    dcc.RangeSlider(
        min=0,
        max=30,
        value=[10, 15],
        allowCross=False)],
    style = dict(padding = '5px')
)

app
  • 效果图


四、输入框

(一) 支持的输入类型

  • 代码
app = JupyterDash('input', height = 120)
app.add_external_link = False
allowed_types = ("text", "number", "password", "email", "search", "tel", "url", "range", "hidden")
app.layout = html.Div([
    dcc.Input(
        id = f"input_{t}",
        type = t,
        placeholder = f"input type {t}") for t in allowed_types]
    + [html.Div(id="out-all-types")])

@app.callback(Output("out-all-types", "children"), [Input(f"input_{t}", "value") for t in allowed_types])
def cb_render(*vals):
    return ' ~~ '.join((str(val) for val in vals if val))

app
  • 效果图

(二) 延迟显示输入的内容

  • 代码
app = JupyterDash('input', height = 150)
app.add_external_link = False
app.layout = html.Div([
    html.I('尝试分别在两个输入框中输入数据,观察回调中如何延迟显示'),
    html.Br(),
    html.Br(),
    html.I('第2个输入框输入内容后,按下Enter键或鼠标点击非操作区,即可显示内容'),
    html.Br(),
    html.Br(),
    dcc.Input(id = 'input1', type = 'text', placeholder = '北京'),
    dcc.Input(id = 'input2', type = 'text', placeholder = '上海', debounce=True),
    html.Div(id = 'output')
])

@app.callback(Output("output", "children"), [Input("input1", "value"), Input("input2", "value")])
def update_output(input1, input2):
    return f"输入框 1 输出:{input1},输入框 2 输出:{input2}"

app
  • 效果图

(三) 数字输入

设置 type 为 number时,仅接受输入整型数字(Integer),若需要输入 Float 时,需要在回调函数中额外添加。
  • 代码
app = JupyterDash('input', height = 150)
app.add_external_link = False
app.layout = html.Div([
    dcc.Input(id = 'df', type = 'number', placeholder = '不延迟显示'),
    dcc.Input(id = 'dt', type = 'number', debounce = True, placeholder = '延迟显示'),
    dcc.Input(id = 'range', type = 'number', placeholder = '范围', min = 10, max = 100, step = 5),
    html.Hr(),
    html.Div(id = 'number-out')
])

@app.callback(Output('number-out', 'children'), [Input('df', 'value'), Input('dt', 'value'), Input('range', 'value')])
def number_render(fval, tval, rangeval):
    return f"方框1: {fval}, 方框2: {tval}, 方框3: {rangeval}"

app
  • 效果图


五、选项卡

(一) 内容作为回调

将图表的内容通过回调,添加到Tab组件的children 属性中
  • 代码
app = JupyterDash('input', height = 600)
app.add_external_link = False
app.layout = html.Div([
    html.H2('选项卡示例', style = dict(textAlign='center')),
    dcc.Tabs(
        id = 'tabs-example',
        value = 'tab-1',
        children = [dcc.Tab(label = '选项卡一', value = 'tab-1'),
                    dcc.Tab(label = '选项卡二', value = 'tab-2')]),
    html.Div(id = 'tabs-demo')
])

@app.callback(Output('tabs-demo', 'children'), [Input('tabs-example', 'value')])
def render_content(tab):
    if tab == 'tab-1':
        return html.Div([
            html.H3('内容一'),
            dcc.Graph(
                id='graph-1-tabs',
                figure = dict(data = [dict(x = [1, 2, 3], y = [3, 1, 2], type = 'bar')])
            )
        ])
    else:
        return html.Div([
            html.H3('内容二'),
            dcc.Graph(
                id='graph-2-tabs',
                figure = dict(data = [dict(x = [1, 2, 3], y = [5, 10, 6], type = 'bar')])
            )
        ])

app
  • 效果图

(二) 内容为Tab子项

可以直接将图表的内容,作为children属性嵌入到Tab组件中
  • 代码
app = JupyterDash('input', height = 600)
app.add_external_link = False
app.layout = html.Div([
    dcc.Tabs(id = 'tabs', children = [
        dcc.Tab(label = 'tab-1', children = [
            html.Div([dcc.Graph(
                id = 'graph-1',
                figure = dict(data = [dict(x = [1, 2, 3], y = [4, 1, 2], type = 'bar', name = 'users'),
                                      dict(x = [1, 2, 3], y = [2, 4, 5], type = 'bar', name = 'orders')]))])]),
        dcc.Tab(label = 'tab-2', children = [
            html.Div([dcc.Graph(
                id = 'graph-2',
                figure = dict(data = [dict(x = [1, 2, 3], y = [1, 4, 1], type = 'bar', name = 'users'),
                                      dict(x = [1, 2, 3], y = [1, 2, 3], type = 'bar', name = 'orders')]))])]),
        dcc.Tab(label = 'tab-3', children = [
            html.Div([dcc.Graph(
                id = 'graph-3',
                figure = dict(data = [dict(x = [1, 2, 3], y = [2, 4, 3], type = 'bar', name = 'users'),
                                      dict(x = [1, 2, 3], y = [5, 4, 3], type = 'bar', name = 'orders')])
            )])
        ]),
    ])
])

app
  • 效果图


六、其它常用组件

(一) 多行文本

  • 代码
app = JupyterDash('input', height = 150)
app.add_external_link = False
app.layout = html.Div([
    dcc.Textarea(
    placeholder='请输入内容...',
    value='这是一个多行文本框的组件',
    style={'width': '100%'}) 
])

app
  • 效果图

(二) 复选框

  • 代码
app = JupyterDash('input', height = 150)
app.add_external_link = False
app.layout = html.Div([ 
    dcc.Checklist(
    options=[{'label': '北京', 'value': 'BJ'},
             {'label': '上海', 'value': 'SHH'},
             {'label': '深圳', 'value': 'SZ'}],
    value=['BJ', 'SZ']
    )
])

app
  • 效果图

(三) 单选框

绑定到回调的基本RangeSlider的示例
  • 代码
app = JupyterDash('input', height = 150)
app.add_external_link = False
app.layout = html.Div([ 
    dcc.RadioItems(
    options=[{'label': '北京', 'value': 'BJ'},
             {'label': '上海', 'value': 'SHH'},
             {'label': '深圳', 'value': 'SZ'}],
    value='BJ',
    )
])

app
  • 效果图

(四) 按钮

默认属性n_clicks是一个整数,表示点击按钮的次数,默认值为None;属性n_clicks_timestamp是整数时间戳,表示点击按钮的时间
  • 代码
app = JupyterDash('input', height = 120)
app.add_external_link = False
app.layout = html.Div([
    html.Button('Buttons', id = 'button'),
    html.Div(id = 'output-id')
])

@app.callback(Output('output-id', 'children'), [Input('button', 'n_clicks')])
def update_output(n_clicks):
    n_clicks = 0 if not n_clicks else n_clicks
    return f"你已点击按钮了{n_clicks}次"

app
  • 效果图

(五) 日期按钮

参数display_format设置日期格式;参数month_format设置日历中月格式;参数first_day_of_week设置周几为周第一天;参数clearable设置是否可以清楚选择
  • 代码
import arrow as ar
app = JupyterDash('input', height = 350)
app.add_external_link = False

app.layout = html.Div([
    dcc.DatePickerSingle(
        id='dt',
        date='2019-06-01',
        display_format = 'Y-MM-DD',
        month_format = 'Y-MM',
        first_day_of_week = 1,
        min_date_allowed=ar.get(2019, 1, 1).date(),
        max_date_allowed=ar.get(2019, 10, 1).date(),
        clearable=True),
    html.Div(id='output-dt')
])

@app.callback(Output('output-dt', 'children'), [Input('dt', 'date')])
def update_output(date):
    str_prefix = '您已选择 : ' if date else '暂无选择'
    str_dt = ar.get(date).format('YYYY-MM-DD') if date else ''
    return str_prefix + str_dt
    
app
  • 效果图

(六) 日期范围

  • 代码
import arrow as ar
app = JupyterDash('input', height = 350)
app.add_external_link = False
app.layout = html.Div([
    dcc.DatePickerRange(
        id='dt_range',
        start_date=ar.get(2019, 1, 1).date(),
        end_date_placeholder_text='选择日期',
        display_format = 'Y-MM-DD',
        month_format = 'Y-MM',
        first_day_of_week = 1,
        min_date_allowed=ar.get(2010, 1, 1).date(),
        max_date_allowed=ar.get(2020, 10, 1).date()
    ),
    html.Div(id='output-dt')
])

app
  • 效果图

(七) Markdown

Markdown是一种编写和格式化文本的简单方法。它包括粗体文本斜体链接,内联code 片段,列表,引号等语法。
  • 代码
app = JupyterDash('input', height = 350)
app.add_external_link = False

app.layout = html.Div([
  dcc.Markdown('''
    #### Dash and Markdown

    Dash supports [Markdown](http://commonmark.org/help).

    Markdown is a simple way to write and format text.
    It includes a syntax for things like **bold text** and *italics*,
    [links](http://commonmark.org/help), inline `code` snippets, lists,
    quotes, and more.
    ''')  
])

app
  • 效果图
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271