【Python】Dash简单介绍

Dash是一个基于FlaskPoltly.js以及React.js的Python框架,由plotly公司开发,设计之初是为了帮助数据分析人员进行快速的数据可视化网页搭建。现时Dash已经是一个相当成熟的框架,拥有丰富的社区资源与生态。

1、简单介绍

由于Dash是基于Flask框架建立的,其运行方式与执行逻辑和Flask是大同小异的。但Flask需要自己处理前后端的连接,且对前端后端知识有一定的了解,这对数据人员来说并不友好。Dash则将前端后端集成在一起,只需写简单的回调就能实现交互功能。

1.1 安装

python 复制代码
pip install dash

安装后即可构建一个Dash应用了!现在来创建一个小的Dash应用。

python 复制代码
# app.py
from dash import Dash, html

app = Dash(__name__)
app.layout = [html.Div('Hello world!')]

if __name__ == '__main__':
    app.run_server()

可以看到终端返回了一些信息,Dash应用在本地主机上运行,端口默认8050。在run_server()设置参数port可更改端口,可设置debug=True切换debug模式。

开启debug模式后每修改一次代码后Dash应用会自动重启更新,遇上错误后中断当前应用。

python 复制代码
# app.py
from dash import Dash, html

app = Dash(__name__)
app.layout = [html.Div('Hello world!')]

if __name__ == '__main__':
    app.run_server(debug=True)

开启debug模式后,Dash应用的右下方出现蓝色logo。展开logo会有三个按钮,第一个按钮展示Dash应用中的回调信息,包括回调关系、回调加载时间等;第二个按钮Errors反映的是程序错误信息,比如回调错误;第三个按钮Server则是显示当前Dash应用的运行状态,绿色表示正常运行,红色表示出错终止。

1.2 应用架构

Dash应用的架构如下:

markdown 复制代码
+ dashProject/
   + asset/
      + css/
      + img/
      + js/
       • favicon.ico
   + callbacks/
   + models/
   + views/
    • app.py
    • server.py
   + pages/  

文件夹assets 存放前端文件,callbacks 存放回调函数,models 存放数据模型文件,views 为视图文件, pages存放不同的页面文件。Dash自动读取相应的文件夹进行相应的操作,可以根据自己的实际需要对架构进行调整。

1.3 基本概念

Dash应用基本上是由两部分组成:布局(layout)和回调(callback)。布局负责页面的外观,回调则赋予了应用交互性。

1.3.1 布局

布局规定Dash页面的外观,规定哪一块区域具体放什么内容、怎么排版等。layout 由一棵"组件"树组成,利如 html.Divdcc.Graph

python 复制代码
app.layout = [
    html.Div('Hello world!'),
    html.Div('1'),
    html.Div('2'),
    html.Div('3'),
]

一个好看的网页往往通常需要编写css、js文件,但使用Dash元素的初衷不就是因为不熟悉不想写繁琐的前端代码吗,所以调用Dash的第三方拓展库dash_bootstrap_components就可以大大减少前端页面设计工作。dash-bootstrap-components不包含css,这是为了让您可以自由地选择任何的Bootstrap v5样式表,实现想要的外观。

python 复制代码
import dash_bootstrap_components as dbc
import dash


app = dash.Dash(
    __name__,
    # 用于引入外部的css,有了这部分网页才有更多样的形式
    external_stylesheets=[dbc.themes.BOOTSTRAP],
    # 可以填入css文件
    # external_stylesheets=['css/bootstrap.min.css'],
)

if __name__ == '__main__':
    app.run_server()

上面的准备工作完成以后,接下来开始学习构造页面布局。

  • Container、Row、Col

首先要了解的是组件Container,它是我们组织页面元素的容器。一个元素的尺寸和位置经常受其容器(Container)的影响。容器都被划分为四个区域,内容区(Content)、内边距区(Padding)、边框区(Margin)和外边框区(Border)。要修改Content、Padding、Margin、Border的样式,可设置style参数,见下面例子。

代码 样式

