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