首页 > 解决方案 > Python Dash - 同一回调函数中的动态和静态输入选项

问题描述

我需要编写一个回调函数,该函数将具有一个动态输入组件和一个静态输入组件。但是将它们放在一起会引发错误。

这是我到目前为止所做的 -

@app.callback(
    [Output("new_list", "children")],
    [
     #Static Input Component
     Input("clear", "n_clicks"),
     #Dynamic Input Component
     Input(str(i), "value") for i in item_list[['item3','item2','item1']].stack().groupby(level=0).agg(' '.join)
     ],
)
###########################################################
## Updated sample working code

import dash
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash import callback_context
from dash.dependencies import Input, Output, State
import numpy as np
import pandas as pd
import dash_table as dt

item1 = ['A','A','B','B']
item2 = ["W","X","Y","Z"]
item3 = ["L",np.nan,'M','L']
item_list = pd.DataFrame(list(zip(item1,item2,item3)),columns=["item1","item2","item3"])

cat_list = item_list.item1.unique()
global itemlist
itemlist = pd.DataFrame( columns = ['item','qty'])
for item in item_list[['item3','item2','item1']].stack().groupby(level=0).agg(' '.join):
    itemlist.loc[len(itemlist)] = (item,0)

def Add_item(data,data2):
    print("BILLING: Add_item()")
    item_list = dbc.Card(children = [
                        generate_item(data,data2),
                        dbc.Row([
                            dbc.Col([
                                dbc.Button(
                                    html.H5("Cancel"), id="cancel", className="ml-auto", color = "danger"
                                )
                            ]),
                            dbc.Col([
                                dbc.Button(
                                    html.H5("Clear"), id="clear", className="ml-auto", color = "info"
                                )
                            ]),
                        ],style = {"width": "71rem"})
                 ],  
                id="modal-body-newitem"
                )
    return item_list

def generate_item(item_list,itemlist):
    buttons = html.Div(children =[
                           dbc.Row([
                                dbc.Button(
                                    html.H5(str('+ ' + a)),
                                    id= str(a),
                                    className="mb-3",
                                    color="primary",
                                    block=True
                                ),
                                dbc.Collapse([
                                        dbc.Card([
                                                dbc.Row([
                                                        dbc.Col([dbc.Button(html.H5(str('+ ' + b)),className="mb-3",color="info",block=True, disabled = True)],width = 10),
                                                        dbc.Col([dbc.Input(type="number", min=0, max=20, step=1,value = itemlist.loc[itemlist.item == b,'qty'],id = b )],width = 2)
                                                ]) for b in item_list.loc[item_list.item1== a, ('item3','item2','item1')].stack().groupby(level=0).agg(' '.join).sort_values()
                                        #,dbc.Card(dbc.CardBody(str("This content is for " + a)))
                                        ],body = True, style = {"width": "71rem", "justify": "centre", "align": "centre"})
                                ],id= str("collapse" + a))        
                            ]) for a in item_list.item1.unique()
               ])
    
    return buttons

app = dash.Dash(__name__,external_stylesheets=[dbc.themes.CYBORG])
app.config.suppress_callback_exceptions = True
server = app.server

app.title="Test"

app.layout = html.Div([
             dbc.Card(
            dbc.CardBody(
                [
                    html.H5("New Item", className="card-title"),
                    Add_item(item_list,itemlist),
                    html.Div(id = 'new_list')
                ]
            )
        )
])


#Item Ctegory List
@app.callback(
    [Output(str("collapse" + i), "is_open") for i in cat_list],
    [Input(str(i), "n_clicks") for i in cat_list]+
    [Input("cancel", "n_clicks")])
def toggle_collapse_category_box(*args):
    trigger = callback_context.triggered[0] 
    print("MYBIZZAPP:toggle_collapse_category_box:  Call - "+str(callback_context.triggered))
    if not callback_context.triggered or trigger["prop_id"].split(".")[0] == 'cancel':
        print('MYBIZZAPP:toggle_collapse_category_box: Not Triggered/Cancel')
        global isopn
        isopn = [False] * len(cat_list) 
    else:
        print('MYBIZZAPP:toggle_collapse_category_box: Triggered')
        for i in range(len(cat_list)):
            if cat_list[i] == trigger["prop_id"].split(".")[0]:
                isopn[i] = not isopn[i]
                
    return isopn


### Clear item list
@app.callback(
    [Output(str(i), "value") for i in item_list[['item3','item2','item1']].stack().groupby(level=0).agg(' '.join)],
    [Input("cancel", "n_clicks"),
     Input("clear", "n_clicks")])
def clear_item_box(n2,n3):
    trigger = callback_context.triggered[0]
    print("MYBIZZAPP:clear_item_box:  Call - "+str(callback_context.triggered))
    if trigger["prop_id"].split(".")[0] == 'cancel':
        print("MYBIZZAPP:clear_item_box: cancel order")
        itemlist = pd.DataFrame( columns = ['item','qty'])
        for item in item_list[['item3','item2','item1']].stack().groupby(level=0).agg(' '.join):
            itemlist.loc[len(itemlist)] = (item,0)
    elif trigger["prop_id"].split(".")[0] == 'clear':
        print("MYBIZZAPP:clear_item_box: clear order")
        
    return [0] * len(item_list)
    

#Menu item list
@app.callback(
    [Output("new_list", "children")],
    [Input(str(i), "value") for i in item_list[['item3','item2','item1']].stack().groupby(level=0).agg(' '.join)]+
    [Input("clear", "n_clicks"),
     Input("cancel", "n_clicks")])
def menu_item_list(*args):
    trigger = callback_context.triggered[0] 
    print("MYBIZZAPP:menu_item_list:  Call - "+str(callback_context.triggered))
    print(trigger["prop_id"].split(".")[0])
    if not callback_context.triggered :
        print('MYBIZZAPP:menu_item_list: Not Triggered')
        global itemlist
        itemlist = pd.DataFrame( columns = ['item','qty'])
        for item in item_list[['item3','item2','item1']].stack().groupby(level=0).agg(' '.join):
            itemlist.loc[len(itemlist)] = (item,0)
    else:
        print('MYBIZZAPP:menu_item_list: Triggered')
        itemlist.loc[itemlist.item == trigger["prop_id"].split(".")[0],'qty'] = trigger["value"]
    
    if trigger["prop_id"].split(".")[0] == 'clear' or trigger["prop_id"].split(".")[0] == 'clear':
        print('MYBIZZAPP:menu_item_list: Clear|Cancel')
        itemlist['qty'] = 0
        
        
    print('MYBIZZAPP:menu_item_list: Item List')
    print(itemlist)
    return ['Total items selected - ' + str(sum(itemlist.qty))]
    



if __name__ == '__main__':
    app.run_server(debug=True)

如果我不提及静态输入组件,代码可以正常工作。但我也需要静态组件。

我正在尝试解决的总体问题是 - 我有动态输入文本框,如果在这些框中的任何一个输入值时,需要考虑该值并将其作为表格返回。此外,还有一个“清除”按钮。如果单击,所有值应重新设置为默认的预定义值。

提前感谢您的帮助。

标签: pythonplotlyplotly-dash

解决方案


感谢您更新错误。这个问题来自您的列表理解。添加第一个输入时,它会为理解创建无效语法。不过,这应该可行:

@app.callback(
    [Output("new_list", "children")],
    [
     #Static Input Component
     Input("clear", "n_clicks"),
    ] + [
     #Dynamic Input Component
     Input(str(i), "value") for i in item_list[['item3','item2','item1']].stack().groupby(level=0).agg(' '.join)
    ],
)

推荐阅读