np8*_*np8 8 python plotly-dash
Dash Web 应用程序有一个 dash 应用程序实例,通常命名为app,并像这样启动:
app = dash.Dash(__name__)
Run Code Online (Sandbox Code Playgroud)
然后,使用callback装饰器将回调添加到应用程序中:
@app.callback(...)
def my_function(...):
# do stuff.
Run Code Online (Sandbox Code Playgroud)
在您找到的大多数教程中,回调是使用app.py. 这当然只是 MWE 的做事方式。在实际应用程序中,将代码分离到模块和包将大大提高可读性和可维护性,但天真地将回调和布局分离只会导致循环导入。
从app.py单页应用程序中分离回调和布局的正确方法是什么?
这是问题的最小(非)工作示例
.
??? my_dash_app
? ??? app.py
? ??? views
? ??? first_view.py
? ??? __init__.py
??? setup.py
Run Code Online (Sandbox Code Playgroud)
import setuptools
setuptools.setup(
name='dash-minimal-realworld',
version='1.0.0',
install_requires=['dash>=1.12.0'],
packages=setuptools.find_packages(),
)
Run Code Online (Sandbox Code Playgroud)
import dash
from my_dash_app.views.first_view import make_layout
app = dash.Dash(__name__)
app.layout = make_layout()
if __name__ == '__main__':
app.run_server(debug=True)
Run Code Online (Sandbox Code Playgroud)
@app.callback(...)
def my_function(...):
# do stuff.
Run Code Online (Sandbox Code Playgroud)
将python ./my_dash_app/app.py结果运行到循环依赖中:
ImportError: cannot import name 'make_layout' from 'my_dash_app.views.first_view' (c:\tmp\dash_minimal_realworld\my_dash_app\views\first_view.py)
Run Code Online (Sandbox Code Playgroud)
San*_*nMu 26
A little late to the party but I have found this to be a straightforward way of doing it:
def get_callbacks(app):
@app.callback([Output("figure1", "figure")],
[Input("child1", "value")])
def callback1(figure):
return
@app.callback([Output("figure2", "figure")],
[Input("child2", "value")])
def callback2(figure):
return
Run Code Online (Sandbox Code Playgroud)
import dash
from callbacks import get_callbacks
import layout
app = dash.Dash(__name__)
app.layout = layout.layout
get_callbacks(app)
Run Code Online (Sandbox Code Playgroud)
小智 13
我知道现在回答你的问题已经太晚了,但也许其他人会发现它很有用。
我希望能够在单独的文件中创建回调,但是我认为虽然从主仪表板模块导入应用程序效果很好,但对于阅读代码的其他人来说可能不清楚。
我创建了一个用于初始化回调的回调管理器。该管理器附加到主应用程序模块中的应用程序。
from dataclasses import dataclass, field
from typing import Callable, List, Union
from dash.dependencies import handle_callback_args
from dash.dependencies import Input, Output, State
@dataclass
class Callback:
func: Callable
outputs: Union[Output, List[Output]]
inputs: Union[Input, List[Input]]
states: Union[State, List[State]] = field(default_factory=list)
kwargs: dict = field(default_factory=lambda: {"prevent_initial_call": False})
class CallbackManager:
def __init__(self):
self._callbacks = []
def callback(self, *args, **kwargs):
output, inputs, state, prevent_initial_call = handle_callback_args(
args, kwargs
)
def wrapper(func):
self._callbacks.append(
Callback(
func,
output,
inputs,
state,
{"prevent_initial_call": prevent_initial_call}
)
)
return wrapper
def attach_to_app(self, app):
for callback in self._callbacks:
app.callback(
callback.outputs, callback.inputs, callback.states, **callback.kwargs
)(callback.func)
Run Code Online (Sandbox Code Playgroud)
import dash
from callback_manager import CallbackManager
callback_manager = CallbackManager()
@callback_manager.callback(
dash.dependencies.Output('label', 'children'),
[dash.dependencies.Input('call_btn', 'n_clicks')])
def update_label(n_clicks):
if n_clicks > 0:
return "Callback called!"
Run Code Online (Sandbox Code Playgroud)
import dash
import dash_html_components as html
from callbacks import callback_manager
app = dash.Dash(__name__)
callback_manager.attach_to_app(app)
app.layout = html.Div([
html.Div(id="label"),
html.Button('Call callback', id='call_btn', n_clicks=0),
])
if __name__ == '__main__':
app.run_server(debug=True)
Run Code Online (Sandbox Code Playgroud)
请注意,您可以拥有多个带有回调的文件,并使用关键字导入它们as:
from callbacks1 import callback_manager as callback_manager1
from callbacks2 import callback_manager as callback_manager2
app = dash.Dash(__name__)
callback_manager1.attach_to_app(app)
callback_manager2.attach_to_app(app)
Run Code Online (Sandbox Code Playgroud)
我相信这样做更明确。
小智 12
您可以只使用装饰器@dash.callback而不是@app.callback. 然后删除该行from my_dash_app.app import app,first_view.py您将摆脱循环依赖。
从文档来看:是Dash 2.0 中引入@dash.callback的替代方案。@app.callback (where app = dash.Dash())它允许您注册回调而无需定义或导入应用程序对象。调用签名是相同的,并且可以app.callback在所有情况下使用它来代替。
Tor*_*xed 10
我不认为(但我可能是错的)本身有一种正确的方法,但是你可以做的是maindash.py在你的启动代码周围有一个中央模块()app = dash.Dash(__name__),并且有不同的回调,只需app从my_dash_app.maindash. 这将在它们自己的单独模块中设置回调,但会为app实例重复使用该一个中央模块。
app.py成为启动一切的主脚本。maindash.py负责创建主应用程序实例。first_view.py是定义装饰器以设置所有回调的地方。
.
??? my_dash_app
? ??? app.py
? ??? maindash.py
? ??? views
? ??? first_view.py
? ??? __init__.py
??? setup.py
Run Code Online (Sandbox Code Playgroud)
由于在 Python 中重用了导入,因此from my_dash_app.maindash import app从不同的其他模块(例如事件处理程序和主脚本)执行多次操作并没有什么真正的危害。他们将共享相同的导入实例 - 因此也重新使用该dash.Dash()实例。
只要确保在设置处理程序之前导入中央模块,就可以了。
以下是为测试而分离的代码片段:
.
??? my_dash_app
? ??? app.py
? ??? maindash.py
? ??? views
? ??? first_view.py
? ??? __init__.py
??? setup.py
Run Code Online (Sandbox Code Playgroud)
from my_dash_app.maindash import app
from my_dash_app.views.first_view import make_layout
if __name__ == '__main__':
app.layout = make_layout()
app.run_server(debug=True)
Run Code Online (Sandbox Code Playgroud)
import dash
app = dash.Dash(__name__)
Run Code Online (Sandbox Code Playgroud)
这是对旧帖子的较晚回复,但这是一种非常简单的方法来完全分离布局、回调和应用程序,而不会引入额外的复杂性。
应用程序.py
from dash import Dash
from layouts import my_layout
from callbacks import my_callback, my_callback_inputs, my_callback_outputs
if __name__ == "__main__":
app = Dash(__name__)
app.layout = my_layout
app.callback(my_callback_outputs, my_callback_inputs)(my_callback)
app.run_server()
Run Code Online (Sandbox Code Playgroud)
它的工作原理是不使用装饰器语法,装饰器语法应该是“语法糖”,以使事情变得更简单。然而,对于这个特定问题,许多提出的解决方案实际上增加了耦合性和复杂性,同时保留了装饰器语法。在这种情况下,不使用装饰器会更简单。
在callbacks.py,中my_callback定义时没有任何装饰器:
回调.py
my_callback_inputs = []
my_callback_outputs = []
def my_callback():
return
Run Code Online (Sandbox Code Playgroud)