【Python百日基础系列】Day26 - Dash回调初识和组件

文章目录

视频讲解:

一、什么是Dash回调?

1.1 回调的作用

Dash应用程序通过Dash回调函数实现交互:当输入组件的属性发生变化时,自动调用编写好的Python函数。可以把多个回调函数链接起来,即UI输入组件中的一个变动,触发整个应用程序中的几个变动。

1.2 回调的实现

Dash通过装饰器函数来实现回调:应用程序接口Input和Ouput是通过app.callback装饰器声明的

二、带回调功能的组件

2.1 按钮

import dash
from dash import dcc
from dash import html

# css样式表无法下载,返回403错误
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(children=[
    html.Div(dcc.Input(id='input-box', type='text')),
    html.Button('提交', id='button'),
    html.Div(id='output-container-button',
             children='输入完毕,请点击提交按钮。')
])


@app.callback(
    dash.dependencies.Output('output-container-button', 'children'),
    [dash.dependencies.Input('button', 'n_clicks')],
    [dash.dependencies.State('input-box', 'value')])
def update_html(n_clicks, values):
    print(values, type(values))     # 你好吗 <class 'str'>
    print(n_clicks, type(n_clicks)) # 1 <class 'int'>
    return f'输入内容:"{values}" ,提交了 {n_clicks} 次'


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

【Python百日基础系列】Day26 - Dash回调初识和组件

2.2 上传组件1:解析 CSV 或 Excel 文件并将结果显示在表格中

import base64
import datetime
import io

import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output, State
import dash_table

import pandas as pd

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
    dcc.Upload(
        id='upload-data',
        children=html.Div([
            '请将Excel或CSV文件拖放到此处,或 ',
            html.A('选择文件')
        ]),
        style={
            'width': '100%',        # 宽度
            'height': '60px',       # 高度
            'lineHeight': '60px',   # 线高
            'borderWidth': '1px',   # 边框宽度
            'borderStyle': 'dashed', # 边框样式
            'borderRadius': '5px',  # 边框半径
            'textAlign': 'center',  # 文字居中
            'margin': '10px'        # 外边距
        },
        # 允许同时上传多个文件
        multiple=True
    ),
    html.Div(id='output-data-upload'),
])

def parse_contents(contents, filename, date):
    content_type, content_string = contents.split(',')

    decoded = base64.b64decode(content_string)
    try:
        if 'csv' in filename:
            # 上传了一个CSV文件
            df = pd.read_csv(
                io.StringIO(decoded.decode('utf-8')))
        elif 'xls' in filename:
            # 上传了一个Excel文件
            df = pd.read_excel(io.BytesIO(decoded))
    except Exception as e:
        print(e)
        return html.Div([
            'Sorry,处理文件出错。'
        ])

    return html.Div([
        html.H5(filename),
        # 从时间戳转化为日期时间字符串
        html.H6(datetime.datetime.fromtimestamp(date)),

        dash_table.DataTable(
            data=df.to_dict('records'),
            columns=[{'name': i, 'id': i} for i in df.columns]
        ),

        html.Hr(),  # 分割线

        # 为了测试,通过浏览器显示原始数据
        html.Div('原始数据'),
        html.Pre(contents[0:200] + '...', style={
            'whiteSpace': 'pre-wrap',
            'wordBreak': 'break-all'
        }),
        html.Pre(str(df.to_dict('records'))),
    ])

@app.callback(Output('output-data-upload', 'children'),
              Input('upload-data', 'contents'),
              State('upload-data', 'filename'),
              State('upload-data', 'last_modified'))
def update_output(list_of_contents, list_of_names, list_of_dates):
    """ 装饰器 Input  State  State 中的值分别传给本函数的三个参数 """
    if list_of_contents is not None:
        children = [
            parse_contents(c, n, d) for c, n, d in
            zip(list_of_contents, list_of_names, list_of_dates)]
        return children

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

【Python百日基础系列】Day26 - Dash回调初识和组件

2.3 上传组件2:上传图像并在页面显示

import datetime

import dash
from dash.dependencies import Input, Output, State
from dash import dcc
from dash import html

# css无法访问,无效
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

