Dash by Plotly 学习笔记
一、介绍
1、dash 是什么
dash 是一个基于 Flask (Python) + React 的 web 框架。
二、安装
1、安装
pip install dash==0.39.0 # The core dash backend
pip install dash-daq==0.1.0 # DAQ components (newly open-sourced!)
2、启动
$ python app.py
三、使用
1、基本框架
import dash
# init dash
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
# create layout
app.layout = html.Div(children=[
# ……
]),
if __name__ == '__main__':
# debug=True 激活所有开发工具,可选参数参考(https://dash.plot.ly/devtools)
app.run_server(debug=True,dev_tools=)
2、组件
Declarative UI —— 声明式 UI
(1)html 元素
import dash_html_components as html
class/style 跟 react 的写法规范差不多。
# html.H1(children='Hello Dash') = html.H1('Hello Dash')
html.H1('Hello Dash'),
html.H1(id="h1-element", className='h1', children='Hello Dash',
style={'textAlign': 'center', 'color': '#7FDBFF'}),
布局
# 嵌套
html.Div(children=[
html.Div('111'),
html.Div('222'),
html.Div('333'),
]),
# 12网格布局
html.Div(className='row', children=[
html.Div('111', className='four columns'),
html.Div('222', className='four columns'),
html.Div('333', className='four columns'),
]),
# 更多预设 className 可以查看: https://codepen.io/chriddyp/pen/bWLwgP.css
# tips:className='four columns' 其实是三列,className='three columns' 其实是四列,意思是用 12 除以它。
(2)python 封装的组件
import dash_core_components as dcc
import plotly.graph_objs as go
包括但不限于 单选框、下拉框、Markdown、Store、Graphs……,更多组件https://dash.plot.ly/dash-core-components
# draw Graph
dcc.Graph(
id='cluster_count',
figure={
'data': [go.Bar(
x=[1,2,3],
y=[100,200,300],
text=['100个','200个','300个'],
textposition='auto',
)],
'layout': go.Layout(
)
},
),
# figure 属性下有 ”data“+"layout" 属性
它基于plotly.py画图库 https://plot.ly/python/,画图库又基于SVG和WebGL (默认为 SVG,对大型数据集会切换到WebGL)。
# 组件用法帮助
>>> help(dcc.Dropdown)
loading 组件
dcc.Loading执行回调的过程中会显示type属性设置的 loading 图标, 而回调完成后会显示children里的内容。
dcc.Loading(id="loading-1", children=[html.Div(id="loading-output-1")], type="default"),
dcc.Input(id="input-1", value='1'),
@app.callback(Output("loading-output-1", "children"), [Input("input-1", "value")])
def input_triggers_spinner(value):
time.sleep(1)
return value
这里的回调
@app.callback在下面会有介绍。
Interval 组件
dcc.Interval,interval设置间隔时间,n_intervals记录触发次数。
dcc.Interval(
id='interval-component',
interval=1*1000, # in milliseconds
n_intervals=0
)
@app.callback(Output('interval-component-div', 'children'),
[Input('interval-component', 'n_intervals')])
def update_metrics(n):
return "Already run {} times".format(n)
(3)创建自己的组件
略
3、回调
Reactive Programming —— 反应式编程
from dash.dependencies import Input, Output, State
dcc.Input(id='my-input', value='initial value', type='text'),
dcc.Input(id='my-input-2', value='initial value', type='text'),
html.Button(id='submit-button', n_clicks=0, children='Submit'),
html.Div(id='my-div'),
html.Div(id='my-div-2')
@app.callback(
[Output(component_id='my-div', component_property='children'),
Output(component_id='my-div-2', component_property='children')
],
# 简写形式(省略 component_id + component_property)
[Input('submit-button', 'n_clicks')],
[State(component_id='my-input', component_property='value'),
State(component_id='my-input-2', component_property='value')
]
)
def update_output_div(n_clicks, input_value, input_value_2):
return 'You\'ve entered "{}" and "{}"'.format(input_value, input_value_2), 'You\'ve click : "{}" times'.format(n_clicks)
注意事项(坑)
(1)每一个 callback 都会发送一个请求。
(2)所有回调里的 Input 都必须有Output。
(3)同一个组件可以出现在多个回调的Input;但是在 Output 上,只能出现在一处回调里。
(4)不支持组件的双向绑定。
@app.callback(
Output('my-id-2', 'value'),
[Input('my-id', 'value')])
def aaa(data):
print("11:"+data)
return data
@app.callback(
Output('my-id', 'value'),
[Input('my-id-2', 'value')])
def bbb(data):
print("22:"+data)
return data
官方说这个 feature 暂时没有解决,具体时间未知。详细讨论如下:
https://community.plot.ly/t/interdependent-components/8299/4
(5)在回调中共享数据
dash 有个原则就是Callbacks绝不能修改其范围之外的变量。
但我们有时候需要用到共享数据,比如引入缓存机制来提高性能,这个时候应该怎么办呢?
方法一:存到当前用户的浏览器会话中(例如隐藏 DIV)
方法二:存到服务器磁盘上(例如文件或数据库)
方法三:存到服务器内存上(例如Redis)
持久化需要把数据序列化( 比如 toJSON() ),对于复杂的数据,推荐使用
apache arrow。
其中方法二和三可以使用Flask-Caching来实现。
pip install Flask-Caching
实现回调的缓存机制:
from flask_caching import Cache
cache = Cache(app.server, config={
'CACHE_TYPE': 'filesystem',
'CACHE_DIR': 'cache-directory'
})
# use redis
# 'CACHE_TYPE': 'redis',
# 'CACHE_REDIS_URL': os.environ.get('REDIS_URL', '')
html.Div(id='flask-cache-memoized-children'),
dcc.RadioItems(
id='flask-cache-memoized-dropdown',
options=[
{'label': 'Option {}'.format(i), 'value': 'Option {}'.format(i)}
for i in range(1, 4)
],
value='Option 1'
),
html.Div('Results are cached for {} seconds'.format(TIMEOUT)),
@app.callback(
Output('flask-cache-memoized-children', 'children'),
[Input('flask-cache-memoized-dropdown', 'value')])
@cache.memoize(timeout=60) # in seconds
def render(value):
return 'Selected "{}" at "{}"'.format(
value, datetime.datetime.now().strftime('%H:%M:%S')
)

