Github仓库https://github.com/CNFeffery/dash-master
dash通用网页组件库fac
的0.2.x
全新版本,为大家介绍了其具有的诸多实用特性功能,也吸引了很多对基于dash
的Python
全栈应用开发感兴趣的朋友,为了方便更多对dash
应用开发不甚了解的朋友快速入门,今天的文章中,我将通过简洁明了的内容带大家快速掌握dash
应用开发的必备基础知识😉。
1 环境搭建
dash
应用作为Python
项目,建议大家从一开始就养成好习惯,使用虚拟环境来构建我们的dash
应用运行所需环境,以我最常用的conda
为例,终端执行下列命令,创建名为dash-app-dev
,Python
版本为3.8
的虚拟环境:conda create -n dash-app-dev python=3.8 -y
激活该环境:
conda activate dash-app-dev
在该环境下使用
pip
安装必要依赖(dash
+fac
开发套件,以及用于开发阶段代码格式自动美化的autopep8
),这里为了国内下载加速,使用了阿里云镜像:pip install dash feffery-antd-components autopep8 -i https://mirrors.aliyun.com/pypi/simple/
至此我们就完成了标准
dash
应用所需Python
虚拟环境的搭建工作了。2 初始化项目
Python插件),我们先在本地某个位置新建示例工程目录
hello-dash
,接着在vscode中将此目录作为项目打开:app.py,即为我们本文演示用简单小应用的主文件,打开
app.py
后,在vscode右下角选择环境为我们先前创建的dash-app-dev
即可:3 dash应用基础结构
app.py之后,我们就可以开始编写
dash
应用代码了,一个dash
应用具有以下几个基本构成部分:3.1 相关包的导入
app.py文件开头导入本文示例应用所需的各个模块,具体如下:
import dash # dash应用核心 from dash import html # dash自带的原生html组件库 import feffery_antd_components as fac # fac通用组件库 from dash.dependencies import Input, Output, State # 用于构建应用交互功能的不同角色
3.2 实例化Dash(对象
接下来我们需要进行
Dash(
对象的实例化,其具有的其他功能参数我们在今后的文章中再分别作详细介绍:app = dash.Dash(__name__
3.3 为dash应用定义初始元素
在已实例化的
Dash(
对象app
的基础上,我们需要为其layout
属性进行赋值,作为我们的dash
应用被访问时,初始化加载的页面内容,layout
可赋值为单个组件或返回单个组件的函数,通常我们会直接将一个html.Div(
组件赋给它:app.layout = html.Div(
在此基础上,我们可以将赋给
app.layout
的html.Div(
组件作为最外层的容器,其他应用初始化时需要加载的更多元素,我们可以通过向下嵌套的方式传给html.Div(
的children
参数。dash组件的世界中,一个组件只要允许接受
children
参数,就可以为其嵌套传入单个组件,或由多个组件构成的列表,因为children
参数也是对应组件的第一个位置参数,所以我们可以像下面这样很方便的传入一些其他组件,这里以fac
中的警告提示组件为例,我们将dash
和fac
的版本信息传入其对应参数中:app.layout = html.Div( [ # 这里以fac中的警告提示组件为例 # 文档地址:https://fac.feffery.tech/AntdAlert fac.AntdAlert( message='Hello Dash!', description=f'当前应用dash版本:{dash.__version__} fac版本:{fac.__version__}', showIcon=True ]
3.4 启动应用
完成了上述过程后,我们先来启动一下当前的应用,在
app.py
末尾添加下列代码,其中debug=True
用于启用开发调试模式,这是我们在dash
应用开发阶段的好帮手,可以帮我们实现热重载、错误信息提示等便捷功能:if __name__ == '__main__': app.run(debug=True
接着在终端中切换到该项目根目录,也就是
app.py
所在的目录,在激活dash-app-dev
环境的前提下,执行命令python app.py
即可临时启动我们的dash
应用,应用默认运行在http://127.0.0.1:8050
中,我们按照提示在浏览器中访问即可:dash应用当前的样子了~
3.5 调整应用样式
dash应用跑起来了,但是样子着实简陋,在
dash
应用中针对组件元素的样式进行调整的方式有很多种,最直接的方式是通过对应组件的style
参数进行相关css
样式属性的设置,譬如我们可以为最外层的html.Div(
容器设置一定的内边距:debug=True模式,因此在调整代码后,按下
ctrl+s
保存app.py
最新变动后,浏览器中正在访问的dash
应用会自动化刷新,非常方便,可以看到,此时我们的应用已经有了内边距:3.6 基于回调函数实现交互功能
dash应用添加交互功能时,就需要用到
dash
中的核心概念——回调函数了,在回调函数眼中,每个具有唯一id
参数的组件的任意属性,都可以被编排为回调函数中的角色,我们书写回调函数的过程实际上就是在玩角色编排的游戏,在dash
中有Input
、Output
和State
三种角色,下面我们来举例说明它们各自的作用:@app.callback(对定义回调逻辑的函数进行装饰):
@app.callback(中编排的内容翻译成人话就是
id
为button-demo
的组件的nClicks
属性每次更新时,都会经过函数体内定义的逻辑将返回值更新到id
为button-demo-output
的组件的children
属性,于是乎便实现了下面动图展示的效果:Output角色进行输出更新也是可以的,譬如我们每次点击按钮时不仅更新按钮一侧的信息,还顺便弹出消息提示,就可以将代码修改为:
美中不足的是我们刚访问应用,并没有进行按钮点击时,回调函数自动就先执行了一遍,这是因为
dash
应用默认会在应用初始化时对所有的回调函数都自动执行一遍,不管其所编排的Input
角色是否更新,如果你不希望这种机制发生,那么在@app.callback(
中设置参数prevent_initial_call=True
即可:通过上面的简单例子,我们已经掌握了
dash
回调函数中Input
与Output
角色的作用,剩下的State
角色就比较特殊,不同于Input
那样可以通过监听目标组件的指定属性变化从而触发回调函数执行,State
角色用来在回调函数中提供辅助属性值,相当于每次回调函数因为某个Input
角色变化而被触发时,会捎带手把State
角色对应的属性值一并携带进回调函数中,起到辅助计算的作用。有了额外
State
角色的辅助,我们的应用交互效果就变成下面动图所示:dash中回调函数的基本写法——即在
@app.callback(
中按照Output
、Input
、State
的顺序依次编排角色,且回调函数输入参数(参数名随意)与已编排的Input
、State
角色顺序一致即可~3.7 更复杂的应用示例
dash应用最基础概念后,下面我们就可以尝试编写更复杂的交互应用场景,譬如下面的简单例子,我们在页面中放置了若干表单输入类组件,配合
fac.AntdForm(
和fac.AntdFormItem(
进行表单的快捷构建,并通过回调函数与下方的表格实现联动筛选(以pandas
数据框为例),效果如下:pandas:
# 相关包的导入 import dash # dash应用核心 import pandas as pd from dash import html # dash自带的原生html组件库 import feffery_antd_components as fac # fac通用组件库 from dash.dependencies import Input, Output, State # 用于构建应用交互功能的不同角色 # 实例化Dash(对象 app = dash.Dash(__name__ # 创建示例表格 demo_df = pd.DataFrame( { '字段1': [f'类别{i}' for i in range(1, 11], '字段2': [10*i for i in range(10], '字段3': [(pd.Timestamp('2023-01-01' + pd.Timedelta(days=i.strftime('%Y-%m-%d' for i in range(10] } # 为dash应用定义初始元素 app.layout = html.Div( [ # 这里以fac中的警告提示组件为例 # 文档地址:https://fac.feffery.tech/AntdAlert fac.AntdAlert( message='Hello Dash!', description=f'当前应用dash版本:{dash.__version__} fac版本:{fac.__version__}', showIcon=True , # 放置水平分割虚线 fac.AntdDivider(isDashed=True, fac.AntdForm( [ fac.AntdFormItem( fac.AntdSelect( id='field1-range', options=[ { 'label': x, 'value': x } for x in demo_df['字段1'].unique( ], mode='multiple', maxTagCount='responsive', style={ 'width': 200 } , label='字段1' , fac.AntdFormItem( fac.AntdSlider( id='field2-range', min=0, max=100, range=True, defaultValue=[0, 100], style={ 'width': 150 } , label='字段2' , fac.AntdFormItem( fac.AntdDateRangePicker( id='field3-range', defaultPickerValue=demo_df['字段3'].min(, style={ 'width': 200 } , label='字段3' , fac.AntdButton( '查询', id='execute-query', icon=fac.AntdIcon( icon='antd-search' , type='primary' ], layout='inline', style={ 'marginBottom': 15 } , html.Div(id='table-result-container' ], style={ # 这里基于css中的padding参数,设置上下内边距50像素,左右内边距100像素 'padding': '50px 100px' } @app.callback( Output('table-result-container', 'children', Input('execute-query', 'nClicks', [State('field1-range', 'value', State('field2-range', 'value', State('field3-range', 'value'] def query_table(nClicks, field1_range, field2_range, field3_range: demo_df_copy = demo_df.copy( if field1_range: demo_df_copy.query('字段1 == @field1_range', inplace=True if field2_range: demo_df_copy.query(f'{field2_range[0]} <= 字段2 <= {field2_range[1]}', inplace=True if field3_range: demo_df_copy.query(f'"{field3_range[0]}" <= 字段3 <= "{field3_range[1]}"', inplace=True if not demo_df_copy.empty: return fac.AntdTable( columns=[ { 'title': column, 'dataIndex': column } for column in demo_df_copy.columns ], data=demo_df_copy.to_dict('records', bordered=True # 否则返回无匹配数据提示 return fac.AntdEmpty( description='当前条件组合下无匹配数据' if __name__ == '__main__': app.run(debug=True
以上就是本文的全部内容,更多有关
dash
应用开发的前沿知识和技巧欢迎持续关注玩转dash公众号。