app.layout = html.Div([
    dcc.Upload(
        id='upload-image',
        children=html.Div([
            '拖放图像文件到此,或 ',
            html.A('选择文件。')
        ]),
        style={
            'width': '100%',
            'height': '60px',
            'lineHeight': '60px',
            'borderWidth': '1px',
            'borderStyle': 'dashed',
            'borderRadius': '5px',
            'textAlign': 'center',
            'margin': '10px'
        },
        # 允许同时上传多个文件
        multiple=True
    ),
    html.Div(id='output-image-upload'),
])

def parse_contents(contents, filename, date):
    return html.Div([
        html.H5(filename),
        html.H6(datetime.datetime.fromtimestamp(date)),
        # 在 Dash 上传模块的支持下,html.IMG可以直接显示图像文件
        html.Img(src=contents, width=600),
        html.Hr(),
    ])

@app.callback(Output('output-image-upload', 'children'),
              Input('upload-image', 'contents'),
              State('upload-image', 'filename'),
              State('upload-image', 'last_modified'))
def update_output(list_of_contents, list_of_names, list_of_dates):
    if list_of_contents is not None:
        children = [
            parse_contents(c, n, d) for c, n, d in
            zip(list_of_contents, list_of_names, list_of_dates)]
        return children

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

【Python百日基础系列】Day26 - Dash回调初识和组件

2.4 上传组件的三种使用方式

import dash
from dash import dcc
from dash import html

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div([
    dcc.Upload(html.Button('上传文件')),
    html.Hr(),
    dcc.Upload(html.A('上传文件')),
    html.Hr(),
    dcc.Upload([
        '拖放文件到此,或 ',
        html.A('选择一个文件')
    ], style={
        'width': '100%',
        'height': '60px',
        'lineHeight': '60px',
        'borderWidth': '1px',
        'borderStyle': 'dashed',
        'borderRadius': '5px',
        'textAlign': 'center'
    })
])

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

【Python百日基础系列】Day26 - Dash回调初识和组件

2.5 上传属性:help(dash.dcc.Upload)

