My app displays data from multiple remote sources.
In the first cut, the plot won’t be shown until data from all sources are fetched. So the users need to wait a while, as some of the sources can be slow.
What I’d like to do is show a base plot, then progressively plot the data as they become available.
- fetch data in the background
- show the base plot to users.
- when data from a source is ready, plot them
What’d be a good way to do it? So far I could approximate it by using scheduling the remote data plot with doc.add_timeout_callback()
. See the attached example below.
Thanks!
Example codes:
notebook_url = "localhost:8888"
import asyncio
import time
import bokeh
from bokeh.models import Range1d
from bokeh.io import show, output_notebook
from bokeh.layouts import layout
from bokeh.plotting import figure, ColumnDataSource
print("bokeh v:", bokeh.__version__)
def create_plot_src_func(fig, label, delay_sec, color):
# simulate fetching data remotely that takes time
def get_remote_data():
time.sleep(delay_sec)
source = ColumnDataSource(data=dict(
x=[delay_sec],
y=[delay_sec],
))
return source
# start fetching remote data right away in background
data_task = asyncio.create_task(asyncio.to_thread(get_remote_data))
# return an async function that will get the remote data and plot them
async def do_plot():
source = await data_task
fig.scatter(source=source, size=10 + (delay_sec ** 2) * 3, marker="square", fill_color=color)
return do_plot
def test_bokeh_server(notebook_url="localhost:8888"):
async def async_create_interact_ui():
fig = figure(
title="Test Bokeh Progerssive Plot",
)
fig.x_range = Range1d(0, 10)
fig.y_range = Range1d(0, 10)
# approximate progressive plot by ordering the function in the expected execution time
plot_fns = [
create_plot_src_func(fig, "fast", 1, "blue"),
create_plot_src_func(fig, "slow", 3, "green"),
create_plot_src_func(fig, "slowest",6, "red"),
]
return fig, plot_fns
def create_interact_ui(doc):
async def do_create_ui():
fig, plot_src_fns = await async_create_interact_ui()
# first show the base figure to users
doc.add_root(layout([
[fig, ]
]))
# then progressively show data as they become avaialble
for i, fn in enumerate(plot_src_fns):
doc.add_timeout_callback(fn, i * 100 + 500)
doc.add_next_tick_callback(do_create_ui)
output_notebook(verbose=False, hide_banner=True)
show(create_interact_ui, notebook_url=notebook_url)
test_bokeh_server(notebook_url=notebook_url)