In a dash/plotly application how to make it so the lasso of the scatter plot component affects the state of the table component

I am working on a project with Dash where I have two separate apps running within the same Flask server. The first app (scatterplot_app) contains a scatter plot, and the second app (table_app) displays a table. I want to synchronize the selected data from the scatter plot and display it in the table. The reason they are seperate is because I am casting it to a react app and this makes it so it is possible to pick a graph to be displayed

This is what I have so far. I am able to lasso and have the table show up below the scatter plot but not in the table_app

# Table action buttons
add_button = dbc.Button("ADD TO", id='add-to-button', className='table-button', n_clicks=0, style={
                'background-color': 'rgb(100 168 68)',
                'color': 'white',
                'border': 'none',
                'margin': '0px 10px 0px 10px',
                'padding': '5px 10px',
                'cursor': 'pointer',
                'border-radius': '3px',
                'border-width': '0px',
                'box-shadow': '-1px 1px 3px 0.5px black',
                'float': 'right',
                'font-size': "12px"
                
            })

create_button = html.Button("CREATE", id='create-button', className='table-button', n_clicks=0, style={
                'background-color': 'rgb(100 168 68)',
                'color': 'white',
                'border': 'none',
                'margin': '0px 10px 0px 10px',
                'padding': '5px 10px',
                'cursor': 'pointer',
                'border-radius': '3px',
                'border-width': '0px',
                'box-shadow': '-1px 1px 3px 0.5px black',
                'float': 'right',
                'font-size': "12px"
                
            })

copy_button = html.Button("COPY", id='copy-button', className='table-button', n_clicks=0, style={
                'background-color': 'rgb(100 168 68)',
                'color': 'white',
                'border': 'none',
                'margin': '0px 10px 0px 10px',
                'padding': '5px 10px',
                'cursor': 'pointer',
                'border-radius': '3px',
                'border-width': '0px',
                'box-shadow': '-1px 1px 3px 0.5px black',
                'float': 'right',
                'font-size': "12px"
                
            })

csv_button = html.Button("CSV", id='csv-button', className='table-button', n_clicks=0, style={
                'background-color': 'rgb(100 168 68)',
                'color': 'white',
                'border': 'none',
                'margin': '0px 10px 0px 10px',
                'padding': '5px 10px',
                'cursor': 'pointer',
                'border-radius': '3px',
                'border-width': '0px',
                'box-shadow': '-1px 1px 3px 0.5px black',
                'float': 'right',
                'font-size': "12px",
                
            })





# Create Dash app for table visualization

table_app = Dash(__name__, server=server, url_base_pathname='/table/', external_stylesheets=[dbc.themes.BOOTSTRAP])

# =======
# table_app = dash.Dash(__name__, server=server, url_base_pathname='/table/', external_stylesheets=[dbc.themes.BOOTSTRAP])

# >>>>>>> 5394ee25269561eb5d1b8e0a0cb29478fd1b17bd



