Hello!
I am experimenting with the following code. We plot 10 lines with their own datasources on one plot. The streaming happens periodically every second for all lines. Each line has a different amount of points generated within this one second.
from bokeh.layouts import column
from bokeh.plotting import figure, curdoc
from bokeh.models import ColumnDataSource, Dropdown
import numpy as np
import time
class Line:
def __init__(self, doc, plot, waiting_time):
self._current_x = time.monotonic()
self._current_y = 0
self._new_points = dict(x=[], y=[])
self._source = ColumnDataSource(data=dict(x=[], y=[]))
plot.line(source=self._source)
doc.add_periodic_callback(self._generate_point, waiting_time)
def _generate_point(self):
self._current_x = time.monotonic()
self._current_y += np.random.normal()
self._new_points["x"].append(self._current_x)
self._new_points["y"].append(self._current_y)
def stream(self):
self._source.stream(self._new_points)
self._new_points["x"].clear()
self._new_points["y"].clear()
doc = curdoc()
p = figure(width=300, height=300, tools="pan,reset,save", y_axis_location="right")
lines = []
for i in range(10):
lines.append(Line(doc, p, 100*i))
def stream_point():
for line in lines:
line.stream()
menu = [("Item 1", "item_1"), ("Item 2", "item_2"), ("Item 3", "item_3")]
dropdown = Dropdown(label="Dropdown button", menu=menu)
doc.add_periodic_callback(stream_point, 1000)
doc.add_root(column(p, dropdown))
The problem here is that after some time the browser spends so much time rendering the plot that the dropdown button becomes unusable.
Taking a look at the profiling tools that Firefox offers I can spot that for every time the plot receives a websocket message it redraws the whole plot.You can see on the picture the black line (websocket message) followed by a brown line (rendering).
What would be really great and would solve my problem here is to trigger rendering only after the last websocket message. Since my update loop is self contained I think the simplest solution would be to give me a possibility to trigger the rendering myself.
Like so
plot(automatic_redraw=False)
def stream_point():
for line in lines:
line.stream()
plot.redraw()
Is anything close to that available in bokeh? If not then does this interface make sense? I am happy to make a PR to bokeh if there are no design problems with it.
I do understand that usually all lines should be coming from the same ColumnDataSource and then all lines are updated at the same time but here the hard requirement is that each data source has a different length.
Thank you in advance!