实际上,参数style对应的就是css文件,也可以通过写css文件对元素样式进行修改。在Container()之内,我们就可以按照bootstrap的网格系统进行内容的排布:行(Row) 嵌套列(Col) ,再向嵌套各种部件。Bootstrap提供了一套响应式、移动设备优先的流式网格系统,随着屏幕尺寸的增加,系统会自动分为最多 12 列。我们可以根据自己的需要定义列数,由于Bootstrap网格系统是响应式的, 列会根据屏幕大小自动重新排列,需要确保每一行中列的总和小于等于12且列数为正整数。

python 复制代码
app.layout = dbc.Container(
    [
        dbc.Row([
            dbc.Col(style={'background-color': 'lightgrey', 'height': 100}, width=1),
            dbc.Col(style={'background-color': 'red', 'height': 100}, width=2),
            dbc.Col(style={'background-color': 'blue', 'height': 100}, width=3),
            dbc.Col(style={'background-color': 'green', 'height': 100}, width=4),
            dbc.Col(style={'background-color': 'black', 'height': 100}, width=2),
        ]),
        dbc.Row([
            dbc.Col(style={'background-color': '#cbd933', 'height': 100}),
            dbc.Col(style={'background-color': '#6b6882', 'height': 100}),
        ]),
    ]
)
  • 设置水平对齐方式

在实际排版中,很多页面布局需求中需要对于同一行的多个列元素设置对齐方式 ,可以通过对Row()设置参数justify来实现,可选项有'start''center''end''between'以及'around'五种,每种产生的效果如下面的例子:

python 复制代码
app.layout = dbc.Container(
    [
        dbc.Row([
            dbc.Col('1', style={'border-color': 'black', 'border-style': 'solid'}, width=2),
            dbc.Col('2', style={'border-color': 'black', 'border-style': 'solid'}, width=2),
            dbc.Col('3', style={'border-color': 'black', 'border-style': 'solid'}, width=2),
        ], justify='start'),
        html.Hr(),
        dbc.Row([
            dbc.Col('1', style={'border-color': 'black', 'border-style': 'solid'}, width=2),
            dbc.Col('2', style={'border-color': 'black', 'border-style': 'solid'}, width=2),
            dbc.Col('3', style={'border-color': 'black', 'border-style': 'solid'}, width=2),
        ], justify='center'),
        html.Hr(),
        dbc.Row([
            dbc.Col('1', style={'border-color': 'black', 'border-style': 'solid'}, width=2),
            dbc.Col('2', style={'border-color': 'black', 'border-style': 'solid'}, width=2),
            dbc.Col('3', style={'border-color': 'black', 'border-style': 'solid'}, width=2),
        ], justify='end'),
        html.Hr(),
        dbc.Row([
            dbc.Col('1', style={'border-color': 'black', 'border-style': 'solid'}, width=2),
            dbc.Col('2', style={'border-color': 'black', 'border-style': 'solid'}, width=2),
            dbc.Col('3', style={'border-color': 'black', 'border-style': 'solid'}, width=2),
        ], justify='between'),
        html.Hr(),
        dbc.Row([
            dbc.Col('1', style={'border-color': 'black', 'border-style': 'solid'}, width=2),
            dbc.Col('2', style={'border-color': 'black', 'border-style': 'solid'}, width=2),
            dbc.Col('3', style={'border-color': 'black', 'border-style': 'solid'}, width=2),
        ], justify='around'),
    ]
)

1.3.2 回调

Dash最大的优点之一就是其高度封装了React.js,使得我们无需编写js代码即可实现前端与后端之间的异步交互,为了实现这一步,我们需要使用到dash.dependencies中的InputOutput,再配合自定义回调函数来实现所需交互功能。当输入组件的属性发生更改时,Dash会自动调用这些函数,以更新另一个组件(输出)中的某些属性。

python 复制代码
from dash import Dash, html, dcc, Input, Output, callback
import dash_bootstrap_components as dbc

app = Dash(__name__,
           external_stylesheets=[dbc.themes.BOOTSTRAP]
           )
app.layout = html.Div([
    html.H6("输入一些内容:"),
    html.Div([
        "输入: ",
        dcc.Input(id='my-input', type='text', placeholder='输入')
    ]),
    html.Br(),
    html.Div(id='my-output'),
])


