I've been working on a project using bokeh visualization to display results from an agent based model (ABM) simulation. In a recent post, I got help getting my data to stream properly in a very simplified version of my simulation. My next task, which I thought would be a no-brainer, was to add a "reset" button to my layout so I could take my figure back to its initial state and run the simulation from "step 0" again. Surprisingly, there does not seem to be a simple way to do this. I have tried several different things, including re-initializing all my data and re-populating my ColumnDataSources, but I can't get the data from previous runs of the simulation to disappear. Here's a standalone code sample that illustrates the problem:
import colorcet as cc
from bokeh.server.server import Server
from bokeh.application import Application
from bokeh.application.handlers.function import FunctionHandler
from bokeh.plotting import figure, ColumnDataSource
from bokeh.models import Button
from bokeh.layouts import column
import random
def make_document(doc):
# make a list of groups
strategies = ['DD', 'DC', 'CD', 'CCDD']
# initialize some vars
step = 0
callback_obj = None
colors = cc.glasbey_dark
#num_colors = len(colors)
# create a list to hold all CDSs for active strategies in next step
sources = []
# Create a figure container
fig = figure(title='Streaming Line Plot - Step 0', plot_width=1400, plot_height=400)
# get step 0 data for initial strategies
for i in range(len(strategies)):
step_data = dict(step=[step],
strategy = [strategies[i]],
ncount=[random.choice(range(1, 100))])
data_source = ColumnDataSource(step_data)
color = colors[i]
# this will create one fig.line renderer for each strategy & its data for this step
fig.line(x='step', y='ncount', source=data_source, color=color, line_width=2)
# add this CDS to the sources list
sources.append(data_source)
def button1_run():
nonlocal callback_obj
if button1.label == 'Run':
button1.label = 'Stop'
button1.button_type='danger'
callback_obj = doc.add_periodic_callback(button2_step, 100)
else:
button1.label = 'Run'
button1.button_type = 'success'
doc.remove_periodic_callback(callback_obj)
def button2_step():
nonlocal step
data = []
step += 1
fig.title.text = 'Streaming Line Plot - Step '+str(step)
for i in range(len(strategies)):
step_data = dict(step=[step],
strategy = [strategies[i]],
ncount=[random.choice(range(1, 100))])
data.append(step_data)
for source, data in zip(sources, data):
source.stream(data)
def button3_reset():
step = 0
fig.title.text = 'Streaming Line Plot - Step '+str(step)
for i in range(len(strategies)):
init_data = dict(step=[step],
strategy = [strategies[i]],
ncount=[random.choice(range(1, 100))])
reset_source = ColumnDataSource(init_data)
print(init_data)
color = colors[i]
# this will create one fig.line renderer for each strategy & its data for this step
fig.line(x='step', y='ncount', source=reset_source, color=color, line_width=2)
# add this CDS to the sources list
sources.append(reset_source)
# add on_click callback for button widget
button1 = Button(label="Run", button_type='success', width=390)
button1.on_click(button1_run)
button2 = Button(label="Step", button_type='primary', width=390)
button2.on_click(button2_step)
button3 = Button(label="Reset", button_type='warning', width=390)
button3.on_click(button3_reset)
doc.add_root(column(fig, button1, button2, button3))
doc.title = "Now with live updating!"
apps = {'/': Application(FunctionHandler(make_document))}
server = Server(apps, port=5004)
server.start()
if __name__ == '__main__':
server.io_loop.add_callback(server.show, "/")
server.io_loop.start()
What I'm trying to do in my button3_reset
code is basically repeat the initialization at the top of the make_document
function. But even though that code works identically (apparent from a print output stuck in the middle of the button3
step), I can't get the figure to reset to its initial empty state. I've read thru a lot of stack overflow posts and other bokeh documentation and haven't found a simple answer to what I thought was a simple question: how do you reset a bokeh line plot back to its original state so you can run the data stream again from its starting point?
I am using bokeh 1.4.0 (anaconda won't let me update), python 3.7.6, spyder 4.0.1, and both Chrome & Brave browsers for visualizing.