我有以下的达世币应用程序(简化)。它显示文本框和单词列表(按钮)。单击具有文本的按钮,将相应的文本添加到文本框中(这是我希望客户端回调的部分)。还有另一个具有相同输出的回调(现在Dash 2.9允许这样做),当用户单击刷新术语的“-〉”按钮时会触发。
# Font Survey app
import re
import dash
import dash_bootstrap_components as dbc
import pandas as pd
from dash import dcc, html
from dash.dependencies import ALL, Input, Output, State
from dash.exceptions import PreventUpdate
EMPTY_DIV = html.Div()
# Div for sample list of terms
WORDS = list(pd.read_csv('font_terms.csv')['adj'])
word_items = [html.Li(children=dbc.Button(word, id={'type': 'fill-word-button', 'index': i})) for i, word in enumerate(WORDS)]
terms_div = html.Div(id='terms', children=word_items)
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP], prevent_initial_callbacks="initial_duplicate")
server = app.server
app.layout = html.Div([
dcc.Store(id='terms-store', data=WORDS),
html.Div(id="adj-inputs", className="column",
children= [dcc.Input(id={"type": "adj-input", "index": i}, value='') for i in range(5)]),
terms_div,
html.Button("→", id='forward-button', n_clicks=0),])
@app.callback(
[Output({"type": "adj-input", "index": i}, 'value') for i in range(5)],
Input({"type": "fill-word-button", "index": ALL}, "n_clicks"),
State({'type': 'adj-input', 'index': ALL}, "value")
)
def fill_word_button(button_click, adj_inputs):
ctx = dash.callback_context
if not ctx.triggered:
raise PreventUpdate
button_id = ctx.triggered[0]['prop_id'].split('.')[0]
button_index = int(re.search(r"\d+", button_id).group())
# check if there is an empty text box
if '' not in adj_inputs:
raise PreventUpdate
adj_inputs = ['' if a is None else a for a in adj_inputs] + [WORDS[button_index]]
adj_inputs = list(set([a for a in adj_inputs if a!=''])) + [a for a in adj_inputs if a=='']
return adj_inputs[:5]
# The following callback, once debugged, should replace the callback above ("fill_word_button")
# @app.clientside_callback(
# """
# function(n_clicks, adj_inputs, words) {
# const ctx = dash_clientside.callbackContext;
# if (!ctx.triggered.length) {
# return adj_inputs;
# }
# const button_id = ctx.triggered[0]['prop_id'].split('.')[0];
# const button_index = parseInt(button_id.match(/\d+/)[0]);
# // check if there is an empty text box
# if (adj_inputs.every(a => a !== '')) {
# return adj_inputs;
# }
# adj_inputs = adj_inputs.concat([words[button_index]]);
# adj_inputs = [...new Set(adj_inputs.filter(a => a !== '')), ...adj_inputs.filter(a => a === '')];
# return adj_inputs.slice(0, 5);
# }
# """,
# Output({"type": "adj-input", "index": ALL}, 'value'),
# Input({"type": "fill-word-button", "index": ALL}, "n_clicks"),
# State({'type': 'adj-input', 'index': ALL}, "value"),
# State('terms-store', 'data')
# )
@app.callback(
[Output({"type": "adj-input", "index": i}, 'value', allow_duplicate=True) for i in range(5)],
[Input("forward-button", "n_clicks")],
[State({'type': 'adj-input', 'index': ALL}, "value")],
)
def refresh_terms(forward_clicks, adj_inputs):
ctx = dash.callback_context
if not ctx.triggered:
raise PreventUpdate
button_id = ctx.triggered[0]['prop_id'].split('.')[0]
if button_id == "forward-button":
adj_inputs = [adj for adj in adj_inputs if adj !=""]
# Do something with the adj_inputs e.g. upload to database and
# db.insert_response_mysql(adj_inputs)
# Reset adj_inputs
return [""]*5
if __name__ == "__main__":
app.run_server(debug=True, port=8000, host="0.0.0.0")
我遇到的错误与refresh_terms
回调有关
[State({'type': 'adj-input', 'index': ALL}, "value")],
TypeError: 'NoneType' object is not callable
只有当我尝试使用客户端回调时才会发生这种情况,但当我使用单独的回调时就可以了。请帮助我调试此错误。
1条答案
按热度按时间7jmck4yq1#
这是因为
app.clientside_callback
不是一个装饰器,而是一个常规函数,你只需要删除它前面的@
。此外,回调上下文属性不是驼峰式的,即。
(edit距离:3个字符;)