【问题标题】:Unable to connect two call back functions together in dash app无法在仪表板应用程序中将两个回调函数连接在一起
【发布时间】:2019-05-26 00:48:34
【问题描述】:

您好,我是第一次使用仪表板应用程序,但无法修复 我面临的一个问题。我有一个前端,它接受用户输入,进行一些计算,将结果存储在隐藏的 div 中,然后更新图表。

应用程序加载正常,但在我点击提交按钮时它会立即出错。下面是我写的代码。看起来两个回调函数同时触发。

app.layout = html.Div(style={'backgroundColor': colors['background']}, children=[
    html.Div([
        html.Div([
            html.Img(src='data:image/png;base64,{}'.format(encoded_image.decode()), className="nine columns")
        ], className="three columns"),
        html.Div([
            html.Div([
                html.Div([
                    html.P("Select Month Range:"),
                ],
                    className="two columns"
                ),

                html.Div([
                    dcc.RangeSlider(
                        id='month_slider',
                        # updatemode='drag',
                        # count=1,
                        min=1,
                        max=maxmarks,
                        step=1,
                        value=[maxmarks - 1, maxmarks],
                        marks=tags,
                        pushable=1
                    ),

                ],
                    className="six columns",
                    style={})
            ],
                className="twelve columns",
                style={
                    'backgroundColor': '#EFEAEA',
                    'padding-top': '1.5em',
                    'padding-bottom': '1em'
                }),
            html.Div([
                html.Div([
                    dcc.Dropdown(
                        id='demographics',
                        options=[
                            {'label': 'All 18-49', 'value': '18-49'},
                            {'label': 'Female 25-54', 'value': '25-54F'},
                            {'label': 'All 25-54', 'value': '25-54'},
                        ],
                        placeholder="Select Demographics",
                    )
                ],
                    className="two columns",
                    style={}),
                html.Div([
                    dcc.Dropdown(
                        id='ID',
                        options=[
                            {'label': '200', 'value': 200, 'type': 'number'},
                            {'label': '250', 'value': 250, 'type': 'number'},
                            {'label': '300', 'value': 300, 'type': 'number'},
                            {'label': '350', 'value': 350, 'type': 'number'},
                            {'label': '400', 'value': 400, 'type': 'number'},
                        ],
                        placeholder="Select ID",
                    )
                ],
                    className="two columns",
                    style={}),
                html.Div([
                    dcc.Dropdown(
                        id='Income',
                        options=[
                            {'label': '50,000', 'value': 50000, 'type': 'number'},
                            {'label': '100,000', 'value': 100000, 'type': 'number'},
                            {'label': '200,000', 'value': 200000, 'type': 'number'},
                            {'label': '350,000', 'value': 350000, 'type': 'number'},
                            {'label': '500,000', 'value': 500000, 'type': 'number'},
                        ],
                        placeholder="Select Income",
                    )
                ],
                    className="two columns",
                    style={}),
                html.Div([
                    dcc.Dropdown(
                        id='Frquency',
                        options=[
                            {'label': 'None per week, 'value': 0, 'type': 'number'},
                            {'label': 'Once per Week', 'value': 1, 'type': 'number'},
                            {'label': 'Thrice per Week', 'value': 3, 'type': 'number'},
                        ],
                        placeholder="Select Frequency",
                    )
                ],
                    className="two columns",
                    style={}),
                html.Div([
                    html.Button('Submit', id='submit_button', className='twelve columns',
                                style={'background-color': '#2D91C3', 'color': 'white', 'font-size': '1em'})
                ],
                    className="two columns",
                    style={}),
            ],
                className="twelve columns",
                style={
                    'backgroundColor': '#EFEAEA',
                    'padding-top': '1em',
                    'padding-bottom': '1.5em'
                })
        ], className="nine columns", style={})
    ], className="twelve columns"),

    # dcc.graph layout

    html.Div([
        html.Div([
            dcc.Graph(id='example-graph', config={"displayModeBar": False, "scrollZoom": False})

        ],
            className="six columns",
            style={'border-right': 'thin grey solid', 'border-left': 'thin grey solid',
                   'border-top': 'thin grey solid'}),

    html.Div(id='intermediate-value1', style={'display': 'none'})
])]

@app.callback(
    Output("intermediate-value1", "children"),
    [Input("submit_button", "n_clicks")],
    [
        State("month_slider", "value"),
        State("demographics", "value"),
        State("Income", "value"),
        State("Frequency", "value"),
    ],
)
def clean_data(n_clicks, month_range, demo, inc, fre_cap):
    if n_clicks is not None and n_clicks > 0:
        employee_data_temp = employee_data.copy()
        start_date = month_range[0]
        end_date = month_range[1]
        mask1 = employee_data_temp["total_months"] == int(end_date - start_date)
        employee_data_temp = employee_data_temp.loc[mask1]
        mask2 = (
            (employee_data_temp["demographic"] == demo)
            & (employee_data_temp["freq_cap"] == fre_cap)
            & (employee_data_temp["total_incressions"] == inc)
        )
        employee_data_temp = employee_data_temp.loc[mask2]
        employee_data_temp = employee_data_temp.sort_values(by=["reach"])
        employee_data_temp["income_percent"] = (employee_data_temp["reach"] / 955000) * 100
        employee_data_temp = employee_data_temp.reset_index(drop=True)
        return employee_data_temp.to_json(date_format="iso", orient="split")
    else:
        return []

@app.callback(Output("example-graph", "figure"),
              [Input("submit_button", "n_clicks")],
              [State("intermediate-value1", "children"),
               State("Income", "value")])

def update_graph(n_clicks, employee_data_temp, inc):
    t.sleep(2)
    if n_clicks is not None and n_clicks > 0:
        dff = pd.read_json(employee_data_temp, orient="split")
        max_income = dff["income_percent"].iloc[9]
        max_income = max_income.round(2)
        trace = Scatter(
            y=dff["income_percent"], x=dff["inc"], line=plotly.graph_objs.scatter.Line(color="#1a2d46"), mode="lines"
        )
        layout1 = Layout(
            plot_bgcolor="#FFFFFF",
            paper_bgcolor="#FFFFFF",
            height=450,
            title="Digital Reach - " + str(max_income) + " " + "%",
            xaxis=dict(showgrid=True, showline=True, zeroline=True, fixedrange=True, title="Total Income"),
            yaxis=dict(showline=True, fixedrange=True, zeroline=True, title="Income (%)"),
            margin=plotly.graph_objs.layout.Margin(t=45, l=50, r=50),
        )
        return Figure(data=[trace], layout=layout1)
    else:
        return []

以下是我得到的错误:

Traceback (most recent call last):
  File "C:\Users\Tushar\Documents\django_projects\tvnz_dash_app\src\dash_app\incrementor\views.py", line 21, in dispatcher
    response = server.full_dispatch_request()
  File "C:\python37\lib\site-packages\flask\app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "C:\python37\lib\site-packages\flask\app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "C:\python37\lib\site-packages\flask\_compat.py", line 35, in reraise
    raise value
  File "C:\python37\lib\site-packages\flask\app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\python37\lib\site-packages\flask\app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "C:\python37\lib\site-packages\dash\dash.py", line 1073, in dispatch
    response.set_data(self.callback_map[output]['callback'](*args))
  File "C:\python37\lib\site-packages\dash\dash.py", line 969, in add_context
    output_value = func(*args, **kwargs)
  File "C:\Users\Tushar\Documents\django_projects\tvnz_dash_app\src\dash_app\incrementor\router.py", line 699, in update_graph
    dff = pd.read_json(employee_data_temp, orient="split")
  File "C:\python37\lib\site-packages\pandas\io\json\json.py", line 413, in read_json
    path_or_buf, encoding=encoding, compression=compression,
  File "C:\python37\lib\site-packages\pandas\io\common.py", line 232, in get_filepath_or_buffer
    raise ValueError(msg.format(_type=type(filepath_or_buffer)))
ValueError: Invalid file path or buffer object type: <class 'NoneType'>

我想要的是当用户单击提交按钮时该函数应该运行,并且由于更新图函数依赖于清理数据函数,它不应该运行,除非清理数据函数完成。

请有人帮我弄清楚我在这里缺少什么并帮助解决问题。

提前非常感谢!!

新的更新功能


@app.callback(
    Output("intermediate-value1", "children"),
    [Input("submit_button", "n_clicks")],
    [
        State("month_slider", "value"),
        State("demographics", "value"),
        State("digital_impressions", "value"),
        State("digital_frequency", "value"),
    ],
)

def get_match(n_clicks, month_range, demo, tarps):
    if n_clicks is not None and n_clicks > 0:
        some long python script data calculatation
        return match_data_temp.to_json(date_format="iso", orient="split")



@app.callback(
    Output("intermediate-value2", "children"),
    [Input("submit_button", "n_clicks")],
    [
        State("month_slider", "value"),
        State("demographics", "value"),
        State("digital_impressions", "value"),
        State("digital_frequency", "value"),
    ],
)


def clean_data(n_clicks, month_range, demo, inc, fre_cap):
    if n_clicks is not None and n_clicks > 0:
        employee_data_temp = employee_data.copy()
        start_date = month_range[0]
        end_date = month_range[1]
        mask1 = employee_data_temp["total_months"] == int(end_date - start_date)
        employee_data_temp = employee_data_temp.loc[mask1]
        mask2 = (
            (employee_data_temp["demographic"] == demo)
            & (employee_data_temp["freq_cap"] == fre_cap)
            & (employee_data_temp["total_incressions"] == inc)
        )
        employee_data_temp = employee_data_temp.loc[mask2]
        employee_data_temp = employee_data_temp.sort_values(by=["reach"])
        employee_data_temp["income_percent"] = (employee_data_temp["reach"] / 955000) * 100
        employee_data_temp = employee_data_temp.reset_index(drop=True)
        return employee_data_temp.to_json(date_format="iso", orient="split")




@app.callback(
    Output("intermediate-value3", "children"),
    [
        Input("month_slider", "value"),
        Input("demographics", "value"),
        Input("intermediate-value2", "children"),
        Input("intermediate-value1", "children"),
    ],
)
def get_match_overlap(month_range, demo, match_data_temp, employee_data_temp):

    overlap_match = pd.read_json(match_data_temp, orient="split")
    overlap_employee = pd.read_json(employee_data_temp, orient="split")
    some long python calculatation
    return final.to_json(date_format="iso", orient="split")


@app.callback(
    Output("example-graph2", "figure"),
    [Input("intermediate-value3", "children"), Input("intermediate-value2", "children")],
)
def update_graph(final, match_data_temp):
    if final is not None:
        dff_overlap = pd.read_json(final, orient="split")
        dff_match_data = pd.read_json(match_data_temp, orient="split")
        overlap = Scatter(
            y=dff_overlap["final"],
            x=dff_overlap["percentage"],
            name="tile",
            line=plotly.graph_objs.scatter.Line(color="red"),
            mode="lines",
        )
        layout1 = Layout(
            plot_bgcolor="#FFFFFF",
            paper_bgcolor="#FFFFFF",
            title="title",
            height=550,
            xaxis=dict(showgrid=True, showline=True, zeroline=False, fixedrange=True, title="% Campaign Completion", range=[0, 100]),
            yaxis=dict(showline=True, fixedrange=True, zeroline=False, title="Audience Reached (%)", range=[0, (reach_increment + 10)]),
            margin=plotly.graph_objs.layout.Margin(t=45, l=50, r=50),
        )

        linear = Scatter(
            y=dff_match_data["Final"],
            x=dff_match_data["percentage"],
            name="title",
            line=plotly.graph_objs.scatter.Line(color="black"),
            mode="lines",
        )
        return Figure(data=[overlap, linear], layout=layout1)

@app.callback(Output("example-graph", "figure"),
              [Input("submit_button", "n_clicks")],
              [State("intermediate-value2", "children"),
               State("Income", "value")])

def update_graph(n_clicks, employee_data_temp, inc):

    if employee_data_temp is not None:
        dff = pd.read_json(employee_data_temp, orient="split")
        trace = Scatter(
            y=dff["income_percent"], x=dff["inc"], line=plotly.graph_objs.scatter.Line(color="#1a2d46"), mode="lines"
        )
        layout1 = Layout(
            plot_bgcolor="#FFFFFF",
            paper_bgcolor="#FFFFFF",
            height=450,
            title="Digital - " + str(max_income) + " " + "%",
            xaxis=dict(showgrid=True, showline=True, zeroline=True, fixedrange=True, title="Total Income"),
            yaxis=dict(showline=True, fixedrange=True, zeroline=True, title="Income (%)"),
            margin=plotly.graph_objs.layout.Margin(t=45, l=50, r=50),
        )
        return Figure(data=[trace], layout=layout1)


【问题讨论】:

    标签: django plotly loading plotly-dash


    【解决方案1】:

    好的,先说一些小事。这里有一些语法错误,尽管它们可能不在您的原始文件中。您缺少 ID,因为 id='Frequency', 有错字。轻松修复。我收到了很多关于 Dropdown 的不正确道具的警告,我通过删除您为每个下拉选项传递的 type='number' 解决了这个问题。我还将update_graph 的默认返回值更改为一个空字典,这解决了另一个类型错误。

    现在是更大的事情。您正在使用同一个按钮触发两个回调。第一个处理数据,并将其存储在隐藏的 div 中。第二个睡眠时间足以让第一个完成,然后运行隐藏 div 中的内容。问题是第二个回调会在它运行时立即拉入该隐藏 div 的内容,并且它休眠的事实不会改变它使用的值 即使该值在睡眠定时器结束。

    这里的解决方法是结合回调。您可以删除隐藏的 div,只需在单个回调中完成所有工作。

    【讨论】:

    • 感谢您的回复。我这样做的原因是我在其他回调中使用相同的数据帧,因此必须将其存储在隐藏的 div 中。解决此限制的任何其他方式。这只是我在应用中使用的回调之一
    • 啊,我明白了。在这种情况下,您可以将第二个回调的 Input 设为隐藏 div 的 children 值。这样你就知道它已经更新了。
    • 再次非常感谢您的回复。我按照说明按照您的要求做了,但是我仍然收到同样的错误
    • 我也使用过它,但它仍然会出现同样的错误。我已经按照我现在的使用方式更新了代码
    • 我已经更新了破折号回调函数的整个序列。我已经删除了长 python 代码。这应该让您对我正在尝试做的事情有所了解。该应用程序即使在出现错误后也能正常工作,但它始终处于更新模式。因此,我无法向应用程序添加加载组件。再次感谢您的回复和所有帮助。如果您需要其他任何东西,请告诉我。
    猜你喜欢
    • 2017-04-19
    • 1970-01-01
    • 1970-01-01
    • 2018-06-24
    • 1970-01-01
    • 1970-01-01
    • 2018-12-12
    • 1970-01-01
    • 2010-12-04
    相关资源
    最近更新 更多