【Dash搭建可视化网站】项目6:利用Dash callback高级特性实现货币汇率计算

手动反爬虫,禁止转载:原博地址 https://blog.csdn.net/lys_828/article/details/122088723(CSDN博主:Be_melting)

 知识梳理不易,请尊重劳动成果,文章仅发布在CSDN网站上,在其他网站看到该博文均属于未经作者授权的恶意爬取信息

项目6:利用Dash callback高级特性实现货币汇率计算

之前的项目3,4,5使用的callback都是一对一的数据流向关系,项目6要实现的就是一个输入多个输出的关系,并且尝试进行网页的美化操作。以一个货币汇率计算的小工具为例:用户输入一个值,自动转换出其它三个国家/地区的货币金额。

6.1 基础功能实现

[1] 新建一个py文件,搭建Dash框架,代码如下。xxx中就是待完善的部分。

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

app = dash.Dash()

app.layout = html.Div(children=[
    xxx
])

app.run_server(debug=True)

[2] 构建输入和输出流向实体。一个输入文本框,三个输出内容。

app.layout = html.Div(children=[
    dcc.Input(value=0,id='ipt'),
    html.Div(['JPY',html.Span('---',id='JPY')]),  
    html.Div(['USD',html.Span('---',id='USD')]),
    html.Div(['GBP',html.Span('---',id='GBP')])
])

[3] 简化代码。对于三个输出的内容,发现存在着大量重复,最直接的反应就是进行函数的封装,代码如下。

def item(name):
    return html.Div([name,html.Span('---',id=name)])
    
app.layout = html.Div(children=[
    dcc.Input(value=0,id='ipt'),
    item('JPY'),
    item('USD'),
    item('GBP')
])

[4] 构建数据输入和输出处理规则。注意是一对三,callback中就是对应的为一个输入三个输出,此外还要留意的就是赋值的参数。即便一个输入,对应的参数赋值是给inputs,但是就是有多个输出,赋值对应的参数也只是output。输入和输出都是放在列表中。

@app.callback(
    output = [Output(),Output(),Output()],
    inputs = [Input()]
)
def rule(rmb):
    return (
        '',
        '',
        ''
    )

接着完善上述代码中要填写的信息,流向实体中输入的id信息和传递的值分别是ipt和value,而对应输出的id信息和传递的值分别是各种货币名称及children(Div中返回的基本就是children),故代码完善如下。

@app.callback(
    output = [Output('JPY','children'),Output('USD','children'),Output('GBP','children')],
    inputs = [Input('ipt','value')]
)
def rule(rmb):
    return (
        f'{rmb*15.0}',
        f'{rmb*0.14}',
        f'{rmb*0.11}'
    )

直接保存后运行,刷新网址:http://127.0.0.1:8050/,输出结果如下。提示最后传递的数据没有办法进行相乘,需要对输出的数据类型进行判别后转化为数值,再进行操作。
请添加图片描述
[5] Bug调试。可以在return前面的一行加上print(type(rmb))语句,然后再次保存程序后运行,刷新网址:http://127.0.0.1:8050/,输出结果如下。
请添加图片描述
注意左侧的输出结果,首先网址打开后返回的是默认值0,数据类型为int整型,然后按退格键把0删除后,就是空值,这里按照是字符串数据类型,然后再输入12,两个数值都被当做了str字符串。可以在这里进行数据类型的判断以及空值的处理,但是有简单的方式就是在构建输入框的时候设定数据类型为数值型,进一步处理空值。

dcc.Input(value=0,id='ipt',type='number')
def rule(rmb):
    rmb = rmb if rmb is not None else 0
    return (
        f'{rmb*15.0}',
        f'{rmb*0.14}',
        f'{rmb*0.11}'
    )

修改完毕保存后运行,刷新网址:http://127.0.0.1:8050/,输出结果如下。解决了bug,而且输入框中由于限定了数值类型,其它字母什么都没有办法输入,相当于增加了容错,最后也可以正常的实现货币汇率的计算。
请添加图片描述
可以打开网页下方的调试按钮,查看一下callback的情况,对应的输入和输出就和设计的一模一样。
请添加图片描述

6.2 利用bootstrap组件美化货币汇率计算工具

前面的内容已经实现了基本的货币转化的逻辑输出,剩下的内容就是对内容进行美化处理。这部分两个核心知识点:(1)如何安装/加载Dash bootstrap components, (2)组件的使用技巧。

[1] 安装Dash bootstrap components。打开命令行执行安装语句:pip install dash-bootstrap-components。官网所在地址:http://dash-bootstrap-components.opensource.faculty.ai/。
请添加图片描述
关于bootstrap,其实就是在web开发届相当老牌的一个框架,官网网址:https://getbootstrap.com/docs/4.6/getting-started/introduction/。利用里面已经成熟的框架可以快速的搭建网站。而dash-bootstrap-components模块基本上把bootstrap里面的80%的components组件都给封装进去,直接使用python的语法就能实现网站的搭建。

[2] 加载bootstrap中的css样式。首先将css样式引入到当前的项目环境中,可以采用官方给的示例,使用代码的方式进行操作。

import dash_bootstrap_components as dbc
app = dash.Dash(
    external_stylesheets=[dbc.themes.BOOTSTRAP]
)

其实更好的方式:就是把对应的网址的bootstrap.min.css直接下载到项目中的assets文件夹下,就可以实现自动加载。css样式可选网址:https://cdnjs.com/。比如选择twitter-bootstrap的css样式。
请添加图片描述
搜索结果第一个点击进去后,跳转页面,复制红框中的样式。
请添加图片描述
打开浏览器在地址栏中粘贴刚刚的内容,回车后在页面中鼠标右键,选择另存为到assets文件夹中即可。
请添加图片描述
[3] 创建组件。打开dash-bootstrap-components官网所在地址:http://dash-bootstrap-components.opensource.faculty.ai/。找到想要创建的类型组件,比如这里把三种货币的输出结果以listgroup的形式展示出来,官方给的示例代码如下。
请添加图片描述
按照提示,在py文件中添加相应的代码,然后把list_group添加到app.layout中,代码如下。

import dash
from dash import html
from dash import dcc
from dash.dependencies import Input,Output
import dash_bootstrap_components as dbc

app = dash.Dash()

list_group = dbc.ListGroup(
    [
        dbc.ListGroupItem("Item 1"),
        dbc.ListGroupItem("Item 2"),
        dbc.ListGroupItem("Item 3"),
    ]
) 

app.layout = html.Div([list_group])

@app.callback(
    output = [Output('JPY','children'),Output('USD','children'),Output('GBP','children')],
    inputs = [Input('ipt','value')]
)
def rule(rmb):
    rmb = rmb if rmb is not None else 0
    return (
        f'{rmb*15.0}',
        f'{rmb*0.14}',
        f'{rmb*0.11}'
    )

app.run_server(debug=True)

修改完毕保存后运行,刷新网址:http://127.0.0.1:8050/,输出结果如下。实现了内容的添加,但是没有和要表达的信息进行联动,也就是没有指定id信息,所以报了4个错都是id not found。
请添加图片描述
[4] 修改默认组件中的内容。把input和output中的数据添加到组件中,形成完整的信息流。具体的内容就是一个输入三个输出。模仿6.1中步骤[2]和[3]中的代码,先尝试进行内容的输入输出,搭建好框架。

app.layout = html.Div(children=[
    dbc.ListGroup([
        dbc.Input(value=0,id='ipt',type='number'),
        dbc.ListGroupItem(['JPY: ',html.Span('--',id='JPY')]),
        dbc.ListGroupItem(['USD: ',html.Span('--',id='USD')]),
        dbc.ListGroupItem(['GBP: ',html.Span('--',id='GBP')]),
    ])
])

修改完毕保存后运行,刷新网址:http://127.0.0.1:8050/,输出结果如下。可以基本上实现要求,接下来就可以进行页面美化的操作以及代码简化。
请添加图片描述
[5] 代码简化与页面美化设计。先对重复的代码进行函数的封装,为了更清晰地展示前面三个字符代表的含义,可以添加对应的图像辅助理解,顺便巩固之前项目1中图片添加的知识点。

def item(name,img_path):
    return dbc.ListGroupItem([
        name,
        html.Img(src=img_path),
        html.Span('--',id=name)
    ])

app.layout = html.Div(children=[
    dbc.ListGroup([
        dbc.Input(value=0,id='ipt',type='number'),
        item('JPY','./assets/jp.png'),
        item('USD','./assets/usa.png'),
        item('GBP','./assets/uk.png'),
    ])
])

def rule(rmb):
    rmb = rmb if rmb is not None else 0
    return (
        f'{rmb*15.0} ¥',
        f'{rmb*0.14} $',
        f'{rmb*0.11} £',
    )

