Search code examples
pythonserverbokehinteractiverenderer

Unable to interactively update renderer (i.e. circle, triangle, etc.) size using Bokeh server


I am able to update the color and line width of renderers in Bokeh server, but if I try to update the "size" nothing happens. You can type in a color (black, green, blue, etc) into the color box, and the renderers change color. I have been tweaking the sliders.py example to test this out. The code is below. If you change the glyph.size to glyph.line_width in the update_size method, then the plot does update the line width, but using "size", nothing happens. I am using Python 2.7.12, Ubuntu 16.04, bokeh server 0.12.5, and Tornado 4.4.2. Thanks for the help.

import numpy as np
from bokeh.io import curdoc
from bokeh.layouts import row, widgetbox
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Slider, TextInput
from bokeh.plotting import figure

# Set up data
N = 200
x = np.linspace(0, 4*np.pi, N)
y = np.sin(x)
source = ColumnDataSource(data=dict(x=x, y=y))

# Set up widgets
text = TextInput(title="title", value='my sine wave')
text2 = TextInput(title="size", value='6')
text3 = TextInput(title="color", value='red')
offset = Slider(title="offset", value=0.0, start=-5.0, end=5.0, step=0.1)
amplitude = Slider(title="amplitude", value=1.0, start=-5.0, end=5.0)
phase = Slider(title="phase", value=0.0, start=0.0, end=2*np.pi)
freq = Slider(title="frequency", value=1.0, start=0.1, end=5.1)

# Set up plot
plot = figure(plot_height=400, plot_width=400, title="my sine wave",
              tools="crosshair,pan,reset,save,wheel_zoom",
              x_range=[0, 4*np.pi], y_range=[-2.5, 2.5])

r = plot.circle('x', 'y', source=source, size=int(text2.value), line_alpha=0.6, color = text3.value, legend = 'test')
glyph = r.glyph

# Set up callbacks
def update_title(attrname, old, new):
    plot.title.text = text.value

text.on_change('value', update_title)

def update_size(attrname, old, new):
    glyph.size = int(text2.value)    

text2.on_change('value', update_size)

def update_color(attrname, old, new):
    glyph.fill_color = text3.value


text3.on_change('value', update_color)

def update_data(attrname, old, new):

    # Get the current slider values
    a = amplitude.value
    b = offset.value
    w = phase.value
    k = freq.value

    # Generate the new curve
    x = np.linspace(0, 4*np.pi, N)
    y = a*np.sin(k*x + w) + b

    source.data = dict(x=x, y=y)

for w in [offset, amplitude, phase, freq]:
    w.on_change('value', update_data)


# Set up layouts and add to document
inputs = widgetbox(text, text2, text3, offset, amplitude, phase, freq)
plot.legend.location = "top_left"
plot.legend.click_policy="hide"
curdoc().add_root(row(inputs, plot, width=800))
curdoc().title = "Sliders"

Solution

  • If you change the size, nothing happens immediately. But if you make another change (e.g. scrub one of the sliders) later, then the size does update, so this is definitely a bug of some sort. I'd suggest filing a GitHub issue:

    http://github.com/bokeh/bokeh/issues

    In the mean time, storing the size in the CDS works around the problem:

    # Set up data
    N = 200
    x = np.linspace(0, 4*np.pi, N)
    y = np.sin(x)
    s = np.ones_like(y) * 6
    source = ColumnDataSource(data=dict(x=x, y=y, size=s))
    

    Then use the size column in the glyph call:

    r = plot.circle('x', 'y', size='size', source=source, 
                    line_alpha=0.6, color = text3.value, legend = 'test')
    

    Finally change the callback to update the source instead:

    def update_size(attrname, old, new):
        source.data['size'] = np.ones_like(y) * int(text2.value)