@callback(
    Output(component_id='my-output', component_property='children'),
    Input(component_id='my-input', component_property='value')
)
def update_output_div(input_value):
    return f'输出: {input_value}'


if __name__ == '__main__':
    app.run_server(debug=True)

在 Dash 中,@callback被用于定义回调函数。@callback装饰器指定了回调函数update_output_div,当Input组件中的值发生变化它将被触发。Output定义了回调函数的输出 ,它指示输出到Output组件的属性;Iutput定义了回调函数的输入 ,表示回调函数接收来自属性的输入。参数component_id指的是元素的idcomponent_property指的是元素的属性。例子里面的Input(component_id='my-input', component_property='value'),即在id为my-input的组件处接收value;Output(component_id='my-output', component_property='children')即在id为my-output的组件处输出children。下面再举多个例子方便理解吧:

例1 在id为year-slider处接收value,在id为graph-with-slider处返回figure。图片将根据选择的年份进行更新。

python 复制代码
from dash import Dash, dcc, html, Input, Output, callback
import plotly.express as px

import pandas as pd

df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv')

app = Dash(__name__)

app.layout = html.Div([
    dcc.Graph(id='graph-with-slider'),
    dcc.Slider(
        df['year'].min(),
        df['year'].max(),
        step=None,
        value=df['year'].min(),
        marks={str(year): str(year) for year in df['year'].unique()},
        id='year-slider'
    )
])


@callback(
    Output('graph-with-slider', 'figure'),
    Input('year-slider', 'value'))
def update_figure(selected_year):
    filtered_df = df[df.year == selected_year]

    fig = px.scatter(filtered_df, x="gdpPercap", y="lifeExp",
                     size="pop", color="continent", hover_name="country",
                     log_x=True, size_max=55)

    fig.update_layout(transition_duration=500)

    return fig


if __name__ == '__main__':
    app.run_server(debug=True)

上面例子说明component_property可以是children、figure等,常用的属性有value、children、figure、n_clicks等。

> **例2** 编写一个dash应用,实现用户每点击一次按钮,更新点击按钮的最新时间。 > ```python > from dash import html, Dash, callback, Output, Input > from datetime import datetime > > > app = Dash( > name > ) > > app.layout = html.Div( > [ > html.Button(id='btn', children='点击'), > html.Div(id='click-time') > ] > ) > > > @callback( > Output('click-time', 'children'), > Input('btn', 'n_clicks') > ) > def update(n_clicks): > return '最近点击时间' + str(datetime.now()) > > > if name == 'main': > app.run_server() > ```

2 常用组件和元素

2.1 html组件

2.1.1 概要

在构建前端页面的过程中,可以使用dash下的html模块(dash.html),无需编写HTML或使用HTML模板引擎。下面是一个简单的html结构实例:

pyhton 复制代码
from dash import html

html.Div([
    html.H1('Hello Dash'),
    html.Div([
        html.P('Dash converts Python classes into HTML'),
        html.P("This conversion happens behind the scenes by Dash's JavaScript front-end")
    ])
])

以上代码会在您的web应用程序中转换为以下HTML:

html 复制代码
<div>
    <h1>Hello Dash</h1>
    <div>
        <p>Dash converts Python classes into HTML</p>
        <p>This conversion happens behind the scenes by Dash's JavaScript front-end</p>
    </div>
</div>

在HTML组件中,有style、class、id等属性用来调整页面风格,这些属性都能在dash中调整。HTML元素和Dash参数基本相同,但有一些关键区别:

  • style属性是一个dict
  • style中的属性采用骆驼命名法
  • HTML里的class,与dash里的className(或class_name)相对应
  • 像素单位的样式属性可以仅作为数字提供,而不需要像素单位px

下面给出一个直观的例子:

python 复制代码
from dash import html

html.Div([
    html.Div('Example Div', style={'color': 'blue', 'fontSize': 14}),
    html.P('Example P', className='my-class', id='my-p-element')
], style={'marginBottom': 50, 'marginTop': 25})

上述代码将对应下面的html:

html 复制代码
<div style="margin-bottom: 50px; margin-top: 25px;">

    <div style="color: blue; font-size: 14px">
        Example Div
    </div>

    <p class="my-class", id="my-p-element">
        Example P
    </p>

</div>

Dash的HTML组件有一个n_clicks属性,它是一个整数,表示元素被点击的次数。您可以使用n_clicks触发回调,并在回调逻辑中使用n_click的值。在下面例子中,我们从id为click-div的html.Div中捕获n_clicks值,并输出到id为click-output的html.P元素。n_clicks使用事件监听器来捕获元素上的用户点击事件,并递增n_clicks值。

python 复制代码
from dash import Dash, html, Input, Output, callback

app = Dash(__name__)

app.layout = html.Div(
    [
        html.Div(
            "Div with n_clicks event listener",
            id="click-div",
            style={"color": "red", "font-weight": "bold"},
        ),
        html.P(id="click-output"),
    ]
)


@callback(
    Output("click-output", "children"),
    Input("click-div", "n_clicks")
    )
def click_counter(n_clicks):
    return f"The html.Div above has been clicked this many times: {n_clicks}"


app.run(debug=True)

2.1.2 常用标签

由于不同类的属性名称定义都是类似的,除特殊属性外以下不再赘述(详细属性说明与用法见官方文档Dash for Python Documentation)。

①常用属性:

  • children: 此组件的子组件,可选
  • id: 组件的id,用于标识回调中的dash组件。id需要在应用程序中的所有组件中都是唯一的。
  • n_clicks: 一个整数,表示此元素被点击的次数,默认为0
  • href: 链接资源的url
  • media: 指定链接资源所针对的媒体的提示
  • className: 通常与css一起使用,为具有公共属性的元素设置样式
  • style: 定义css样式,这些样式将覆盖之前设置的样式
  • title: 将鼠标悬停在元素上时在工具提示中显示的文本

②常用标签:

dash组件 html标签 用法
html.A <a> <a> 标签定义超链接,用于从一个页面链接到另一个页面。<a> 元素最重要的属性是 href 属性,它指定链接的目标。
html.B <b> 用于定义粗体的文本
html.Br <br> 换行
html.Button <button> 定义一个按钮
html.Div <div> 定义 HTML 文档中的一个分隔区块或者一个区域部分。<div>标签常用于组合块级元素,以便通过 CSS 来对这些元素进行格式化。<div> 元素经常与 CSS 一起使用,用来布局网页。
html.Footer <footer> 定义文档或者文档的一部分区域的页脚
html.Form <form> 创建供用户输入的 HTML 表单
html.Header <header> 定义文档或者文档的一部分区域的页眉
html.H1-html.H6 <h1>-<h6> <h1> - <h6> 标签被用来定义 HTML 标题。<h1> 定义重要等级最高的标题。<h6> 定义重要等级最低的标题。
html.Hr <hr> 定义 HTML 页面中的主题变化(比如话题的转移),并显示为一条水平线
html.I <i> 斜体文本
html.Iframe <iframe> 规定一个内联框架。一个内联框架被用来在当前 HTML 文档中嵌入另一个文档。
html.Img <img> 定义 HTML 页面中的图像
html.Li <li> 定义列表项目
html.Nav <nav> 定义导航链接的部分
html.P <p> 定义段落
html.Progress <progress> 定义运行中的任务进度(进程)
html.Script <script> 定义客户端脚本
html.Span <span> 用于对文档中的行内元素进行组合
html.Table <table> 表格
html.Tbody <tbody> 用于组合 HTML 表格的主体内容
html.Td <td> 定义 HTML 表格中的标准单元格
html.Tfoot <tfoot> 用于组合 HTML 表格的页脚内容
html.Th <th> 定义 HTML 表格中的表头单元格
html.Thead <thead> 用于组合 HTML 表格的表头内容
html.Tr <tr> 定义 HTML 表格中的行
html.Ul <ul> 定义无序列表

2.1.3 一些应用

python 复制代码
import dash
from dash import html