4、交互
只适用于dash_core_components
目前只支持四种交互方式: hoverData, clickData, selectedData, relayoutData。
@app.callback(
Output('click-data', 'children'),
[Input('cluster_count', 'clickData')])
def display_click_data(clickData):
return json.dumps(clickData, indent=2)
注意事项(坑)
(1)回调时的形参不能随意指定
@app.callback(
Output('hover-data', 'children'),
[Input('cluster_count', 'hoverData')])
# tips: 这里形参的hoverData不可以随便指定其他称呼,否则会获取不到值
def display_hover_data(hoverData):
return json.dumps(hoverData, indent=2)
(2)暂不支持设置悬停和点击时的样式修改。
5、CSS & JS (& Header )
(1)外部文件(需手动引入)
# external_stylesheets
external_stylesheets = [
'https://codepen.io/chriddyp/pen/bWLwgP.css',
{
'href': 'https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css',
'rel': 'stylesheet',
'integrity': 'sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO',
'crossorigin': 'anonymous'
}
]
# external_scripts
external_scripts = [
'https://www.google-analytics.com/analytics.js',
{'src': 'https://cdn.polyfill.io/v2/polyfill.min.js'},
{
'src': 'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.core.js',
'integrity': 'sha256-Qqd/EfdABZUcAxjOkMi8eGEivtdTkh3b65xCZL4qAQA=',
'crossorigin': 'anonymous'
}
]
meta_tags = [
{
'name': 'description',
'content': 'My description'
},
{
'http-equiv': 'X-UA-Compatible',
'content': 'IE=edge'
}
]
# init dash
app = dash.Dash(__name__,
external_stylesheets=external_stylesheets,
external_scripts=external_scripts,
meta_tags=meta_tags
)
(2)CDN文件(需手动引入)
app = dash.Dash(__name__,
assets_external_path='http://your-external-assets-folder-url/'
)
app.scripts.config.serve_locally = False
(3) 本地文件(自动引入)
在根目录创建assets文件夹:
# 可以改这个设置
app.config.assets_folder = 'assets'
A、里面的 CSS/JS 文件会自动引入
B、IMG 图片需要这样加载html.Img(src='/assets/image.png'):
app.py
- assets/
|-- typography.css
|-- header.css
|-- custom-script.js
|-- image.png
6、路由
# ------------ 先定义 dcc.Location ,它的 pathname 属性会实时记录当前 url 的值 ------------
dcc.Location(id='url', refresh=False), # 没有任何显示作用
# ------------ 改变 url ------------
# 会刷新页面
dcc.Link('Navigate to "/"', href='/'),
dcc.Link('Navigate to "/page-2"', href='/page-2'),
# 不会刷新页面
html.A(href='/page-3',children='Navigate to "/page-3"'),
# ------------ 改变 url 的 回调 ------------
@app.callback(dash.dependencies.Output('page-content', 'children'),
[dash.dependencies.Input('url', 'pathname')])
def display_page(pathname):
return html.Div([
html.H3('You are on page {}'.format(pathname))
])
构建多页面应用程序
略
注意事项 (坑)
如果我们通过不同的 url去渲染不同的页面的话,会碰到一个问题,就是我们可能会率先定义了回调,而回掉中的组件暂时还没渲染到app.layout中,因此Dash会引发异常以警告我们可能做错了。在这种情况下,我们可以通过设置忽略该异常。即:
app.config.suppress_callback_exceptions = True
7、Auth
(1)HTTP Basic Auth
pip install dash-auth

