Is there a way to set a custom formatter for OpenURL?

I want to redirect the user to a specific URL address whenever he clicks on one of the boxes in a vbar_stack plot. The issue is that the URL is determined by the sub-categories that each bar is divided to.
For instance, consider the following stacked bar plot:


The categories are fruits and the sub-categories are days. The URL address depends on the day of the box that was clicked.
Usually I would use something like:

tap_tool = plot.select(type=TapTool)
tap_tool.callback = OpenURL(url=f"https://{host}/@path")

but in this case the @path value changes according to the fruits.
I know that the hover tool supports custom formatters, is it possible somehow to use a custom formatter with OpenURL as well? If not, any other ideas?

Hi @roinr,

Your best bet is a CustomJS callback that opens a URL you construct based on the selection. For more specifics, please provide code for a minimal, reproducible example (it looks like you’ve got one, based on the image you posted) that we can modify to provide a working illustration.

Thanks @carolyn!
Here’s the code:

from bokeh.plotting import figure
from bokeh.io import show

fruits = ["Banana", "Orange"]
days = ["sunday", "monday"]
colors = ["red", "green"]
data = {
    "fruits": fruits,
    "sunday": [1, 2],
    "monday": [2, 3],
}

plot = figure(x_range=fruits)
plot.vbar_stack(
    days,
    x='fruits',
    color=colors,
    width=0.9,
    source=data,
    legend_label=days
)

show(plot)

@carolyn After adding the URL addresses to the data object like so:

data = {
    "fruits": fruits,
    "sunday": [1, 2],
    "monday": [2, 3],
    "link": [{"sunday": <sunday_url>, "monday": <monday_url>}] * len(fruits)
}

I managed to implement the callback like:

tap_tool = self.plot.select(type=TapTool)
tap_tool.callback = CustomJS(args=dict(plot=self.plot), code="""
    for (const renderer of plot.renderers) {
        if (renderer.data_source.selected.indices.length) {
            const day = renderer.name;
            const day_url = `http://${renderer.data_source.data.link[0][day]}`;
            window.open(day_url, '_blank');
        }
    }
""")

I’m glad that it works but I was hoping for a more elegant solution like having a custom formatter.
Anyways, thank you so much for the idea!

1 Like

Oh excellent! I was fiddling with your code, but you got there faster than I did. :slight_smile: You’re very welcome, and good luck with your project!

1 Like