Hi Dash Community! This is my first post, so please let me know if its in the wrong category.
I am trying to create a simple gallery to display images the user can upload. I want to have previous and next buttons to click backward and forward through the array of uploaded images. So far, I am using bootstrap buttons which have n_click attribute. Iโm not sure how to prevent indices that are negative or go beyond the length of the list. This is what I have so far:
@app.callback(Output("displayProcessedImage", "children"),
[Input("uploadImage", "contents"),
Input("prevImage", "n_clicks"),
Input("nextImage", "n_clicks")],
prevent_initial_call=True)
def displayImagesPrevious(contents, prevClicks, nextClicks):
if not contents:
return html.Div()
# get the index of image to view
lenContents = len(contents)
if nextClicks is None:
index = 0
elif prevClicks is not None:
dif = nextClicks - prevClicks
if dif < 0:
index = 0
else:
index = min(dif, lenContents - 1)
else:
index = min(nextClicks, lenContents - 1)
print("prev: ", prevClicks, " next: ", nextClicks, " index: ", index)
return dcc.Graph(figure=runModel(contents[index]))
The logic I am using to get the index does not work correctly when you spam a button then try to go in the other direction. Any help is appreciated!
Thank you @AnnMarieW , this is almost exactly what I was looking for! The only difference is the carousel only seems to accept images, whereas I was trying to display the image as a Plotly figure so the user can zoom in and possibly annotate it. (I should have mentioned that in my original post!)
This will work perfectly as a temporary fix for now though!
Correct - the Carousel only displays images, so you were on the right track originally.
Hmm, on second thought, you might be able to do a hybrid. For example you could use the Carousel to scroll through the images, then if you want to zoom in and annotate, there could be a button to open that image as a Plotly figure for annotating etc. .
I figured out another way! Instead of literally trying to get the index from the n_clicks of next and previous buttons, I have a temporary index variable (using dcc.store) that is updated when the buttons are clicked IF it wonโt cause the index to be undefined.
Here is the code if anyone is trying to do something similar:
@cfg.app.callback(Output("displayProcessedImage", "children"),
[Input("uploadImage", "contents"),
Input("imageIndex", "data")],
prevent_initial_call=True)
def displayImages(contents, data):
"""
Displays the labeled image given the array of uploaded images and the index
"""
if not contents:
return html.Div()
# get the index of image to view
data = data or {'index': 0} # default index to 0 if not already set
index = data['index']
return dcc.Graph(figure=runModel(contents[index]))
@cfg.app.callback(Output("imageIndex", "data"),
[Input("prevImage", "n_clicks"),
Input("nextImage", "n_clicks")],
[State("imageIndex", "data"),
State("uploadImage", "contents")])
def updateIndex(prev, next, data, contents):
"""
When the next or previous buttons are clicked, update the image index
ensuring it stays within 0 <= index < len(contents)
"""
if not contents:
return data
# set default index if there is none
data = data or {'index': 0}
index = data["index"]
# get which button click triggered callback
trig = dash.callback_context.triggered[0]['prop_id']
# update data if necessary and return it
if trig == "prevImage.n_clicks" and index > 0:
data['index'] = index - 1
elif trig == "nextImage.n_clicks" and index < len(contents) - 1:
data['index'] = index + 1
else:
# index should not change, it would cause undefined index
raise PreventUpdate
return data