import dash_auth
# Keep this out of source code repository - save in a file or a database
VALID_USERNAME_PASSWORD_PAIRS = [
['hello', 'world']
]
auth = dash_auth.BasicAuth(
app,
VALID_USERNAME_PASSWORD_PAIRS
)
app.scripts.config.serve_locally = True
(2)Plotly OAuth (需要付费)
略
8、部署
(1) 用 <iframe> 内嵌
(2)Flask 嵌入 Dash
略
(3)Dash 嵌入 Flask
略
延展知识
什么是 WSGI?
WSGI是仅针对 python 的 Web服务器网关接口(Python Web Server Gateway Interface)规范。
注意,只是规范,是 web服务器和 web应用 之间的接口规范。
WSGI 跟 CGI 的区别?
CGI 是通用网关接口的规范,并不限于 Python 语言。虽然早已过时,后来分别诞生了 python 领域的 WSGI 和 php 的 FastCGI(PHP-FPM)等。
python 的 web 框架都要遵循这个规范,比如Flask内置的 **wsgiref **或第三方的 Gunicorn 。 但前者 wsgiref 性能低下,所以推荐部署时选择 flask+Gunicorn+nginx 方案。
gunicorn --workers 6 --threads 2 app:server—workers 指定进程数
—threads 指定线程数
四、坑
1、gunicorn 部署导致页面交互进行不了
这个问题其实 github 上有人讨论(https://github.com/plotly/dash/issues/85),可惜没有有效的解决办法,我自己最后歪打正着了。
我的 dash 站点就只有单页,不过页面上有一些用 callback 实现的,用来做用户跟图表交互的功能,如图:

而我遇到的问题就是,当我用 gunicorn 启动项目的时候, 我跟图表的交互,时好时坏。(即点击切换单选按钮图表没有反应)。但我用 python3 直接启动 .py 时,却没有问题。
我去观察 chrome 浏览器 inspect 的 network tab, 发现http://127.0.0.1:8000/_dash-update-component这个请求,时而200,时而500,这就是它导致了我前端操作无响应。
但是我最终解决了这个问题。我发现只要把原有的启动命令从
gunicorn -w 4 app:server 变成 gunicorn app:server 就好了。
可是,为什么呢?
2、dash 强制引用外境CDN文件导致页面加载很慢
dash 会强制引用CDN 文件: plotly-1.44.3.min.js ,且不支持代码级别的修改,最可恶的是此文件加载速度很慢,所以我们要把他改到我们自己的 cdn 路径上。
方法为简单粗暴的改源码,步骤如下:
1、查看 dash-core-components 所在路径
pip3 show dash-core-components
2、打开例如 /home/.local/lib/python3.7/site-packages/dash_core_components/__init__.py 文件
3、修改此行: 'external_url': 'https://cdn.plot.ly/plotly-1.44.3.min.js' 为我们自己的 CDN 路径
参考资料
Dash by Plotly 学习笔记的更多相关文章
- alfs学习笔记-自动化构建lfs系统
我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! 一名linux爱好者,记录构建Linux From Scratch的过程 经博客园-骏马金龙前辈介绍,开始接触学习lfs,用博客 ...
- lfs(systemd版本)学习笔记-第1页
我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! 一名linux爱好者,记录构建Linux From Scratch的过程 经博客园-骏马金龙前辈介绍,开始接触学习lfs,用博客 ...
- lfs(systemv版本)学习笔记-第1页
我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! 一名linux爱好者,记录构建Linux From Scratch的过程 经博客园-骏马金龙前辈介绍,开始接触学习lfs,用博客 ...
- 学习笔记之Bokeh
Welcome to Bokeh — Bokeh 0.12.16 documentation https://bokeh.pydata.org/en/latest/ Bokeh is an inter ...
- 学习笔记之Data Science
Data science - Wikipedia https://en.wikipedia.org/wiki/Data_science Data science, also known as data ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
- 2014年暑假c#学习笔记目录
2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...
随机推荐
- 2019西湖论剑web wp
发在正文前 这应该是自己在安全圈摸爬滚打两年多以来第一次正规的ctf比赛.没解出flag,没截图,只提供了一些思路. 遥想往昔,初入大学,带着对PT的向往,一个人穿行在幽暗的图书馆,翻阅啃读一本本安全 ...
- Python_操作邮箱
脚本: from win32com.client import Dispatchimport datetime as dateimport datetimeimport reimport win32c ...
- Echarts 在动态HTML报告中的应用
# 参考官网 http://echarts.baidu.com/examples/ <scripts> <!--- echarts examples ---> </scr ...
- 检测2个url的不同之处(爬虫分析接口)
就是简单的检测2个url的不同之处,在做爬虫时,要分析接口地址的不同之处,靠自己的眼睛有点累,所以写了一个小程序,不喜勿喷 #测试数据 a = "https://list.tmall.com ...
- 如何让div弄成可以输入文字
如何让div弄成可以输入文字 加一个contenteditable="true"属性即可. <!DOCTYPE html> <html lang=" ...
- 初步了解学习flask轻量级框架,
关于flask我有话说 flask作为一个轻量级框架,它里面有好多扩展包需要下载,比较麻烦,而且有的时候flask需要在虚拟环境下运行,但是他的优点还是有滴 ,只要是用过Django的人,都会觉得fl ...
- ScrollView无法滚动
ScrollView视图无法滚动 箭头所指地方应该用dp单位的数值
- Effective Java -- 使可变性最小化
为了使类成为不可变的,应该遵循以下五条原则: 1. 不要提供任何会下盖对象状态的方法 2. 保证类不会被扩展 3. 使所有的域都是final的 4. 使所有的域都成为私有的 5. 确保对于任何可变组件 ...
- k8s storageClass对接nfs
前提:已存在一个nfs服务 192.168.137.11:/home/nfs_data 下面以rbac方式对接nfs 1.创建/root/k8s-nfs-rbac/serviceaccount.yam ...
- SpringCloud消息总线
我们在springcloud(七):配置中心svn示例和refresh中讲到,如果需要客户端获取到最新的配置信息需要执行refresh,我们可以利用webhook的机制每次提交代码发送请求来刷新客户端 ...