4
votes

I have a dash DataTable object with deleteable rows and columns. I would like to update a figure based on the visible rows. I am not sure how to create the callback and what arguments to pass. The data stored in the table object might actually not change when deleting the rows in the browser.

from dash_table import DataTable

def lineplot(id, df, cols, title=None, xlabel=None, ylabel=None, x=None, 
             xaxis_type=None, yaxis_type=None, plotly_config=plotly_config,
            ):

    if x is None:
        x_values = df.index
        xlabel = df.index.name
    else:
        x_values = df[x]

    data = [{
        'x': x_values,
        'y': df[col],
        'name': col }  for col in cols]

    layout = go.Layout(
        title=go.layout.Title(
            text=title,
            xref='paper',
            xanchor='center'
        ),
        xaxis = go.layout.XAxis(
            title=go.layout.xaxis.Title(
                text=xlabel,
                font=plotly_config['font'],

            ),
            type=xaxis_type
        ),
        yaxis=go.layout.YAxis(
            title=go.layout.yaxis.Title(
                text=ylabel,
                font=plotly_config['font'],
            ),
        type=yaxis_type
        ),
        showlegend=True
    )


    return dcc.Graph(
        id=id,
        figure={'data': data,
                'layout': layout},
        style={"max-width": "600px", 
               "margin": "auto", 
               "display": "inline-block"})

def table(id, df):
    dt = DataTable(
        id=id,
        data=df.to_dict('records'),
        columns=[{"name": i, 
                  "id": i, 
                  "deletable": True, 
                  "searchable": True} for i in df.columns], 
        sorting=True, 
        style_cell={
            'minWidth': '0px', 
            'maxWidth': '180px',
            'whiteSpace': 'no-wrap',
            'overflow': 'hidden',
            'textOverflow': 'ellipsis'},
        style_table={'overflowX': 'scroll'},
        row_deletable=True,
        pagination_mode="fe",
        pagination_settings={
                "displayed_pages": 1,
                "current_page": 0,
                "page_size": 15},
        navigation="page"
        )
    return dt

app = dash.Dash(__name__)
app.layout = html.Div(children=[
    table(id='table', df=pd.DataFrame(...)),
    lineplot(id='plot',...)
])

@app.callback(Output('plot', 'data'),
              [Input('table', 'data')])
def update_graph(data):
    return pd.DataFrame(data)

if __name__ == '__main__':
    app.run_server(port=8888, debug=True)
1

1 Answers

2
votes

I am not sure how to create the callback and what arguments to pass.

The arguments that you need must contain the information in your table, so that you can update your graph accordingly. You can find the attributes that you can gather from components on the online documentation: https://dash.plot.ly/datatable/reference.

The data stored in the table object might actually not change when deleting the rows in the browser.

I'm not sure if I understood what you mean by this. If you delete rows in your table via the web interface you will have an according change in the data that gets passed to your update function which makes it possible to update the graph accordingly.

I have made a few adjustments to your code so that it looks more natural to me. At some places I had to make changes because I did not have enough information about your goals. Below you can see a working example that you have to adjust accordingly to your needs. I created a hard coded test dataframe. You need to create it dynamically if you want to load your data during runtime.

from dash_table import DataTable
from dash.dependencies import Input, Output
import dash
import dash_html_components as html
import dash_core_components as dcc
import pandas as pd
import plotly.graph_objs as go


raw_data = {
        'column1': [4, 24, 31, 2, 3],
        'column2': [25, 94, 57, 62, 70]
}
test_df = pd.DataFrame(raw_data)


app = dash.Dash(__name__)
app.layout = html.Div(children=[
    DataTable(
        id='table',
        data=test_df.to_dict('records'),
        columns=[{"name": i,
                  "id": i,
                  "deletable": True,
                  "searchable": True} for i in test_df.columns],
        sorting=True,
        style_cell={
            'minWidth': '0px',
            'maxWidth': '180px',
            'whiteSpace': 'no-wrap',
            'overflow': 'hidden',
            'textOverflow': 'ellipsis'},
        style_table={'overflowX': 'scroll'},
        row_deletable=True,
        pagination_mode="fe",
        pagination_settings={
            "displayed_pages": 1,
            "current_page": 0,
            "page_size": 15},
        navigation="page"
    ),
    dcc.Graph(
        id='plot',
        style={"max-width": "600px",
               "margin": "auto",
               "display": "inline-block"})
])


@app.callback(Output('plot', 'figure'),
              [Input('table', 'data'),
               Input('table', 'columns')])
def update_graph(data, cols):
    df = pd.DataFrame(data, columns=[c['name'] for c in cols])
    x_values = df.index

    data = [{
        'x': x_values,
        'y': df[col['name']],
        'name': col['name']} for col in cols]

    layout = go.Layout(
        title=go.layout.Title(
            text='title',
            xref='paper',
            xanchor='center'
        ),
        xaxis=go.layout.XAxis(
            title=go.layout.xaxis.Title(
                text='x-title'
            ),
            type=None
        ),
        yaxis=go.layout.YAxis(
            title=go.layout.yaxis.Title(
                text='y-title'
            ),
            type=None
        ),
        showlegend=True
    )

    return {"data": data, "layout": layout}


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