飞道的博客

Python+Dash快速web应用开发:回调交互篇(上)

603人阅读  评论(0)


添加微信号"CNFeffery"加入技术交流群

本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes

1 简介

这是我的系列教程「Python+Dash快速web应用开发」的第三期,在前两期的教程中,我们围绕什么是Dash,以及如何配合方便好用的第三方拓展dash-bootstrap-components来为我们的Dash应用设计布局展开了非常详细的介绍。

Dash最吸引我的地方在于其高度封装了react.js,使得我们无需编写js语句,纯Python编程就可以实现浏览器前端与后端计算之间常规的异步通信,从而创造出功能强大的交互式web应用。

图1

从今天的文章开始,我就将开始带大家走进Dash的核心内容——「回调」

2 Dash中的基础回调

2.1 最基础的回调

Dash中的「回调」(callback)是以装饰器的形式,配合自编回调函数,实现前后端异步通信交互,这句话可能不太好理解,我们从一个简单的例子出发来认识Dash中的「回调」

app1.py


   
  1. import dash
  2. import dash_html_components as html
  3. import dash_bootstrap_components as dbc
  4. from dash.dependencies  import Input, Output
  5. app = dash.Dash(
  6.     __name__,
  7.     external_stylesheets=[ 'css/bootstrap.min.css']
  8. )
  9. app.layout = html.Div(
  10.     [
  11.         html.Br(),
  12.         html.Br(),
  13.         html.Br(),
  14.         dbc.Container(
  15.             [
  16.                 dbc.Row(
  17.                     [
  18.                         dbc.Col(dbc.Input(id= 'input-value',
  19.                                           placeholder= '请输入些东西'),
  20.                                 width= 12),
  21.                         dbc.Col(dbc.Label(id= 'output-value'),
  22.                                 width= 12)
  23.                     ]
  24.                 )
  25.             ]
  26.         )
  27.     ]
  28. )
  29. # 对应app实例的回调函数装饰器
  30. @app.callback(
  31.     Output( 'output-value''children'),
  32.     Input( 'input-value''value')
  33. )
  34. def input_to_output(input_value):
  35.      '' '
  36.     简单的回调函数
  37.     ' ''
  38.      return input_value
  39. if __name__ ==  '__main__':
  40.     app.run_server()

先来看看app1的交互效果:

图2

下面我们来分解上面的代码,梳理一下要构造一个具有实际交互功能的Dash应用需要做什么:

  • 「确定输入与输出部件」

一个可交互的系统一定是有「输入」「输出」的,我们开头导入的InputOutput对象,他们分别扮演着「输入者」「输出者」两种角色,其各自的第一个参数component_id用于联动前端部分定义的部件。

我们在前面定义前端部件时,为dbc.Input对应的输入框设置了id='input-value',为dbc.Label对应的文字输出设置了id='output-value',让它们作为第一个参数可以被Input()Output()唯一识别出来。

  • 「确定输入与输出内容」

在确定了「输入者」「输出者」之后,更重要的是为告诉Dash需要监听什么输入,响应什么输出,这就要用到第二个参数component_property

它与对应的前端部件有关,譬如我们的dbc.Input()输入框,其被输入的内容都存在value属性中,而children属性是dbc.Label以及绝大多数html部件的第一个参数,这样我们就确定了输入输出内容。

  • 「装饰回调函数」

app.callback()装饰器按照规定的先Output()Input()的顺序传入相应对象,而既然是装饰器,自然需要配合自定义回调函数使用。

我们的input_to_output()就是对应的回调函数,其参数与装饰器中的Input()对应,而函数内部则用来定义计算处理过程。

最后return的对象则对应Output()


   
  1. # 对应app实例的回调函数装饰器
  2. @app.callback(
  3.     Output( 'output-value''children'),
  4.     Input( 'input-value''value')
  5. )
  6. def input_to_output(input_value):
  7.      '' '
  8.     简单的回调函数
  9.     ' ''
  10.      return input_value

通过上面这样的结构,我们得以纯Python“寥寥数语”实现了交互功能,赋予我们编写任意功能Dash应用的能力。

2.2 同时设置多个Input()与Output()

在上一小节中我们介绍的是最基本的「单输入 -> 单输出」回调模式,很多时候我们需要更复杂的回调模式,譬如下面的例子:

app2.py


   
  1. import dash
  2. import dash_html_components as html
  3. import dash_bootstrap_components as dbc
  4. from dash.dependencies  import Input, Output
  5. app = dash.Dash(
  6.     __name__,
  7.     external_stylesheets=[ 'css/bootstrap.min.css']
  8. )
  9. app.layout = html.Div(
  10.     [
  11.         html.Br(),
  12.         html.Br(),
  13.         html.Br(),
  14.         dbc.Container(
  15.             [
  16.                 dbc.Row(
  17.                     [
  18.                         dbc.Col(dbc.Input(id= 'input-value1'), width= 3),
  19.                         dbc.Col(html.P( '+'), width= 1),
  20.                         dbc.Col(dbc.Input(id= 'input-value2'), width= 3),
  21.                     ],
  22.                     justify= 'start'
  23.                 ),
  24.                 html.Hr(),
  25.                 dbc.Label(id= 'output-value')
  26.             ]
  27.         )
  28.     ]
  29. )
  30. @app.callback(
  31.     Output( 'output-value''children'),
  32.     Input( 'input-value1''value'),
  33.     Input( 'input-value2''value')
  34. )
  35. def input_to_output(input_value1, input_value2):
  36.     try:
  37.          return float(input_value1) + float(input_value2)
  38.     except:
  39.          return  '请输入合法参数!'
  40. if __name__ ==  '__main__':
  41.     app.run_server()
