简体   繁体   中英

Bokeh not updating plot line update from CheckboxGroup

I'm following this great tutorial to play a little bit with Bokeh.

Basically, I have a figure with two independent line added to it. Everything is rendered properly but when I want to update nothing happens even if I checked that the new ColumnDataSource is well updated with the new values.

I render it using the command : bokeh serve --show my_app

Here is how I create my figure :

src_p6 = make_dataset(["select_a", "select_b"])
p6 = make_plot(src_p6)
select_selection = CheckboxGroup(labels=["select_a", "select_b"], active = [0, 1])
select_selection.on_change('active', update)
controls = WidgetBox(select_selection)
curdoc().add_root(column(controls, p6, width=1200))

def make_dataset(select_list):
  if 'select_a' in select_list and 'select_b' in select_list:
    tmp = pd.DataFrame({'time': df["time"], 
                       'a': df["a"], 
                       'b': df["b"]
                       })
  elif 'select_a' in select_list and 'select_b' not in select_list:
    tmp = pd.DataFrame({'time': df["time"], 
                       'a': df["a"]
                       })
  elif 'select_a' not in select_list and 'select_b' in select_list:
    tmp = pd.DataFrame({'time': df["time"], 
                       'b': df["b"]
                       })
  else:
    tmp = pd.DataFrame({'time': df["time"]
                       })

  src = ColumnDataSource(tmp)

  return src

def make_plot(plot_src):
  p = figure(plot_width=1000, plot_height=600, 
           title="Line x2 with hover and update",
           x_axis_label='Time', 
           y_axis_label='Values'
          )

  hover_content = [("Time", "@time")]

  if 'a' in plot_src.data:
    p.line(x='time', y='a', source=plot_src, legend="A", line_color="blue")
    hover_content.append(("A", "@a"))
  if 'b' in plot_src.data:
    p.line(x='time', y='b', source=plot_src, legend="B", line_color="red")
    hover_content.append(("B", "@b"))

  p.add_tools(HoverTool(tooltips=hover_content))

  return p

def update(attr, old, new):
  print(src_p6.data)

  select_to_plot = [select_selection.labels[i] for i in select_selection.active]

  new_src = make_dataset(select_to_plot)

  src_p6.data = new_src.data

  print("**********************")
  print(src_p6.data) # I see here that the data are well updated compared to the first print

My incoming data is JSON and looks like this :

# {"data":[{"time":0,"a":123,"b":123},{"time":1,"a":456,"b":456},{"time":2,"a":789,"b":789}]}
# data = json.load(data_file, encoding='utf-8')
# df = pd.io.json.json_normalize(data['data'])

Thank you for your insights

This will not function correctly:

src_p6.data = new_src.data

The ColumnDataSource is one of the most complicated objects in Bokeh, eg the .data object on a CDS is not a plain Python dict, it has lots of special instrumentation to make things like efficient streaming possible. But it is also tied to the CDS it is created on. Ripping the .data out of one CDS and putting assigning it to another is not going to work. We probably need to find a way to make that complain, I am just not sure how, offhand.

In any case, you need to assign .data from a plain Python dict, as all the examples and demonstrations do:

src_p6.data = dict(...)

For your sepcific code, that probably means having make_dataset just return the dicts it creates directly, instead of putting them in dataframes then making a CDS out of that.

First of all thanks to @bigreddot for his time and guidance.

One of my biggest problem was that I didn't actually wanted to update the values but rather just show/hide it, so just removing it from the source was not working .

Adding line in my make_plot function with a if statement doesn't work also because it is only called the first time the plot is created. For updates, it update the value on the figure but do not reconstruct everything from scratch... So if you start with only one line, I don't know how it will create a new line, if it's even possible...


I started to simplify my make_dataset function to only return a simple Python dict :

tmp = dict(time=df["time"], a=df["a"], b=df["b"])

But when I wanted to remove a line, I used an empty array even if there is better solutions (I was just playing with Bokeh here) : Line ON/OFF , Interactive legend

empty = np.empty(len(df["time"])); empty.fill(None)
tmp = dict(time=df["time"], a=df["a"], b=empty)

When I first create my plot I then do :

src_p6 = ColumnDataSource(data=make_dataset(["select_a", "select_b"]))
p6 = make_plot(src_p6)

And the update function update the .data of the ColumnDataSource with a basic Python dict :

new_src = make_dataset(select_to_plot)
src_p6.data = new_src

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM