Skip to content

Commit

Permalink
Unified bokeh plot patching approach dropping 0.10 compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Jun 21, 2016
1 parent ffec7f7 commit 049cb30
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 95 deletions.
16 changes: 2 additions & 14 deletions holoviews/plotting/bokeh/bokehwidgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,8 @@ var BokehMethods = {
var data = this.frames[current];
}
if (data !== undefined) {
if (data.root !== undefined) {
var doc = Bokeh.index[data.root].model.document;
}
$.each(data.data, function(i, value) {
if (data.root !== undefined) {
var ds = doc.get_model_by_id(value.id);
} else {
var ds = Bokeh.Collections(value.type).get(value.id);
}
if (ds != undefined) {
ds.set(value.data);
ds.trigger('change');
}
});
var doc = Bokeh.index[data.root].model.document;
doc.apply_json_patch(data.patch);
}
},
dynamic_update : function(current){
Expand Down
34 changes: 10 additions & 24 deletions holoviews/plotting/bokeh/callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,10 @@
import param

from ...core.data import ArrayColumns
from .renderer import bokeh_version
from .util import models_to_json, bokeh_version
from .util import compute_static_patch, models_to_json

from bokeh.models import CustomJS, TapTool, ColumnDataSource
if bokeh_version < '0.11':
from bokeh.protocol import serialize_json
else:
from bokeh.core.json_encoder import serialize_json
from bokeh.core.json_encoder import serialize_json


class Callback(param.ParameterizedFunction):
Expand Down Expand Up @@ -71,20 +67,11 @@ class Callback(param.ParameterizedFunction):
function callback(msg){
if (msg.msg_type == "execute_result") {
var data = JSON.parse(msg.content.data['text/plain'].slice(1, -1));
if (data.root !== undefined) {
var doc = Bokeh.index[data.root].model.document;
if (data !== undefined) {
console.log(data.root)
var doc = Bokeh.index[data.root].model.document;
doc.apply_json_patch(data.patch);
}
$.each(data.data, function(i, value) {
if (data.root !== undefined) {
var ds = doc.get_model_by_id(value.id);
} else {
var ds = Bokeh.Collections(value.type).get(value.id);
}
if (ds != undefined) {
ds.set(value.data);
ds.trigger('change');
}
});
} else {
console.log("Python callback returned unexpected message:", msg)
}
Expand Down Expand Up @@ -145,14 +132,13 @@ def update(self, data, chained=False):
return self.serialize(objects)


def serialize(self, objects):
def serialize(self, models):
"""
Serializes any Bokeh plot objects passed to it as a list.
"""
data = dict(data=models_to_json(objects))
if bokeh_version >= '0.11':
plot = self.plots[0]
data['root'] = plot.state._id
plot = self.plots[0]
patch = compute_static_patch(plot.document, models)
data = dict(root=plot.state._id, patch=patch)
return serialize_json(data)


Expand Down
11 changes: 3 additions & 8 deletions holoviews/plotting/bokeh/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from ..util import dynamic_update
from .callbacks import Callbacks
from .plot import BokehPlot
from .util import bokeh_version, mpl_to_bokeh, convert_datetime, update_plot
from .util import mpl_to_bokeh, convert_datetime, update_plot


# Define shared style properties for bokeh plots
Expand Down Expand Up @@ -280,9 +280,7 @@ def _plot_properties(self, key, plot, element):
if self.show_title:
plot_props['title'] = self._format_title(key, separator=' ')
if self.bgcolor:
bg_attr = 'background_fill'
if bokeh_version > '0.11': bg_attr += '_color'
plot_props[bg_attr] = self.bgcolor
plot_props['background_fill_color'] = self.bgcolor
if self.border is not None:
for p in ['left', 'right', 'top', 'bottom']:
plot_props['min_border_'+p] = self.border
Expand Down Expand Up @@ -675,10 +673,7 @@ def _process_legend(self):
if legend_fontsize:
plot.legend[0].label_text_font_size = legend_fontsize

if bokeh_version < '0.11':
plot.legend.orientation = self.legend_position
else:
plot.legend.location = self.legend_position
plot.legend.location = self.legend_position
legends = plot.legend[0].legends
new_legends = []
for label, l in legends:
Expand Down
4 changes: 2 additions & 2 deletions holoviews/plotting/bokeh/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
AdjointLayout, NdLayout, Empty, GridSpace, HoloMap)
from ...core import traversal
from ...core.options import Compositor
from ...core.util import basestring
from ...core.util import basestring, wrap_tuple
from ...element import Histogram
from ..plot import DimensionedPlot, GenericCompositePlot, GenericLayoutPlot
from ..util import get_dynamic_mode, initialize_sampled
Expand Down Expand Up @@ -257,7 +257,7 @@ def initialize_plot(self, ranges=None, plots=[]):
passed_plots = list(plots)
for i, coord in enumerate(self.layout.keys(full_grid=True)):
r = i % self.cols
subplot = self.subplots.get(coord, None)
subplot = self.subplots.get(wrap_tuple(coord), None)
if subplot is not None:
plot = subplot.initialize_plot(ranges=ranges, plots=passed_plots)
plots[r].append(plot)
Expand Down
38 changes: 17 additions & 21 deletions holoviews/plotting/bokeh/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from ...core import Store, HoloMap
from ..renderer import Renderer, MIME_TYPES
from .widgets import BokehScrubberWidget, BokehSelectionWidget
from .util import bokeh_version, models_to_json
from .util import compute_static_patch

import param
from param.parameterized import bothmethod
Expand All @@ -12,12 +12,12 @@
from bokeh.embed import notebook_div
from bokeh.io import load_notebook
from bokeh.resources import CDN, INLINE
from bokeh.io import _CommsHandle
from bokeh.util.notebook import get_comms

if bokeh_version < '0.11':
from bokeh.protocol import serialize_json
else:
from bokeh.core.json_encoder import serialize_json
from bokeh.model import _ModelInDocument as add_to_document
from bokeh.core.json_encoder import serialize_json
from bokeh.model import _ModelInDocument as add_to_document
from bokeh.document import Document


class BokehRenderer(Renderer):
Expand Down Expand Up @@ -60,25 +60,21 @@ def __call__(self, obj, fmt=None):
elif fmt == 'json':
plotobjects = [h for handles in plot.traverse(lambda x: x.current_handles)
for h in handles]
data = dict(data=[])
if bokeh_version >= '0.11':
data['root'] = plot.state._id
data['data'] = models_to_json(plotobjects)
patch = compute_static_patch(plot.document, plotobjects)
data = dict(root=plot.state._id, patch=patch)
return self._apply_post_render_hooks(serialize_json(data), obj, fmt), info


def figure_data(self, plot, fmt='html', **kwargs):
if bokeh_version >= '0.11':
doc_handler = add_to_document(plot.state)
with doc_handler:
doc = doc_handler._doc
comms_target = str(uuid.uuid4())
doc.last_comms_target = comms_target
div = notebook_div(plot.state, comms_target)
plot.document = doc
return div
else:
return notebook_div(plot.state)
doc_handler = add_to_document(plot.state)
with doc_handler:
doc = doc_handler._doc
comms_target = str(uuid.uuid4())
doc.last_comms_target = comms_target
div = notebook_div(plot.state, comms_target)
plot.document = doc
doc.add_root(plot.state)
return div


@classmethod
Expand Down
60 changes: 45 additions & 15 deletions holoviews/plotting/bokeh/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,9 @@

import bokeh
bokeh_version = LooseVersion(bokeh.__version__)
if bokeh_version < '0.11':
from bokeh.enums import Palette
from bokeh.plotting import Plot
else:
from bokeh.core.enums import Palette
from bokeh.models.plots import Plot
from bokeh.core.enums import Palette
from bokeh.document import Document
from bokeh.models.plots import Plot
from bokeh.models import GlyphRenderer
from bokeh.plotting import Figure

Expand Down Expand Up @@ -139,18 +136,51 @@ def models_to_json(models):
continue
else:
ids.append(plotobj.ref['id'])
if bokeh_version < '0.11':
json = plotobj.vm_serialize(changed_only=True)
else:
json = plotobj.to_json(False)
json.pop('tool_events', None)
json.pop('renderers', None)
json_data.append({'id': plotobj.ref['id'],
'type': plotobj.ref['type'],
'data': json})
json = plotobj.to_json(False)
json.pop('tool_events', None)
json.pop('renderers', None)
json_data.append({'id': plotobj.ref['id'],
'type': plotobj.ref['type'],
'data': json})
return json_data


def refs(json):
"""
Finds all the references to other objects in the json
representation of a bokeh Document.
"""
result = {}
for obj in json['roots']['references']:
result[obj['id']] = obj
return result


def compute_static_patch(document, models):
"""
Computes a patch to update an existing document without
diffing the json first, making it suitable for static updates
between arbitrary frames. Note that this only supports changed
attributes and will break if new models have been added since
the plot was first created.
"""
json = document.to_json()
references = refs(json)
requested_updates = [m.ref['id'] for m in models]

value_refs = {}
events = []
for ref_id, obj in references.items():
if ref_id not in requested_updates:
continue
for key, val in obj['attributes'].items():
event = Document._event_for_attribute_change(references,
obj, key, val,
value_refs)
events.append(event)
return dict(events=events, references=list(value_refs.values()))


def hsv_to_rgb(hsv):
"""
Vectorized HSV to RGB conversion, adapted from:
Expand Down
18 changes: 7 additions & 11 deletions holoviews/plotting/bokeh/widgets.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import json

from .util import bokeh_version
from ..widgets import NdWidget, SelectionWidget, ScrubberWidget

import param
import bokeh
from bokeh.io import Document
if bokeh_version >= '0.11':
from bokeh.io import _CommsHandle
from bokeh.util.notebook import get_comms
from bokeh.io import _CommsHandle
from bokeh.util.notebook import get_comms

from ..widgets import NdWidget, SelectionWidget, ScrubberWidget


class BokehWidget(NdWidget):
Expand Down Expand Up @@ -39,15 +37,13 @@ def _plot_figure(self, idx, fig_format='json'):
first call and
"""
self.plot.update(idx)
if self.embed or fig_format == 'html' or bokeh_version < '0.11':
return self.renderer.html(self.plot, fig_format)
if self.embed or fig_format == 'html':
html = self.renderer.html(self.plot, fig_format)
return html
else:
doc = self.plot.document

if hasattr(doc, 'last_comms_handle'):
handle = doc.last_comms_handle
else:
doc.add_root(self.plot.state)
handle = _CommsHandle(get_comms(doc.last_comms_target),
doc, doc.to_json())
doc.last_comms_handle = handle
Expand Down

0 comments on commit 049cb30

Please sign in to comment.