0

I'm trying to create a bokeh plot where, with a slider, it updates multiple labels. The code I have below is a combination from two questions posed on stackoverflow here and here.

In the code i have static x and y coordinates, its just the text labels that i want to manipulate with the slider. With code below it doesnt seem to show or even update. Why is it not showing and/or updating?

from bokeh.plotting import figure, show, output_file
from bokeh.models import ColumnDataSource, Range1d, LabelSet, Label, Slider, TextInput, CustomJS
from bokeh.layouts import column, row

output_file("image.html")

plot = figure(x_range=(0, 100), y_range=(0, 100))


source = ColumnDataSource(
    data=dict(
        x=[55, 28, 18, 74, 76, 28, 32, 18, 60, 84, 44, 56, 56, 76],
        y=[8, 8, 33, 14, 72, 64, 46, 20, 52, 56, 84, 22, 36, 32],
        v1=prices_final["1"].values,
        v2=prices_final["2"].values,
        v3=prices_final["3"].values,
        v4=prices_final["4"].values,
    )
)

labels = ColumnDataSource(data=dict(x=[], y=[], t=[], ind=[]))
plot.add_layout(
    LabelSet(
        x="x",
        y="y",
        text="t",
        source=labels,
        level="overlay",
        x_offset=0,
        y_offset=0,
        render_mode="canvas",
        text_font_size="10pt",
        text_color="black",
        background_fill_color="white",
        border_line_color="black",
    )
)

# Set up widgets
slider = Slider(title="Hour of day", value=1.0, start=1.0, step=1.0, end=24.0)

code = """
    labels.data = {'x':[],'y':[],'t':[]}
    source.selected.indices = [slider.value]
    labels.data = {'ind':[slider.value],
            'x':[source.data.x],
            'y':[source.data.y],
            't':[source.data.v[slider.value]]}
    labels.change.emit()
    source.change.emit()
    """

callback = CustomJS(args=dict(source=source, slider=slider, labels=labels), code=code)
slider.js_on_change("value", callback)


layout = column(slider, plot)

show(layout)

prices final looks like:

prices_final['1'].values = array([ -5.25,   2.67,  10.67,  -0.95,  -9.54,
   -4.22,  -5.2 ,  -5.53, -9.33,  -3.49,  -0.47,  -8.96, -17.88,  -5.49])

all other prices_final have similar data structure.

3
  • Can you provide more information like import modules, var prices_final, so that we could reproduce easily? Commented Dec 25, 2019 at 13:44
  • updated requested information Commented Dec 25, 2019 at 15:35
  • I have wrote a working sample. Please try. Commented Dec 26, 2019 at 10:57

1 Answer 1

1

Edited: This code shows a graph with sliders and v1, v2, .. values as labels. You'll need to change v1, ... v4 part. I couldn't write short with many v, so it might be very long with 24.

from bokeh.layouts import row, column
from bokeh.models import CustomJS, Slider, LabelSet
from bokeh.plotting import figure, output_file, show, ColumnDataSource

x = [55, 28, 18, 74, 76, 28, 32, 18, 60, 84, 44, 56, 56, 76]
y = [8, 8, 33, 14, 72, 64, 46, 20, 52, 56, 84, 22, 36, 32]

label_selected = [''] * 14

# To simplify, make v1 to v4 with same num
v1 = [1] * 14
v2 = [2] * 14
v3 = [3] * 14
v4 = [4] * 14

# list of all v1,v2...
v = [v1, v2, v3, v4]

source = ColumnDataSource(
    data=dict(x=x, y=y, v=v, label_selected=label_selected)
)

plot = figure(x_range=(0, 100), y_range=(0, 100), plot_width=400, plot_height=400)

plot.circle(x, y, radius=5, fill_color="red", fill_alpha=0.6, line_color=None)

slider = Slider(title="Hour of day", value=1.0, start=1.0, step=1.0, end=24.0)

code = """
    const data = source.data;
    const A = hour.value; /* hour starts from 1*/
    const x = data['x'];
    const y = data['y'];
    let label_selected = data['label_selected'];
    const v1 = data['v'][0];
    const v2 = data['v'][1];
    const v3 = data['v'][2];
    const v4 = data['v'][3];

    if (A == 1){
        label_list = v1
    } else if (A == 2) {
        label_list = v2
    } else if (A == 3) {
        label_list = v3
    } else if (A == 4) {
        label_list = v4
    }
    for (var i = 0; i < label_selected.length; i++){
        label_selected[i] = label_list[i]

    } 
    source.change.emit();
"""


callback = CustomJS(
    args=dict(source=source, hour=slider),
    code=code
)

slider.js_on_change("value", callback)

labels = LabelSet(
    x="x",
    y="y",
    text="label_selected",
    level="glyph",
    x_offset=5,
    y_offset=5,
    source=source,
    render_mode="canvas",
)

plot.add_layout(labels)

layout = row(plot, column(slider))

output_file("slider.html", title="slider.py example")

show(layout)

Showing snapshot with slider 3... enter image description here

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your help so far, but it's not really what i was after, although it does show now. Basically what I want: in my code you can see I make multiple v's: v1 v2, v3 and in the end up until v24. I want to use the slider to call all of the values in a specific v, given the coordinates in x and y which will never change. So i want for example when the slider is on value 1, to show all of v1's values on the x and y coordinates. When i slide the slider to 2, it should call v2 with the same x and y. sorry if that was unclear
Sorry, I misunderstood. Then edited as use all of v1 (or v2...) as slider nums. Please try.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.