Plotly Dash tabs and interactive graphs - python

In my dash I have a set of tabs with a graph inside. Each tab has one graph. I have a 'clickdata' callback that runs a function every time you click on one of the graphs, and it works so far I don't change the tab. But once I change the tab the 'clickdata' callback to the graphs stop working. Any idea?
In case it help, this is the structure of my code:
app = dash.Dash(__name__)
app.layout = html.Div([
... #deleted code
html.Button(id='Calculate_button',
n_clicks=0,
children='Calculate',
style={'fontSize':18,
'width':'100%'}),
html.Div([
dcc.Tabs(id="tabs",
value='tab-1',
children=[dcc.Tab([dcc.Graph(id='LapLabels',
style={'height':1000,
'paddingTop':30})],
label='Lap Labels',
value='tab-1',
id='tab-1'),
dcc.Tab([dcc.Graph(id='RacePlot',
style={'height':1000,
'paddingTop':30})],
label='Raceplot',
value='tab-2',
id='tab-2'),])])])
#app.callback(Output('LapLabels','figure'),
[Input('Calculate_button','n_clicks')],
[State('input1','value'),
State('input2','value'),
State('csvcontainer','value')])
def update_Labels(n_clicks,Traffic_Trigger,Slow_Percent2best,path):
return LapLabels(Traffic_Trigger,Slow_Percent2best,path) #this function returns a figure
#app.callback(Output('Calculate_button','n_clicks'),
[Input('LapLabels','clickData'),
Input('RacePlot','clickData')],
[State('csvcontainer','value')])
def modsc_Labels(hoverData,hoverDataRplot,path):
return myfunc(hoverData,hoverDataRplot,path) #this function updates the file that LapLabels reads

Related

Dash generalize progress bar

I am developing a data app using dash. I am doing a file copy task for that I am showing the progress bar.
I have written this code from dash website as reference. This is pseduo code structure
import dash_bootstrap_components as dbc
from dash import Input, Output, dcc, html, ctx
progress = html.Div(
[
dcc.Interval(id="progress_interval", max_intervals=500, n_intervals=0, interval=2000),
dbc.Progress(id="progress"),
]
)
copy_button = dbc.Button(id="btn_copy", children="Copy", className="btn btn-success"),
#app.callback(
[Output("progress", "value"), Output("progress", "label")],
[
Input("progress_interval", "n_intervals"),
Input("btn_copy", "n_clicks")
],
)
def update_copy_progress(n):
triggered_id: Any = ctx.triggered_id
# use n_intervals constrained to be in 0-100
if triggered_id == "progress_interval":
progress = min(n % 110, 100)
# only add text after 5% progress to ensure text isn't squashed too much
return progress, f"{progress} %" if progress >= 5 else ""
if triggered_id == "btn_copy":
# start some background process
long_running_file_copy_task()
The progress bar starts running when clicked on button and display 5% 6% 7%....
say I am copying 100MB file size.
Issues, I noticed:
File copied is 10 MB on terminal but on UI progress bar displaying 40%
File copy is going on and once progress bar reaches 100% it gets refreshed and starts again from 1%
How in real web application the progress bar is handled say for file copying task for different file sizes and for any long running tasks. How to generalize the progress bar.
Please suggest some direction.

Dash: how to write callback for html.Form component

