This is the best I could come up with. When I create the plots (actually a set of sub_plots
), I add a trace of “highlights” drawn with markers, whose y
values are all initialized to None
. Then, whenever the user clicks a (valid) trace, I replace the associated highlights trace with one that has just one point at (x_click, y_click)
, the coordinates of the point that was clicked.
That’s pretty simple (though it seems like a lot of code for something so basic), the slightly annoying bit is that I have to keep track of the last click in order to remove it whenever there is a new one.
def delete_clicks(sub_plots, last_highlight, nb_points):
clicks_trace_index = last_highlight["curve_number"] + 1
clicks_trace = sub_plots["data"][clicks_trace_index]
clean_y = nb_points * [None]
clicks_trace["y"] = tuple(clean_y)
def update_click_highlight(click_data, sub_plots, current_highlights, last_highlight):
clean_click_data = click_data["points"][0]
curve_number = clean_click_data["curveNumber"]
# I don't want the click highlights to be clickable, nor the validated clicks, which also have their own trace
if curve_number % 3 != 0:
print("update_click_highlight: This is not a line but a trace of markers (either registered events or clicks)")
return
clicks_trace_index = curve_number + 1 # the clicks trace is inserted just after the main trace
clicks_trace = sub_plots["data"][clicks_trace_index]
nb_points = len(clicks_trace["x"])
click_index = clean_click_data["x"]
click_y_value = clean_click_data["y"]
if(len(current_highlights) == 0):
for i in range(nb_points):
current_highlights.append(None) # We need to append and cannot assign nb_points * [None], or the list won't persist
else:
current_highlights[last_highlight["x"]] = None # removing previous click
if curve_number != last_highlight["curve_number"]:
delete_clicks(sub_plots, last_highlight, nb_points)
current_highlights[click_index] = click_y_value
last_highlight["curve_number"] = curve_number
last_highlight["x"] = click_index
clicks_trace["y"] = tuple(current_highlights)
def main():
…
current_highlights = []
last_highlight = {"curve_number": -1, "x": -1}
…
@pn.depends(plot_pane.param.click_data, watch=True)
def get_global_plots_click_data(click_data):
update_click_highlight(click_data, sub_plots, current_highlights, last_highlight)
…
My real code is actually a bit more complicated because I have to do this for two sets of sub_plots
rather than just one here, but this should communicate enough information for someone meaning to replicate this approach.
Still, I’m not convinced I should mark it as a solution, because it feels like there should be a simpler way, and also because it’s not super fast. On a set of sub_plots
with just 3 sub_plots
, it’s basically instantaneous, but on another one with a few dozen sub_plots
, it takes maybe a second or so.