# Table app layout
table_app.layout = html.Div([
dbc.Container([
    dbc.Row([
        dbc.Col([
            
            html.H1('Selected Data', className='text-center', style={'margin-top': '10px', 'font-family': 'Roboto', 'font-size': '16px', 'float': 'left'}),
            html.Div([
            add_button,
            # temporary (maybe?) fix for table popups
            # Add To Set modal
        dbc.Modal(
            [
                dbc.ModalBody([
                    dbc.Label("Add To Set"),
            dbc.Textarea(id="textarea-input", placeholder="Title", style={"height": 100, 'resize': 'none'}),
                ], style={'padding': '10px', 'padding-top': '5px', 'padding-bottom': '0px'}),
                dbc.ModalFooter(
                    dbc.Col([
                        dbc.Button(
                        "Ok", id="add-to-close-ok", className="ms-auto", n_clicks=0, style={'background-color': 'rgb(0 105 63)', 'float': 'right', 'border-width': '0', 'border-radius': '3px', 'box-shadow':'-1px 1px 3px 0.5px black'}
                    ),
                        dbc.Button(
                        "Cancel", id="add-to-close", className="ms-auto", n_clicks=0, style={'background-color': 'rgb(255 255 255)', 'color': 'black', 'margin-right': '10px', 'float': 'right', 'border-width': '0', 'border-radius': '3px', 'box-shadow':'-1px 1px 3px 0.5px black'}
                    )
                    ])
                , style={'padding': '5px'}),
            ],
            id="add-to-modal",
            is_open=False,
            backdrop="static"
        ),
            create_button,
            #Create Set modal
            dbc.Modal(
            [
                dbc.ModalBody([
                    dbc.Label("Create New Set"),
            dbc.Textarea(id="textarea-input", placeholder="Title", style={"height": 100, 'resize': 'none'}),
                ], style={'padding': '10px', 'padding-top': '5px', 'padding-bottom': '0px'}),
                dbc.ModalFooter(
                    dbc.Col([
                        dbc.Button(
                        "Ok", id="create-close-ok", className="ms-auto", n_clicks=0, style={'background-color': 'rgb(0 105 63)', 'float': 'right', 'border-width': '0', 'border-radius': '3px', 'box-shadow':'-1px 1px 3px 0.5px black'}
                    ),
                        dbc.Button(
                        "Cancel", id="create-close", className="ms-auto", n_clicks=0, style={'background-color': 'rgb(255 255 255)', 'color': 'black', 'margin-right': '10px', 'float': 'right', 'border-width': '0', 'border-radius': '3px', 'box-shadow':'-1px 1px 3px 0.5px black'}
                    )
                    ])
                , style={'padding': '5px'}),
            ],
            id="create-modal",
            is_open=False,
            backdrop="static"
        ),
            copy_button,
            #Copy Set modal
            dbc.Modal(
            [
                dbc.ModalBody([
                    dbc.Label("Copy Set"),
                ], style={'padding': '10px', 'padding-top': '5px', 'padding-bottom': '0px'}),
                dbc.ModalFooter(
                    dbc.Col([
                        dbc.Button(
                        "Ok", id="copy-close-ok", className="ms-auto", n_clicks=0, style={'background-color': 'rgb(0 105 63)', 'float': 'right', 'border-width': '0', 'border-radius': '3px', 'box-shadow':'-1px 1px 3px 0.5px black'}
                    ),
                        dbc.Button(
                        "Cancel", id="copy-close", className="ms-auto", n_clicks=0, style={'background-color': 'rgb(255 255 255)', 'color': 'black', 'margin-right': '10px', 'float': 'right', 'border-width': '0', 'border-radius': '3px', 'box-shadow':'-1px 1px 3px 0.5px black'}
                    )
                    ])
                , style={'padding': '5px'}),
            ],
            id="copy-modal",
            is_open=False,
            backdrop="static"
        ),
            csv_button,
            #Download Set modal
            dbc.Modal(
            [
                dbc.ModalBody([
                    dbc.Label("Download Set"),
            dbc.Textarea(id="textarea-input", placeholder="Title", style={"height": 100, 'resize': 'none'}),
                ], style={'padding': '10px', 'padding-top': '5px', 'padding-bottom': '0px'}),
                dbc.ModalFooter(
                    dbc.Col([
                        dbc.Button(
                        "Ok", id="csv-close-ok", className="ms-auto", n_clicks=0, style={'background-color': 'rgb(0 105 63)', 'float': 'right', 'border-width': '0', 'border-radius': '3px', 'box-shadow':'-1px 1px 3px 0.5px black'}
                    ),
                        dbc.Button(
                        "Cancel", id="csv-close", className="ms-auto", n_clicks=0, style={'background-color': 'rgb(255 255 255)', 'color': 'black', 'margin-right': '10px', 'float': 'right', 'border-width': '0', 'border-radius': '3px', 'box-shadow':'-1px 1px 3px 0.5px black'}
                    )
                    ])
                , style={'padding': '5px'}),
            ],
            id="csv-modal",
            is_open=False,
            backdrop="static"
        ),
            
        ], className='ms-auto', style={'margin-right': '20px', 'display': 'flex', 'justify-content': 'flex-end'})
        ], className='button-container', width=12, style={'display': 'flex', 'align-items': 'center', 'position': 'fixed', 'z-index': '100', 'background-color': 'white', 'margin-top': '-10px','padding-top': '5px', 'padding-bottom': '5px', })
    ]),
    dbc.Row([
        dbc.Col(
            # data tables settings
            dash_table.DataTable(
                id='data-table',
                columns=[{"name": i, "id": i} for i in df.columns],
                data=df.to_dict('records'),
                page_size=100,
                style_table={'overflowY': 'auto'},
                style_cell={
                    'textAlign': 'left',
                    'padding': '10px',
                    'backgroundColor': 'white',
                    'color': '#333',
                    'fontSize': '12px'

                    
                },
                style_header={
                    'backgroundColor': 'white',
                    'fontWeight': 'bold',
                    'border': '1px solid #e9e9e9',
                    'color': '#333',
                    'fontSize': '12px',
                    'paddingTop': '40px'
                },
                style_data={
                    'border': '1px solid #e9e9e9'
                }
            ),
            width=12
        )
    ]),
    
], fluid=True, style={'backgroundColor': 'white', 'padding-top': '10px'})
])
# table_app.layout = table_layout