图3

这里我们的Input()对象不止一个,在Output()对象之后依次传入(也可以把所有Input()对象包在一个列表中传入),其顺序对应后面回调函数的参数顺序,从而实现了多个输入值的一一对应。

同样的,Output()也可以有多个:

app3.py


   
  1. import dash
  2. import dash_html_components as html
  3. import dash_bootstrap_components as dbc
  4. from dash.dependencies  import Input, Output
  5. app = dash.Dash(
  6.     __name__,
  7.     external_stylesheets=[ 'css/bootstrap.min.css']
  8. )
  9. app.layout = html.Div(
  10.     [
  11.         html.Br(),
  12.         html.Br(),
  13.         html.Br(),
  14.         dbc.Container(
  15.             [
  16.                 dbc.Row(
  17.                     [
  18.                         dbc.Col(dbc.Input(id= 'input-lastname'), width= 3),
  19.                         dbc.Col(html.P( '+'), width= 1),
  20.                         dbc.Col(dbc.Input(id= 'input-firstname'), width= 3),
  21.                     ],
  22.                     justify= 'start'
  23.                 ),
  24.                 html.Hr(),
  25.                 dbc.Label(id= 'output1'),
  26.                 html.Br(),
  27.                 dbc.Label(id= 'output2')
  28.             ]
  29.         )
  30.     ]
  31. )
  32. @app.callback(
  33.     [Output( 'output1''children'),
  34.      Output( 'output2''children')],
  35.     [Input( 'input-lastname''value'),
  36.      Input( 'input-firstname''value')]
  37. )
  38. def input_to_output(lastname, firstname):
  39.     try:
  40.          return  '完整姓名:' + lastname + firstname, f '姓名长度为{len(lastname+firstname)}'
  41.     except:
  42.          return  '等待输入...''等待输入...'
  43. if __name__ ==  '__main__':
  44.     app.run_server()
图4

可以看到不管是多个Output()还是Input(),只需要嵌套在列表中即可。

2.3 利用State()实现惰性交互

很多情况下,如果我们的回调函数计算过程时间开销较大,那么像前面介绍的仅靠Input()Output()实现的前后端通信会很频繁,因为监听到的所有输入部件对应属性值只要略一改变,就会触发回调。

为了解决这类问题,Dash中设计了State()对象,我们可以利用State()替换Input()来绑定对应的输入值,再将一些需要主动触发的譬如dbc.Button()按钮部件的属性n_clicks,作为Input()对象进行绑定。

让我们通过下面的例子更好的理解它的作用:

app4.py


   
  1. import dash
  2. import dash_html_components as html
  3. import dash_bootstrap_components as dbc
  4. from dash.dependencies  import Input, Output, State
  5. app = dash.Dash(
  6.     __name__,
  7.     external_stylesheets=[ 'css/bootstrap.min.css']
  8. )
  9. app.layout = html.Div(
  10.     [
  11.         html.Br(),
  12.         html.Br(),
  13.         html.Br(),
  14.         dbc.Container(
  15.             [
  16.                 dbc.Row(
  17.                     [
  18.                         dbc.Col(dbc.Input(id= 'input-value'),
  19.                                 width= 4),
  20.                         dbc.Col(dbc.Button( '小写转大写',
  21.                                            id= 'state-button',
  22.                                            n_clicks= 0),
  23.                                 width= 4),
  24.                         dbc.Col(dbc.Label(id= 'output-value',
  25.                                           style={ 'padding''0',
  26.                                                   'margin''0',
  27.                                                    'line-height''38px'}),
  28.                                 width= 4)
  29.                     ],
  30.                     justify= 'start'
  31.                 )
  32.             ]
  33.         )
  34.     ]
  35. )
  36. @app.callback(
  37.     Output( 'output-value''children'),
  38.     Input( 'state-button''n_clicks'),
  39.     State( 'input-value''value')
  40. )
  41. def input_to_output(n_clicks, value):
  42.      if n_clicks:
  43.          return value.upper()
  44. if __name__ ==  '__main__':
  45.     app.run_server()
图5

可以看到,装饰器中按照Output()Input()State()的顺序传入各个对象后,我们的Button()部件的n_clicks参数记录了对应的按钮被点击了多少次,初始化我们设置其为0,之后每次等我们输入完单词,主动去点击按钮从而增加其被点击次数记录时,回调函数才会被触发,这样就方便了我们的很多复杂应用场景~


以上就是本期的全部内容,欢迎在评论区与我进行讨论~

加入知识星球【我们谈论数据科学】

300+小伙伴一起学习!




· 推荐阅读 ·

这个Pandas函数可以自动爬取Web图表

Python+Dash快速web应用开发——页面布局篇

Python+Dash快速web应用开发——基础概念篇


转载:https://blog.csdn.net/baoqiangwang/article/details/113011859
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场