First of all I would like to extend my congratulations to the developers, as of recent I checked on the different plotting options with Python, and Bokeh has delivered! I recently published some nice XPS spectrum deconvolution for scientific publication via Bokeh! I may display these pictures here once editorial publishes the document.
Now I am heading towards server applications, I would like to develop a GUI for electrochemical readouts. But I am currently struggling with the add_periodic_callback and remove_periodic_callback methods.
I require two buttons, one for data acquisition and another one to stop acquisition. So far, I am able to add periodic callbacks at the global scope, and then remove these via on_click function.
However, I am not able to add periodic callbacks from a button event and then delete these from a second one. Evidently, this problem has to do with the scope of variables/classes, but I haven’t found my way around it. I provide an example code, which is a modified version of the “Updating from Unlocked Callbacks” example.
from functools import partial
import time
from concurrent.futures import ThreadPoolExecutor
from tornado import gen
from bokeh.document import without_document_lock
from bokeh.models import ColumnDataSource
from bokeh.plotting import curdoc, figure
from bokeh.models.widgets import Button
from bokeh.layouts import column, row
source = ColumnDataSource(data=dict(x=[0], y=[0], color=["blue"]))
i = 0
doc = curdoc()
executor = ThreadPoolExecutor(max_workers=2)
def blocking_task(i):
time.sleep(1.5)
return i
@gen.coroutine
def locked_update(i):
source.stream(dict(x=[source.data['x'][-1]+1], y=[i], color=["blue"]))
@gen.coroutine
@without_document_lock
def unlocked_task():
global i
i += 1
res = yield executor.submit(blocking_task, i)
doc.add_next_tick_callback(partial(locked_update, i=res))
@gen.coroutine
def update():
source.stream(dict(x=[source.data['x'][-1]+1], y=[i], color=["red"]))
#print("Just streamed")
p = figure(x_range=[0, 100], y_range=[0, 20])
l = p.circle(x='x', y='y', color='color', source=source)
#---------------------------#
# Buttons #
#---------------------------#
Add_Callbacks = Button(label='Add Callbacks', button_type='success', width=320)
Remove_Callbacks = Button(label='Remove Callbacks',
button_type='warning', width=320)
def callback_Add_threads():
#NOTE: Adding callbacks from here, can't be removed
nonlocal callback_unlocked_task = doc.add_periodic_callback(unlocked_task, 1000)
callback_update = doc.add_periodic_callback(update, 200)
def callback_Remove_threads():
#The following functions are not able to acces these threads
doc.remove_periodic_callback(callback_unlocked_task)
doc.remove_periodic_callback(callback_update)
Add_Callbacks.on_click(callback_Add_threads)
Remove_Callbacks.on_click(callback_Remove_threads)
#NOTE: If I add the periodic callbacks from here, these can be removed by my "remove" button.
#UNCOMMENT callback_unlocked_task = doc.add_periodic_callback(unlocked_task, 1000)
#UNCOMMENT callback_update = doc.add_periodic_callback(update, 200)
doc.add_root(row(p, column(Add_Callbacks, Remove_Callbacks)))