I am developing a dash app. I am creating a form using html.Form component.
html.Form(
id="form_upload",
name="form_upload",
method="POST",
action="/upload",
encType="multipart/form-data",
# accept="application/octet-stream",
children=[
dbc.Input(id="ip_name", name="ip_name", type="text"),
dbc.Input(id="ip_upload", name="ip_upload", type="file"),
dbc.Input(id="ip_submit", name="ip_submit", type="submit"),
],
),
But I have no clue how to write a callback for above form, so that I can access and process the form inputs(request payload) i.e. name and file contents.
I read offical document and searched alot but didnot find any demo or example.
Please help.
do you want the callback to trigger whenever an input is typed (for every letter)? If so you can trigger the callback using the 'value' parameter from the Input field. See dbc examples below:
import dash_bootstrap_components as dbc
from dash import Input, Output, html
text_input = html.Div(
[
dbc.Input(id="input", placeholder="Type something...", type="text"),
html.Br(),
html.P(id="output"),
]
)
#app.callback(Output("output", "children"), [Input("input", "value")])
def output_text(value):
return value
If you dont want it to trigger with every input, you should add a submit button to the form. In which case you can use the Buttons n_click parameter for the trigger:
import dash_bootstrap_components as dbc
from dash import Input, Output, html
button = html.Div(
[
dbc.Button(
"Click me", id="example-button", className="me-2", n_clicks=0
),
html.Span(id="example-output", style={"verticalAlign": "middle"}),
]
)
#app.callback(
Output("example-output", "children"), [Input("example-button", "n_clicks")]
)
def on_button_click(n):
if n is None:
return "Not clicked."
else:
return f"Clicked {n} times."
Good luck =)

How to hide scrollbar in Plotly Dash Table

I have table with a large number of rows, and I want to show everything inside my app.
By default, dash_table.DataTable adds a scrollbar when the table is too long. I would like to remove that scrollbar.
Let's say you have the following application, where the height is set to 300px and overflow is automatic:
import dash
import dash_table
import dash_html_components as html
import pandas as pd
df = pd.read_csv(
"https://raw.githubusercontent.com/plotly/datasets/master/1962_2006_walmart_store_openings.csv"
)
app = dash.Dash(__name__)
table = dash_table.DataTable(
id="table",
columns=[{"name": i, "id": i} for i in df.columns],
data=df.to_dict("records"),
style_table={"height": "300px", "overflowY": "auto"},
)
app.layout = html.Div([html.H1("Header"), table, html.Button("Click here")])
if __name__ == "__main__":
app.run_server(debug=True)
You will get this resulting app:
In your case, you want to hide the scroll bar. You might be tempted to change the style_table to:
style_table={"height": "300px", "overflowY": "show"}
Although the entire table will be shown, this unfortunately means the button will be hidden since the table is overflowing beyond the designated height:
So the correct change is to simply set the height of the table to be unlimited:
style_table={"height": None}
And the button will show up correctly:
Controlling the height of the table is thoroughly documented in Dash Table docs. It will show you different ways to use overflowY.
add this argument into dash_table.DataTable:
virtualization=False
and you don't need to touch the height

Changing the pattern-matching call depending on value of radio button in Dash

