回调函数
了解回调函数的概念及其在 Dash 应用中实现用户交互的作用。
一个核心概念
在使用 Dash 构建出色的网页应用时,关键在于实现回调函数。回调函数通过增强用户交互行为,为我们的应用注入活力——我们可以根据用户的操作动态生成图表或任意类型的对象。本节课将探讨 Dash 回调的含义、如何提升用户交互性,以及它为何是区分 Dash 与 Plotly 的核心特性。我们还将深入分析回调的结构、@ 装饰器的用法,以及如何定义回调函数。
Dash 回调 是一种 Python 函数,当用户与 Dash 应用中的特定组件发生交互时,会自动执行。例如点击按钮或更改下拉菜单的值等操作,都会触发回调函数,进而更新指定的输出组件,展示新的数据或视觉内容。
虽然 Plotly 提供了强大的工具用于创建静态和交互式图表,但它无法构建能够响应用户输入的完整交互式网页应用。而 Dash 则通过回调机制,建立起用户操作与应用响应之间的直接联系。这极大地提升了用户交互体验,并支持开发出能根据用户输入实时更新的复杂网页应用。
回调的结构
一个 Dash 回调由两部分组成:@ 装饰器和回调函数本身。@ 装饰器用于定义输入与输出组件之间的关系,其语法包括 @app.callback 语句,后接 Output 和 Input 对象,分别指定需要更新的输出组件和触发回调的输入组件。
以下是回调结构的示例:
1@app.callback( 2 Output('output-component-id', 'property-to-update'), 3 [Input('input-component-id', 'property-that-triggers-callback')] 4)
上述代码片段展示了 Dash 回调的基本结构。@app.callback 装饰器将指定的输入组件与输出组件连接起来,建立它们之间的交互关系。下面我们来分解这个结构的各个组成部分:
-
Output('output-component-id', 'property-to-update'):定义要更新的输出组件及其具体属性。请将'output-component-id'替换为实际要更新的组件 ID,将'property-to-update'替换为需要更新的属性(如'children'、'value'或'figure')。 -
Input('input-component-id', 'property-that-triggers-callback'):定义触发回调的输入组件及其属性。请将'input-component-id'替换为实际触发回调的组件 ID,将'property-that-triggers-callback'替换为相关属性(如'value'、'n_clicks'或'selectedData')。
回调函数
接下来,在 @app.callback 装饰器下方定义回调函数:
1def update_output(input_value): 2 # 处理 input_value 并计算更新后的值 3 updated_value = input_value * 2 4 return updated_value
在 @ 装饰器之后,我们需要定义回调函数,该函数接收输入值作为参数,进行处理,并返回用于更新输出组件的新值。函数应使用标准的 Python def 关键字定义,其参数个数与顺序需与装饰器中指定的输入组件逐一对应(函数名可自行命名)。
输入与输出
在上述示例中,回调函数 update_output 接收输入值作为参数,对其进行处理(此处为乘以 2),然后返回更新后的值。该值随后被赋给输出组件的指定属性。
请注意,装饰器中输入组件的顺序和数量必须与回调函数参数的顺序和数量完全匹配。
我们可以通过一些简单的例子来练习输入与输出的使用,逐步熟悉其用法,为后续基于用户交互动态调整图表打下基础。这正是 Dash 强大的地方。
回调、布局与组件
在 Dash 中,组件、应用布局和回调函数之间存在密不可分的联系。三者的核心关联在于:回调函数可以修改应用布局中定义的 Dash 组件。这一过程对用户和开发者来说是高度抽象的,因为底层运行的是 JavaScript。接下来我们将通过一系列示例,更深入地理解 Dash 回调实现的语法和细节。
提示:输入输出顺序
你可能已经注意到,在回调函数装饰器中,按照最佳实践,应将 Output() 放在 Input() 之前,如下所示:
1@app.callback( 2 Output(...), 3 Input(...) 4) 5def do_task(the_input): 6 # 你的代码 7 return the_output
这种写法使代码更具可读性,也便于未来调试项目时查找问题。这是 Dash 社区论坛和官方文档中普遍推荐的做法。
下面我们来看几个不同场景下的回调使用示例:
下拉菜单回调到 HTML
在此示例中,用户可通过 Dash 核心组件的下拉菜单选择多种选项,所选内容将作为 div HTML 组件显示出来。我们按常规方式定义应用布局,包含标题(H1 标签)、dcc.Dropdown 和一个位于 Dash Bootstrap 容器内的 html.Div。设置 fluid=True 以适配不同屏幕尺寸。
在这个示例中,我们使用了一个包含以下内容的 Dash 回调:
- 一个
app.callback函数装饰器。 - 回调函数本身命名为
show_selected_option。
在装饰器中,我们可以放置至少一个 Input() 对象和至少一个 Output() 对象,供用户交互。本例中,我们根据用户在单个输入组件(即下拉菜单)中的选择,决定单个输出组件(即 div)的内容。
每个与用户交互相关的组件都必须赋予一个 id,因为在 Output() 和 Input() 中,我们需要传入 component_id,指向要调整的组件 ID。同时,还需在 Output() 和 Input() 中指定 component_property,即组件的属性。
本例中,我们修改的是 dcc.Dropdown 组件的 value 属性(ID 为 my_input),从而影响 html.Div 组件(ID 为 my_output)的 children 属性。
文本区域回调
在此示例中,我们实现一个功能:统计文本区域组件中的单词数量。应用初始化和布局遵循相同的模式。
我们沿用相同的模板构建应用。有一个用户可输入文字的文本框,因此我们在 Input() 中指定其 component_id='my_input',对应 dcc.Textarea 输入组件的 ID。由于我们希望将结果显示在 ID 为 my_output 的 div 组件中,因此在 Output() 中指定该组件的 children 属性作为输出内容。
回调中的日期处理
我们可以在回调函数中完成任何常规 Python 操作,常见用途之一是处理日期数据。
在此应用中,我们计算从某个特定日期至今经过的时间:
这里有一个日期选择器,用户可以选择一个日期。因此,我们在 Input() 中指定其 component_id='my_input' 和 component_property='date'。
由于我们希望将结果展示在 ID 为 my_output 的 div 组件中,因此在 Output() 中指定 component_id='my_output' 和 component_property='children'。
回调控制 HTML 图片标签
设计 Dash 应用时,嵌入图片是一项常用功能。我们知道,Dash 将 HTML 标签封装为函数形式,因此可以使用标准 HTML 图片标签,但通过 Dash 的方式实现。具体方法是使用 html.Img()。更重要的是,借助回调,我们可以让图片展示变得动态而非静态。
本次示例中,根据用户的选择动态显示图片。用户可以从多个国家中选择,系统将根据所选国家加载对应的图片。
在此示例中,输入来自下拉菜单的选中项,其 id="input",因此在 @app.callback 的 Input() 中,component_id='input'。由于用户可以改变下拉菜单的选中值,因此在 Input() 中设置 component_property="value"。
对于 @app.callback 中的 Output(),输出组件(即图片)的 id 为 output,因为我们希望动态更改加载的图片,所以将输出组件的属性设为 'src',即图片的源地址。