Simon
Simon

Reputation: 10150

Bokeh checkbox only updates when its checked

Im using bokeh server to plot a line graph, where theres a checkbox button that will flip the line if its checked. If its unchecked I want to see the original version of the line (unflipped). Following the flip/unflip, a second function is called to perform some other calculations:

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


def flip_signal(signal, flip):
    if flip:
        signal = -signal
    else:
        signal = signal

    return signal


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

plot = figure(plot_height=500, plot_width=850, title="test",
              tools="crosshair,pan,reset,save,wheel_zoom")
line_orig = plot.line('x', 'y', source=source, line_width=1, line_alpha=1)

flip_signal_btn = CheckboxGroup(labels=["Flip signal"])


def update_flip(attrname, old, new):
    if 0 in flip_signal_btn.active:
        flip = True
    else:
        flip = False

    # Update plot
    source.data = dict(x=source.data['x'], y=flip_signal(source.data['y'], flip))


def update_peaks(attrname, old, new):
    # do something else
    pass  


for w in [flip_signal_btn]:
    w.on_change('active', update_flip)
    w.on_change('active', update_peaks)

options = widgetbox(flip_signal_btn)
doc_layout = layout(row([options], height=200))

curdoc().add_root(row(plot, doc_layout, width=800))
curdoc().title = "checkbox"

The checkbox only seems to call update_flip when its checked, so in order to flip (or unflip) the signal I need to click it twice. For example, when I uncheck the box nothing happens, but I'm expecting it to unflip the signal. Rather it only unflips the signal if I uncheck and then check the box again

Upvotes: 1

Views: 1678

Answers (1)

bigreddot
bigreddot

Reputation: 34568

The callback is being invoked with the correct values on every button click, as can be verified with some print statements. The error is in your logic. Since you are operating on the current signal, rather than some original signal, you presumably always want to flip every time, unconditionally. Currently, you are only flipping every other button push, because this flip_signal(..., False) just returns the signal passed in, as-is. Changing the update_flip callback to always flip yields the behavior you want:

def update_flip(attrname, old, new):
    # flip the *current* data on *every* button toggle
    source.data = dict(x=source.data['x'], y=flip_signal(source.data['y'], True))

For your logic to work you would need a different function than your current flip_signal. You would need a function that always returns the original unflipped signal on False, and always returns the flipped signal on True. Contrast this with the current flip_signal, if you call it with False it gives you back whatever you passed in, regardless of whether it is the flipped or unflipped signal.

Upvotes: 1

Related Questions