我们有一个带有登录页面的 Dash 应用程序,可以使用 Flask-Login 对用户进行身份验证。经过身份验证后,应用程序会动态加载仪表板模块,在本例中是名为 modal_test 的测试模块。
测试模块应该显示一个按钮,单击该按钮会打开一个模式,并且它包含处理此交互的回调。
ComponentsLogin 页面 (app.py):显示一个登录表单,用户可以在其中输入用户名和密码。成功登录后,它会动态加载 modal_test 仪表板模块并设置其回调。
**仪表板模块(modal_test.py):**包含一个带有按钮的布局,单击该按钮应打开模式。包括处理模式打开和关闭的回调函数。
问题 问题是,虽然 modal_test 模块中的回调已注册(如“注册回调...”调试打印所示),但单击按钮时,这些回调中的函数不会被执行。这意味着模式没有按预期打开,并且没有相关的调试语句打印到控制台。
我们正在努力解决什么问题
回调注册验证:确保模块加载时正确注册modal_test.py中的回调。验证设置布局后是否调用了setup_callbacks函数。回调
触发:了解为什么单击按钮时回调函数(例如,toggle_modal)没有被触发。确保回调定义中使用的输入 ID 与布局中的输入 ID 匹配。
应用程序.py 该脚本包括登录页面以及成功登录后加载 modal_test 模块的逻辑。
import dash
import dash_bootstrap_components as dbc
from dash import html, dcc, Input, Output, State, ctx
from flask import Flask
from flask_login import LoginManager, login_user, UserMixin, current_user
import os
import secrets
import importlib
from functools import lru_cache
import json
# Flask server setup
server = Flask(__name__)
server.secret_key = os.environ.get('SECRET_KEY', secrets.token_urlsafe(16))
app = dash.Dash(__name__, server=server, external_stylesheets=[dbc.themes.BOOTSTRAP], suppress_callback_exceptions=True)
# Flask-Login setup
login_manager = LoginManager()
login_manager.init_app(server)
# Dummy user class for authentication
class User(UserMixin):
def __init__(self, id):
self.id = id
# In-memory user store
users = {'testuser': 'testpassword'}
@login_manager.user_loader
def load_user(user_id):
return User(user_id)
# Login page layout
login_color = '#06bfa9'
login_layout = html.Div([
html.Div([
html.H2("Welcome to the App", style={'color': login_color, 'textAlign': 'center'}),
dcc.Input(id='username', type='text', placeholder='Username',
style={'backgroundColor': '#1a1a1a', 'color': login_color, 'border': 'none', 'borderRadius': '5px', 'padding': '10px', 'margin': '10px 0', 'width': '100%'}),
dcc.Input(id='password', type='password', placeholder='Password',
style={'backgroundColor': '#1a1a1a', 'color': login_color, 'border': 'none', 'borderRadius': '5px', 'padding': '10px', 'margin': '10px 0', 'width': '100%'}),
dbc.Button('Login', id='login-button',
style={'backgroundColor': login_color, 'color': '#1a1a1a', 'border': 'none', 'borderRadius': '5px', 'padding': '10px', 'width': '100%', 'cursor': 'pointer'}),
], style={'width': '300px', 'padding': '20px', 'border': '2px solid #00E5FF', 'borderRadius': '10px', 'backgroundColor': '#2c2c2c'}),
], style={'display': 'flex', 'justifyContent': 'center', 'alignItems': 'center', 'height': '100vh', 'backgroundColor': '#1a1a1a'})
# Main layout
app.layout = html.Div([
dcc.Location(id='url', refresh=True),
html.Div(id='page-content', children=login_layout),
html.Div(id='dashboard-content') # Placeholder for the selected dashboard content
])
# Login callback
@app.callback(
[Output('page-content', 'children'),
Output('dashboard-content', 'children')],
[Input('login-button', 'n_clicks')],
[State('username', 'value'), State('password', 'value')],
prevent_initial_call=True
)
def login(n_clicks, username, password):
if username in users and users[username] == password:
user = User(username)
login_user(user)
dashboard_module = load_dashboard_module('modal_test')
if dashboard_module:
layout = dashboard_module.create_layout(app)
if hasattr(dashboard_module, 'setup_callbacks'):
dashboard_module.setup_callbacks(app)
return html.Div(), layout
return login_layout, html.Div('Login failed. Please try again.')
@lru_cache(maxsize=32)
def load_dashboard_module(script_name):
"""Dynamically import and cache dashboard modules."""
try:
module = importlib.import_module(f"dashes.{script_name}")
if hasattr(module, 'create_layout') and callable(module.create_layout):
print(f"Successfully loaded module: {script_name}")
return module
else:
print(f"Module {script_name} does not have a callable 'create_layout' function")
except ImportError as e:
print(f"ImportError for module {script_name}: {e}")
return None
if __name__ == '__main__':
app.run_server(host="0.0.0.0", port=8050, debug=True)
modal_test.py 该脚本定义了一个简单的仪表板模块,其中包含用于打开模式的按钮,并包含用于处理按钮单击的回调。
import dash
import dash_bootstrap_components as dbc
from dash import html, Input, Output, State, callback_context
def create_layout(app):
return dbc.Container(
[
dbc.Button("Open Modal", id="open-modal-button", n_clicks=0),
dbc.Modal(
[
dbc.ModalHeader(dbc.ModalTitle("Modal Header")),
dbc.ModalBody("This is the content of the modal"),
dbc.ModalFooter(
dbc.Button("Close", id="close-modal-button", className="ml-auto")
),
],
id="example-modal",
is_open=False,
),
html.Div(id="output-div") # For testing callback
],
fluid=True,
className="p-5",
)
def setup_callbacks(app):
print("Registering callbacks...")
@app.callback(
Output("example-modal", "is_open"),
[Input("open-modal-button", "n_clicks"), Input("close-modal-button", "n_clicks")],
[State("example-modal", "is_open")],
)
def toggle_modal(n1, n2, is_open):
print("toggle_modal called")
print(f"n1: {n1}, n2: {n2}, is_open: {is_open}")
print("Callback context:", callback_context.triggered)
if n1 or n2:
return not is_open
return is_open
@app.callback(
Output("output-div", "children"),
[Input("open-modal-button", "n_clicks")],
)
def test_button_click(n_clicks):
print("test_button_click called")
print(f"Button clicked {n_clicks} times")
print("Button click context:", callback_context.triggered)
return f"Button clicked {n_clicks} times"`
总结 我面临一个问题,即单击按钮时不会触发 modal_test 仪表板模块中的回调,即使它们已注册。目标是了解回调未执行的原因并找到解决方案以确保它们按预期工作。任何帮助将不胜感激。
实际上尝试了多个日志记录和故障排除步骤。
在 app.py 中,您需要注册 all 回调 - 即。包括仪表板模块中的模块 - before 调用
app.run()
,这意味着您无法从回调中动态加载这些模块,只需执行 :
from modal_test import create_layout, setup_callbacks
对于回调(
app.layout = html.Div([])
之后):
# Dashboard module callbacks
setup_callbacks(app)
# Login callback
@app.callback(
[Output('page-content', 'children'),
Output('dashboard-content', 'children')],
[Input('login-button', 'n_clicks')],
[State('username', 'value'), State('password', 'value')],
prevent_initial_call=True
)
def login(n_clicks, username, password):
if username in users and users[username] == password:
user = User(username)
login_user(user)
modal_layout = create_layout(app)
return html.Div(), modal_layout
return login_layout, html.Div('Login failed. Please try again.')
if __name__ == '__main__':
app.run_server(host="0.0.0.0", port=8050, debug=True)
另一种方法是构建一个多页面应用程序