bokeh button callback only partially working

2019-08-27 19:37发布

I am plotting a complex interactive donut chart in bokeh. The code below is a simplification of a component of this chart.

I have a function which compiles a dataframe of data for the donut, and then converts it to a CDS. This data is then plotted as annular wedges. A radiobutton group should trigger a switch to a different dataframe (as CDS), and replot the annular wedge glyphs.

The example provide (for Jupyter Lab) works in one direction. When initially plotted the button.active == 0 (outer_1). When outer_2 is selected, the chart correctly changes to plot the second dataframe (cds).

But when the outer_1 button is pressed, the glyphs do not change back. The callback is triggered - as the title changes. But the glyphs do not change.

Why are the glyphs not changing in subsequent button presses / callbacks?

I've read a few similar SO posts, and also reviewed a number of bokeh examples (the weather example here is similar)

import pandas as pd
from math import pi

from bokeh.plotting import figure

from bokeh.layouts import column, row 
from bokeh.models.widgets import RadioButtonGroup
from bokeh.io import curdoc
from bokeh.models.sources import ColumnDataSource
from bokeh.layouts import column, row

from bokeh.plotting import show, output_notebook
output_notebook()

df = pd.DataFrame({'start':[pi/2, pi, 3*pi/2],
                   'end' :[pi/2+1.5, pi+1.5, (3*pi/2)+1.5],
                   'inner': [100,100,100],
                   'outer': [200,200,200],
                   'color':['red','green','blue']})

df_2 = pd.DataFrame({'start':[pi/2, pi, 3*pi/2],
                   'end' :[pi/2+1, pi+1, (3*pi/2)+1],
                   'inner': [100,100,100],
                   'outer': [250,300,350],
                    'color':['orange','gray','purple']})

data_1 = ColumnDataSource(data=df)
data_2 = ColumnDataSource(data=df_2)


def create_doc(doc):

    button = RadioButtonGroup(labels=["outer_1", "outer_2"], active=0)

    inputs = column(button)

    p = figure(plot_width=600, plot_height=600, title="data_1",
                   x_axis_type=None, y_axis_type=None,
                   x_range=(-300, 300), y_range=(-300, 300),
                   min_border=0, outline_line_color=None,
                   background_fill_color='white', toolbar_location="above")

    circle = p.circle(0,0, radius=100, fill_alpha=0, line_color='grey', line_alpha=0.4)

    source = [data_1, data_2][button.active]

    segments = p.annular_wedge(0,0,'inner', 'outer', 'start', 'end', color='color', alpha=0.6, source=source, name='segments')


    r = row (inputs,p)


    def callback(attr, old, new):
        if button.active == 1:
            p.title.text = 'data_2  {}'.format(button.active)
            source.data.update(data_2.data)
        elif button.active == 0:
            p.title.text = 'data_1 {}'.format(button.active)
            source.data.update(data_1.data)

    button.on_change('active', callback)

    doc.add_root(r)


show(create_doc)

The glpyhs change successfully once, but not again, although the changing title text (on button presses) indicate that the buttons & callback continue to work partially.

1条回答
我只想做你的唯一
2楼-- · 2019-08-27 20:07

Why converting to DataFrame knowing that internally the data of ColumnDataSource is a dictionary? The following code works fine for Bokeh v1.1.0

from math import pi
from bokeh.plotting import figure, show, curdoc, Row, Column, output_notebook
from bokeh.models import RadioButtonGroup

output_notebook()

data1 = {'start':[pi/2, pi, 3*pi/2],
        'end' :[pi/2+1.5, pi+1.5, (3*pi/2)+1.5],
        'inner': [100,100,100],
        'outer': [200,200,200],
        'color':['red','green','blue']}

data2 = {'start':[pi/2, pi, 3*pi/2],
         'end' :[pi/2+1, pi+1, (3*pi/2)+1],
         'inner': [100,100,100],
         'outer': [250,300,350],
         'color':['orange','gray','purple']}

def create_doc(doc):

    button = RadioButtonGroup(labels=["outer_1", "outer_2"], active=0)

    p = figure(plot_width=600, plot_height=600, title="data_1",
                   x_axis_type=None, y_axis_type=None,
                   x_range=(-300, 300), y_range=(-300, 300),
                   min_border=0, outline_line_color=None,
                   background_fill_color='white', toolbar_location="above")

    circle = p.circle(0,0, radius=100, fill_alpha=0, line_color='grey', line_alpha=0.4)
    segments = p.annular_wedge(0,0,'inner', 'outer', 'start', 'end', color='color', alpha=0.6, source = data1, name='segments')

    def callback(attr, old, new):
        if button.active == 0:
            print '000'
            p.title.text = 'data_1 {}'.format(button.active)
            segments.data_source.data = data1
        elif button.active == 1:
            print '111'
            p.title.text = 'data_2  {}'.format(button.active)
            segments.data_source.data = data2 

    button.on_change('active', callback)

    inputs = Column(button)
    r = Row(inputs,p)
    doc.add_root(r)

show(create_doc)

enter image description here

查看更多
登录 后发表回答