# Table modal functionality settings
# Four callbacks going into toggle functionality
@table_app.callback(
    Output("add-to-modal", "is_open"),
    [Input("add-to-button", "n_clicks"), Input("add-to-close", "n_clicks")],
    [State("add-to-modal", "is_open")],
)
@table_app.callback(
    Output("create-modal", "is_open"),
    [Input("create-button", "n_clicks"), Input("create-close", "n_clicks")],
    [State("create-modal", "is_open")],
)
@table_app.callback(
    Output("copy-modal", "is_open"),
    [Input("copy-button", "n_clicks"), Input("copy-close", "n_clicks")],
    [State("copy-modal", "is_open")],
)
@table_app.callback(
    Output("csv-modal", "is_open"),
    [Input("csv-button", "n_clicks"), Input("csv-close", "n_clicks")],
    [State("csv-modal", "is_open")],
)
def toggle_modal(n1, n2, is_open):
    if n1 or n2:
        return not is_open
    return is_open
    

# Scatterplot app
scatterplot_df = df

all_pred_years = scatterplot_df['Pred_Year'].unique()

top_100_dfs = []
for year in all_pred_years:
    year_df = scatterplot_df[scatterplot_df['Pred_Year'] == year]
    year_df = year_df.nlargest(100, 'Prediction')
    top_100_dfs.append(year_df)

top_100_df = pd.concat(top_100_dfs, ignore_index=True)
top_100_df['Pred_Year'] = top_100_df['Pred_Year'].astype(int)

x_axis_values = []
for year in sorted(set(top_100_df['Pred_Year'])):
    x_axis_values.append(year)
    x_axis_values.append(np.nan)

scatterplot_app = Dash(__name__, server=server, url_base_pathname='/scatterplot/', external_stylesheets=[dbc.themes.BOOTSTRAP])

