kms
kms

Reputation: 2014

Callback-triggered update of plotly figure following change in values of datatable in Dash app

I have a map and datatable component in the layout that is update by a callback. The datatable is editable and the user can add new information to the new table. Upon the click event of update table

I have two callbacks, one for updating the map and another one for datatable. What I'd like to do is run the map callback from inside of the datatable callback. Basically, I'd like to update the map based on the values of datatable.

Can I run another callback from a callback or run some lines of code inside another callback? Trying to figure out a elegant and efficient way of doing this.

layout = html.Div([

                # Plot map
                dcc.Graph(id="map-graph"),

                # DataTable
                dash_table.DataTable(

                    id="table",

                    columns=[{"id":"Type","name":"Type"},
                                   {"id":"space","name": "space"}],

                    sort_action="native",
                    filter_action="native",
                    row_deletable=True
                ),

                # Update map
                dbc.Button("Update map", id="update-map", className="mr-1"),

                # Update Table
                dbc.Button("Update table", id="update-table", className="mr-1")

 
            ]), 


     # Callbacks 

    # Update map
    @app.callback(Output("map-graph", "figure"),
                          [Input("update-map", "value")])
    def updatemap(value):

         ...
         do something
         ...

         return value

 
    # Update table
    @app.callback(Output("table", "data"),
                          [Input("update-table", "value")])
    def updatetable(value):

         ...
         do something
         ...

         # Run the code inside previous callback which updates the map. 

         return rows

Upvotes: 7

Views: 11908

Answers (2)

John Collins
John Collins

Reputation: 2951

You can actually achieve updating a plotly graph following editing of data in a dash_table.DataTable with a single callback in a Dash app

In the example you provide you have an HTML button component user interaction acting as the trigger event which initializes the first callback. One callback in this case can be sufficient.

E.g., a simple example using a pie chart instead of a map graph:

import pandas as pd
import plotly.express as px

import dash_bootstrap_components as dbc

from dash import dcc, html, dash_table
from dash import Dash, Input, Output, State, callback
from dash.exceptions import PreventUpdate


df = pd.DataFrame(
    {
        "reputation": [0, 10, 2, 25, 50, 0, 4],
        "day": [
            "Sunday",
            "Monday",
            "Tuesday",
            "Wednesday",
            "Thursday",
            "Friday",
            "Saturday",
        ],
    }
)
pie_cmap = {
    "Sunday": "red",
    "Monday": "orange",
    "Tuesday": "yellow",
    "Wednesday": "green",
    "Thursday": "blue",
    "Friday": "purple",
    "Saturday": "pink",
}

def plot_fig(df):
    return px.pie(
        df,
        values="reputation",
        names="day",
        color="day",
        color_discrete_map=pie_cmap,
    )

fig = plot_fig(df)

app = Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])

app.layout = dbc.Container(
    [
        dcc.Graph(figure=fig, id="pie-graph"),
        dbc.Label("Click a cell in the table to edit the table:"),
        dash_table.DataTable(
            df.to_dict("records"),
            [{"name": i, "id": i} for i in df.columns],
            id="table-1",
            editable=True,
        ),
        html.Br(),
        html.Button("Update Graph", id="update-graph", n_clicks=0),
    ],
    style={"textAlign": "center"},
)


@callback(
    Output("pie-graph", "figure"),
    Input("update-graph", "n_clicks"),
    State("table-1", "data"),
)
def update_graphs(n_clicks, table_data):
    if n_clicks < 1:
        raise PreventUpdate
    df_update = pd.DataFrame(table_data)
    return plot_fig(df_update)


if __name__ == "__main__":
    app.run(debug=True)

gives: Demo of provided dash app updating a graph based on user edits to data table

Multiple Callbacks

Nonetheless, if you did have another callback which was updating the data in the data table, that would be compatible with this example code. The graph will update based on the latest state (i.e., State) of the data in the table following the event that triggers the callback (in this case and example above, a user clicking a button) - regardless of how it was that the data in the dash_table.DataTable was changed/updated.

Upvotes: 0

LightCC
LightCC

Reputation: 11619

Check out the Dash documentation in Dash Tutorial - Part 3: Basic Callbacks in the section Dash App With Chained Callbacks, currently about half-way down the page.

The basics of that section are that you can use any output from a callback as an input to another callback.

For example, below the first callback writes out to my-output-1, its value property, then the second callback uses that id and property as an input to drive the my-output-2 id's value property.

@app.callback(
    Output('my-output-1', 'value'),
    Input('my-input-1', 'value'))
def set_output_1_from_input_1(input_1_value):
    return input_1_value

@app.callback(
    Output('my-output-2', 'value'),
    Input('my-output-1', 'value'))
def set_output_2_from_output_1(output_1_value):
    return some_function_doing_something(output_1_value)

Upvotes: 0

Related Questions