I am creating a Dash app that allows the user to either enter URLS to be screenshot and then analyzed or the user can upload their own images. I am trying to get the main app.py file right so the inputs can be sent to one of three different functions (one that only takes screenshots of the websites they listed, one that uses robots to search for similar websites and then takes screenshots of those to be analyzed, or just having the user upload their own images to be analyzed). Note that once they hit the "Submit" button, that the relevant inputs (the email, company name, and URL/images) will be passed off to an rq job. I understand how to do this part, so for the example, we can just print out the relevant inputs in the web app just to confirm that we have the right inputs.
The idea for the UI is that it would first have a section to put your email (and other info), and then start with radio buttons for each screenshot option. Then depending on the user's preference of the inputs they want to provide, they would either be shown a place for them to enter the different URLs or a place to upload their images (See image here).
It seems that pattern-matching would be useful for them to add more URLs, but I can't quite figure out how to allow different types of inputs using it. Here's what I have so far:
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, MATCH, ALL
app = dash.Dash(__name__, external_stylesheets = ['https://codepen.io/chriddyp/pen/dZVMbK.css'])
server = app.server
app.layout = html.Div([
html.Div([
html.H6("""E-mail address:"""),
dcc.Input(
id='rec-email',
placeholder="youremail#domain.com",
size=30
),
html.H6("""Company Name:"""),
dcc.Input(
id='company-name',
size=30
),
]),
html.Div([
dcc.RadioItems(
id='radio-option',
options=[
{'label': "Only analyze these websites", 'value': 'exact_sites'},
{'label': "Allow robot to analyze additional related websites", 'value': 'robot_sites'},
{'label': "I want to upload my own images", 'value': 'upload_images'}
],
value='exact_sites'
),
]),
html.Div(id='website-methods-output')
])
#app.callback(
Output('website-method', 'children'),
[Input('radio-option', 'value')],
[State('rec-email', 'value'),
State('company-name', 'value')]
)
def display_inputs(radio_option, email, company):
if radio_option=="upload_images":
return html.Div([
id='inputs-start',
dcc.Upload(
id='input-upload',
children=html.Div([
'Drag and Drop or ',
html.A('Select Image to be Analyzed.')
]),
style={
'width': '30%',
'height': '60px',
'lineHeight': '60px',
'borderWidth': '1px',
'borderStyle': 'dashed',
'borderRadius': '5px',
'textAlign': 'center',
'margin': '10px'
},
# Allow multiple files to be uploaded
multiple=False # Maybe change to True?
),
children=[],
html.Button("Add Image", id="add-image", n_clicks=0)
])
else:
return html.Div([
id='inputs-start'
dcc.Input(
placeholder='www.website.com'
),
children=[],
html.Button("Add URL", id="add-url", n_clicks=0)
])
if __name__ == '__main__':
app.run_server(debug=True)
What if you have an input for each of the possible radio button options, but only show the one that's selected? You can set up pattern-matching callbacks for all the inputs, and use just one at a time. Perhaps a change to the radio button selection would not only change which options are shown/hidden, but clear all of them as well, so you can be sure to accept only entries in the correct one.
Edit:
This really goes beyond the scope of a single question, so I'm leaving out a lot of details here to keep from writing an entire Dash app. I hope this helps. These are some possible function definitions for each of the different types of callbacks I mentioned in the comments.
Show/hide sections
#app.callback(
Output('inputs-container', 'children'),
[Input('radio-option', 'value')])
def callback_func_show_hide(radio_selection):
# Add the function logic
pass
Add a new input for a section
#app.callback(
Output('inputs-type-1-container', 'children'),
[Input('add-input-1', 'n_clicks')]
[State('inputs-type-1-container', 'children')])
def callback_func_add_input_1(add_input_click, previous_children):
# Add the function logic. You'll need the previous_children in
# order to append new inputs
pass
Use inputs from a section
#app.callback(
Output('some-output', 'children'),
[Input({'type': 'upload-1', 'index': dash.dependencies.ALL}, 'value')])
def callback_func_process_upload_1(uploaded_data_list):
# Add the function logic. Use uploaded_data_list to process everything.
# Output can be whatever you want to let the user know the task is done
pass

Dash modal with multiple buttons that open it

Following this part of the docs: https://dash-bootstrap-components.opensource.faculty.ai/l/components/modal I've created a modal in my Dash app. The trigger for the modal will be dynamically rendered thumbnails. When any of them is clicked, the modal should open and display the image from the thumbnail as it's body.
Is is possible, inside Dash, to have multiple buttons (I don't know how many will there be, depending on how many thumbnails in the database) that will all open the same modal dialog and pass some of their data to the modal (such as img src in my case)?
The input in the example above is simple:
[
Input("open", "n_clicks"), Input("close", "n_clicks")
],
but in reality I don't know how many will there be and can't hardcode an ID.
Any suggestions?
Yes, you can have multiple buttons open a modal. Just as you showed, the callback would have an Input for each one. No, you cannot create them dynamically. Dash does not play well with any ID that is not in the layout at the start of running the app.
Create a set of buttons dynamically using the below list comprehension:
[dcc.Button(x, id={'type': 'thumbnail_button', 'index': x}) for x in thumbnail_list]
Use the pattern-matching callback to open modal when any of these buttons are clicked:
#app.callback(
Output('your-modal', 'is_open'),
[Input({'type': 'thumbnail_button', 'index': ALL}, 'n_clicks')]
)
def handle_button_click(n_clicks):
invoker = [p['prop id'] for p in dash.callback_context.triggered][0]
invoker_type = json.loads(invoker.split('.')[0])['type']
invoker_index = json.loads(invoker.split('.')[0])['index']
if invoker_type == "thumbnail_button":
return not is_open
else:
return is_open
Lastly the imports:
from dash.dependencies import Input, Output, ALL

Categories