scatterplot_layout = dbc.Container([
    dbc.Row([
        dbc.Col([
            dcc.Dropdown(
                id='color-selector',
                options=[
                    {'label': 'Breeder', 'value': 'Breeder'},
                    {'label': 'Population', 'value': 'Population'}
                ],
                value='Breeder',
                style={'width': '100%', 'margin-bottom': '10px'}
            )
        ], width=4),
        dbc.Col([
            html.Button("Download Data", id='download-button', n_clicks=0, className='table-button', style={
                'background-color': 'rgb(100 168 68)',
                'color': 'white',
                'border': 'none',
                'padding': '5px 10px',
                'cursor': 'pointer',
                'border-radius': '3px',
                'border-width': '0px',
                'box-shadow': '-1px 1px 3px 0.5px black',
                'font-size': "12px",
                'width': '100%'
            })
        ], width=2)
    ], className='align-items-center', style={'background-color': 'white', 'padding': '5px', 'position': 'fixed', 'z-index': '100', 'width': '100%'}),
    dbc.Row([
        dbc.Col([
            dcc.Graph(
                id='scatter-plot',
                style={'height': '400px', 'width': '100%'},
                config={
                    'modeBarButtonsToAdd': ['lasso2d'],
                    'displaylogo': False
                }
            )
        ], width=12)
    ], style={'margin-top': '60px'}),  # Adjust margin to account for fixed header
    dbc.Row([
        dbc.Col(
            dash_table.DataTable(
                id='scatter-data-table',
                columns=[{'name': i, 'id': i} for i in top_100_df.columns],
                data=[],
                style_table={'height': '200px', 'overflowY': 'auto'},
                style_cell={
                    'textAlign': 'left',
                    'padding': '10px',
                    'backgroundColor': 'white',
                    'color': '#333',
                    'fontSize': '12px',
                    'whiteSpace': 'normal',
                    'height': 'auto'
                },
                style_header={
                    'backgroundColor': 'white',
                    'fontWeight': 'bold',
                    'border': '1px solid #e9e9e9',
                    'color': '#333',
                    'fontSize': '12px'
                },
                style_data={
                    'border': '1px solid #e9e9e9',
                    'whiteSpace': 'normal',
                    'height': 'auto'
                }
            ),
            width=12
        )
    ])
], fluid=True, style={'backgroundColor': 'white', 'padding-top': '10px'})

scatterplot_app.layout = scatterplot_layout



@scatterplot_app.callback(
    Output('scatter-plot', 'figure'),
    [Input('color-selector', 'value')]
)
def update_scatter_plot(color_by):
    if color_by not in ['Breeder', 'Population']:
        raise PreventUpdate

    # Extract unique Male values
    unique_males = top_100_df['Male'].unique()

    categories = top_100_df[color_by].unique()
    colors = sns.color_palette("tab10", n_colors=len(categories)).as_hex()

    traces = []

    for idx, (category, color) in enumerate(zip(categories, colors)):
        group_df = top_100_df[top_100_df[color_by] == category]

        traces.append(go.Scatter(
            x=group_df['Male'],
            y=group_df['Prediction'],
            mode='markers',
            name=str(category),
            marker=dict(
                size=8,
                opacity=0.8,
                color=color,
            ),
            customdata=group_df.to_dict('records')
        ))

        mean_values = group_df.groupby('Male')['Prediction'].mean()
        for male, mean_value in mean_values.items():
            traces.append(go.Scatter(
                x=[male],
                y=[mean_value],
                mode='markers',
                name=str(category) + ' Mean',
                marker=dict(
                    size=10,
                    opacity=1,
                    color='red',
                    symbol='x'
                ),
                showlegend=False
            ))

    layout = go.Layout(
        title='Interactive Scatter Plot',
        xaxis={'title': 'Male Testers', 'tickvals': unique_males},  # Use unique Male values for tickvals
        yaxis={'title': 'Prediction'},
        height=400,
        margin=dict(l=50, r=50, b=50, t=50, pad=4)
    )

    return {'data': traces, 'layout': layout}

# Callback to download scatter plot data
@scatterplot_app.callback(
    Output('download-data', 'data'),
    [Input('download-button', 'n_clicks')],
    prevent_initial_call=True
)
def download_plot(n_clicks):
    return dcc.send_data_frame(top_100_df.to_csv, "data.csv")

# Callback to display selected data in table
@scatterplot_app.callback(
    Output('scatter-data-table', 'data'),
    [Input('scatter-plot', 'selectedData')]
)
def display_selected_data(selectedData):
    if selectedData is None:
        raise PreventUpdate
    
    selected_points = selectedData['points']
    selected_records = [point['customdata'] for point in selected_points]
    
    return selected_records

Hey @vvittal Welcome to the forums.

I’m not sure I understand. You have two apps. App1 contains a graph. App2 contains a table.

You want the selected points in the graph [app1] to filter the table in [app2]?

Yeah that is what I wanted, I was able to fix that my using some post and get requests using flask as for some reason when I would only use dcc.Store it would not work.