children(列表或单个破折号组件,字符串或数字 | 字符串;可选):上传组件的内容。
id( string ; 可选): 该组件的 ID,用于标识回调中的 dash 组件。ID 需要在应用程序中的所有组件中都是唯一的。
accept(字符串;可选):允许特定类型的文件。有关更多信息,请参阅 https://github.com/okonet/attr-accept。

  • 请记住,mime 类型确定在跨平台上并不可靠。例如,CSV 文件在 macOS 下报告为 text/plain,但在 Windows 下报告为 application/vnd.ms-excel。在某些情况下,可能根本没有设置 MIME 类型。请参阅:https : //github.com/react-dropzone/react-dropzone/issues/276。
    className(字符串;可选):组件的 HTML 类名。
    className_active(字符串;可选):活动时组件的 HTML 类名。
    className_disabled(字符串;可选):组件的 HTML 类名称(如果禁用)。
    className_reject(字符串;可选):如果被拒绝,组件的 HTML 类名。
    contents(字符串 | 字符串列表;可选):上传文件的内容作为二进制字符串。
    disable_click( boolean ; default False): 不允许点击组件打开文件对话框。
    disabled( boolean ; default False): 完全启用/禁用上传组件。
    filename(字符串 | 字符串列表;可选):上传的文件的名称。请注意,这不包括文件的路径(出于安全原因)。
    last_modified( number | list of numbers ; optional): 上传文件的最后修改日期在unix时间(自1970年以来的秒数)。
    loading_state( dict ; 可选): 保存来自 dash-renderer 的加载状态对象的对象。
    loading_state 是一个带键的字典:
  • component_name(字符串;可选):保存正在加载的组件的名称。
  • is_loading(boolean ; 可选):确定组件是否正在加载。
  • prop_name(字符串;可选):保存正在加载的属性。
    max_size(数字;默认-1):最大文件大小(以字节为单位)。如果-1,则无穷大。
    min_size(数字;默认0):最小文件大小(以字节为单位)。
    multiple( boolean ; default False): 允许删除多个文件。
    style(dict ; 可选):要应用的 CSS 样式。
    style_active( dict ; default { borderStyle: ‘solid’, borderColor: ‘#6c6’, backgroundColor: ‘#eee’,}):活动时应用的 CSS 样式。
    style_disabled( dict ; default { opacity: 0.5,}): CSS 样式(如果禁用)。
    style_reject( dict ; default { borderStyle: ‘solid’, borderColor: ‘#c66’, backgroundColor: ‘#eee’,}): CSS 样式,如果被拒绝。

2.6 下载组件:下载文本

import dash
from dash.dependencies import Output, Input
from dash import html
from dash import dcc

app = dash.Dash(prevent_initial_callbacks=True)

app.layout = html.Div(children=
    [html.Button("下载文本", id="btn_txt"),
     dcc.Download(id="download-text-index")]
)


@app.callback(
    Output("download-text-index", "data"),
    Input("btn_txt", "n_clicks"))
def func(n_clicks):
    if n_clicks is None:
        raise dash.exceptions.PreventUpdate
    else:
        return dict(content="这是将要保存的文本内容。", filename="hello.txt")


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

【Python百日基础系列】Day26 - Dash回调初识和组件

2.7 下载组件:DataFrame下载为CSV

import dash
from dash.dependencies import Output, Input
from dash import html
from dash import dcc
import pandas as pd

app = dash.Dash(__name__)
app.layout = html.Div(
    [
        html.Button("下载CSV", id="btn_csv"),
        # 下载组件
        dcc.Download(id="download-dataframe-csv"),
    ]
)

df = pd.DataFrame({"a": [1, 2, 3, 4],
                   "b": [2, 1, 5, 6],
                   "c": ["x", "x", "y", "y"]})


@app.callback(
    Output("download-dataframe-csv", "data"),
    Input("btn_csv", "n_clicks"),
    prevent_initial_call=True,
)
def func(n_clicks):
    return dcc.send_data_frame(df.to_csv, "mydf.csv")


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

【Python百日基础系列】Day26 - Dash回调初识和组件

2.8 下载组件:DataFrame下载为Excel

import dash
from dash.dependencies import Output, Input
from dash import html
from dash import dcc
import pandas as pd

app = dash.Dash(__name__)
app.layout = html.Div(
    [
        html.Button("下载为Excel文件", id="btn_xlsx"),
        # 下载组件
        dcc.Download(id="download-dataframe-xlsx"),
    ]
)

df = pd.DataFrame({"a": [1, 2, 3, 4],
                   "b": [2, 1, 5, 6],
                   "c": ["x", "x", "y", "y"]})


@app.callback(
    Output("download-dataframe-xlsx", "data"),
    Input("btn_xlsx", "n_clicks"),
    prevent_initial_call=True,		# 阻止首次回调
)
def func(n_clicks):
    return dcc.send_data_frame(df.to_excel, "mydf.xlsx", sheet_name="Sheet_name_1")


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

【Python百日基础系列】Day26 - Dash回调初识和组件

2.9 下载组件:下载图像

import dash
from dash.dependencies import Output, Input
from dash import html
from dash import dcc

app = dash.Dash(__name__)
app.layout = html.Div([
    html.Button("下载图像", id="btn_image"),
    dcc.Download(id="download-image")
])


@app.callback(
    Output("download-image", "data"),
    Input("btn_image", "n_clicks"),
    prevent_initial_call=True,  # 阻止首次回调
)
def func(n_clicks):
    return dcc.send_file("QR-PyDataLab.jpg")


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

【Python百日基础系列】Day26 - Dash回调初识和组件

2.10 下载属性

help(dash.dcc.Download)

id( string ; 可选): 该组件的 ID,用于标识回调中的 dash 组件。
base64( boolean ; default False):base64 的默认值,在未设置为数据属性的一部分时使用。
data(dict ; 可选):更改时,调用下载。
data 是一个带键的字典:

  • base64(布尔值;可选):设置为 True,当数据采用 base64 编码时。
  • content(字符串;必需):文件内容。
  • filename( string ; required): 下载对话框中建议的文件名。
  • type(字符串;可选):Blob 类型,通常是 MIME 类型。
    type( string ; default ‘text/plain’): type 的默认值,在未设置为 data 属性的一部分时使用。
上一篇:每日一题-Day26-移动零


下一篇:Day26 打印三角形及Debug