修改完毕保存后运行,刷新网址:http://127.0.0.1:8050/,输出结果如下。要显示的信息都显示出来了,接下来就是调整一下位置。
请添加图片描述
比如将国旗调整到对应货币简称字母的下面,换算的金额和对应的符号调整到右边。如果不指定标签直接给出添加的数据信息,比如这里的name,就是直接和下面的图片属于同一行,当指定了H5标签后就实现了自动换行。此外对应数据布置的问题,可以使用className进行设置,float本意就是飘、浮动的意思,所以float-right就是相当于是右对齐的感觉。

def item(name,img_path):
    return dbc.ListGroupItem([
        html.H5(name),
        html.Img(src=img_path),
        html.H5('--',id=name,className='float-right')
    ])

修改完毕保存后运行,刷新网址:http://127.0.0.1:8050/,输出结果如下。
请添加图片描述
[6] 头部信息的显示。还是参考官方的代码及输出样式,只需要添加一个active参数既可以完成。

app.layout = html.Div(children=[
    dbc.ListGroup([
        dbc.ListGroupItem(children=[
            html.H1("汇率计算器-简单版"),
            html.P('write something here'),
            dbc.Input(value=0,id='ipt',type='number'),
        ],active=True),
        item('JPY','./assets/jp.png'),
        item('USD','./assets/usa.png'),
        item('GBP','./assets/uk.png'),
    ])
])

但是这里面仍有需要留意的地方,将dbc.Input组件包装在dbc.ListGroupItem下面,里面是可以再添加html中的信息内容,比如这里的H1和P标签对应的信息。还有就是官网中给出的children只是单个的字符串数据,这里实际上可以添加多个元素,用列表传递。
请添加图片描述
修改完毕保存后运行,刷新网址:http://127.0.0.1:8050/,输出结果如下。
请添加图片描述
[7] 解决居中问题。只需要将原来的布局由html.Div修改为dbc.Container即可,由于默认是水平居中,所以对于组件的上方距离需要人为手动的进行调整,这个参数就是stlye中的padding,需要注意style不要添加位置错误。

app.layout = dbc.Container(children=[
    dbc.ListGroup([
        dbc.ListGroupItem(children=[
            html.H1("汇率计算器-简单版"),
            html.P('write something here'),
            dbc.Input(value=0,id='ipt',type='number'),
        ],active=True),
        item('JPY','./assets/jp.png'),
        item('USD','./assets/usa.png'),
        item('GBP','./assets/uk.png'),
    ],style={'padding':'3rem'})
])

修改完毕保存后运行,刷新网址:http://127.0.0.1:8050/,输出结果如下。
请添加图片描述
[8] 简单的UI设计,比如添加一个阴影效果。这种设计需要在官网中的Utilities中查找。除此之外还有很多可以自定义的小设计都可以在Utilities下进行查找。官网地址:https://getbootstrap.com/docs/4.6/utilities/shadows/。
请添加图片描述
具体在代码中的操作如下。就是对整个dbc.ListGroup进行属性的添加,注意添加的位置。

app.layout = dbc.Container(children=[
    dbc.ListGroup([
        dbc.ListGroupItem(children=[
            html.H1("汇率计算器-简单版"),
            html.P('write something here'),
            dbc.Input(value=0,id='ipt',type='number'),
        ],active=True),
        item('JPY','./assets/jp.png'),
        item('USD','./assets/usa.png'),
        item('GBP','./assets/uk.png'),
    ],style={'padding':'3rem'},className='shadow')  #只修改了这里
])

修改完毕保存后运行,刷新网址:http://127.0.0.1:8050/,输出结果如下。
请添加图片描述
仔细一点可以发现这里的阴影貌似并不是我们想要的效果,应该加在里面的内容部分,错误就出在了style的样式的添加,这里的style加在了dbc.ListGroup中,表示相对于dbc.Container来说的一个padding距离,而实际上应该是dbc.Container相对于整个html的padding距离,调整一下style的位置后代码如下。

app.layout = dbc.Container(children=[
    dbc.ListGroup([
        dbc.ListGroupItem(children=[
            html.H1("汇率计算器-简单版"),
            html.P('write something here'),
            dbc.Input(value=0,id='ipt',type='number'),
        ],active=True),
        item('JPY','./assets/jp.png'),
        item('USD','./assets/usa.png'),
        item('GBP','./assets/uk.png'),
    ],className='shadow')
],style={'padding':'3rem'})  #style添加在dbc.Container中

修改完毕保存后运行,刷新网址:http://127.0.0.1:8050/,输出结果如下。很完美达到目的效果。
请添加图片描述
至此整个项目6,利用Dash callback高级特性实现货币汇率计算全部过程就介绍完毕。核心知识点包含:callback一个输出对应多个输出的设计,Dash bootstrap components组件的使用,以及如何利用bootstrap实现页面的美化。