app = dash.Dash(
    __name__,
)
app.layout = html.Div(
    [
        html.Header('Home'),
        html.H1('Good Morning!'),
        html.Button('点击', id='input-click'),
        html.Hr(),
        html.A('跳转百度', href='www.baidu.com'),
        html.Br(),
        html.Nav(
            html.Ol(
                [
                    html.Li('Bikes'),
                    html.Li('BMX'),
                ]
            ),
        ),
        html.Table(
            [
                html.Caption('Front-end web developer course 2021'),
                html.Thead(
                    [
                        html.Tr(
                            [
                                html.Th('Person', scope='col'),
                                html.Th('Most interest in', scope='col'),
                                html.Th('Age', scope='col'),
                            ]
                        ),
                    ]
                ),
                html.Tbody(
                    [
                        html.Tr(
                            [
                                html.Th('Chris', scope='row'),
                                html.Td('HTML tables'),
                                html.Td('22'),
                            ]
                        ),
                        html.Tr(
                            [
                                html.Th('Dennis', scope='row'),
                                html.Td('Web accessibility'),
                                html.Td('45'),
                            ]
                        ),
                        html.Tr(
                            [
                                html.Th('Sarah', scope='row'),
                                html.Td('Javascript frameworks'),
                                html.Td('22'),
                            ]
                        ),
                    ]
                ),
                html.Tfoot(
                    [
                        html.Tr(
                            [
                                html.Th('Average', scope='row', colSpan='2'),
                                html.Td('33'),
                            ]
                        ),
                    ]
                ),
            ]
        ),
    ]
)


if __name__ == '__main__':
    app.run(debug=True)

2.2 核心组件

2.2.1 概要

dash的核心组件模块(dash-core-component)封装了前端常用的交互式组件,包括下拉菜单、按钮、滑块等。引入方式:

python 复制代码
from dash import dcc
# import dash_core_component(旧的引入方式)

2.2.2 常用组件

① 选择框(dcc.CheckList)

② 日期选择(dcc.DatePickerSingle)

③ 下载(dcc.Download,常与Button一起使用)

python 复制代码
from dash import Dash, dcc, html, Input, Output, callback

app = Dash(__name__)
app.layout = html.Div([
    html.Button("Download Text", id="btn-download-txt"),
    dcc.Download(id="download-text")
])


@callback(
    Output("download-text", "data"),
    Input("btn-download-txt", "n_clicks"),
    prevent_initial_call=True,
)
def func(n_clicks):
    return dict(content="Hello world!", filename="hello.txt")


if __name__ == "__main__":
    app.run(debug=True)

④ 下拉菜单(dcc.Dropdown)

⑤ 图片(dcc.Graph)

Graph组件用于展示plotly图形,并用figure参数接收。

python 复制代码
    from dash import dcc, html, Dash
    import plotly.express as px


    app = Dash(
        __name__
    )
    server = app.server
    df = px.data.iris()
    fig = px.scatter(df, x='sepal_width', y='sepal_length')


    app.layout = html.Div(
        [
            dcc.Graph(figure=fig)
        ]
    )

    if __name__ == '__main__':
        app.run_server()

⑥ 滑块(dcc.Slider)

⑦ 分页(dcc.Tabs、dcc.Tab)

相关推荐
博观而约取38 分钟前
Django ORM 1. 创建模型(Model)
数据库·python·django
精灵vector2 小时前
构建专家级SQL Agent交互
python·aigc·ai编程
Zonda要好好学习2 小时前
Python入门Day2
开发语言·python
Vertira2 小时前
pdf 合并 python实现(已解决)
前端·python·pdf
太凉2 小时前
Python之 sorted() 函数的基本语法
python
项目題供诗3 小时前
黑马python(二十四)
开发语言·python
晓13133 小时前
OpenCV篇——项目(二)OCR文档扫描
人工智能·python·opencv·pycharm·ocr
是小王同学啊~3 小时前
(LangChain)RAG系统链路向量检索器之Retrievers(五)
python·算法·langchain
AIGC包拥它3 小时前
提示技术系列——链式提示
人工智能·python·langchain·prompt
孟陬3 小时前
Python matplotlib 如何**同时**展示正文和 emoji
python