diff --git a/doc/Tutorials/Bokeh_Backend.ipynb b/doc/Tutorials/Bokeh_Backend.ipynb index 39b9a20931..4cb92c9e78 100644 --- a/doc/Tutorials/Bokeh_Backend.ipynb +++ b/doc/Tutorials/Bokeh_Backend.ipynb @@ -322,11 +322,11 @@ }, "outputs": [], "source": [ - "%%opts Overlay [tabs=True width=600 height=600] RGB [width=600 height=600]\n", + "%%opts Overlay [tabs=True] Image [width=400 height=400]\n", "x,y = np.mgrid[-50:51, -50:51] * 0.1\n", "\n", "img = hv.Image(np.sin(x**2+y**2), bounds=(-1,-1,1,1))\n", - "img.relabel('Low') * img.clone(img.data*2).relabel('High') + img" + "img.relabel('Low') * img.clone(img.data*2).relabel('High')" ] }, { diff --git a/holoviews/plotting/bokeh/element.py b/holoviews/plotting/bokeh/element.py index 11618b6798..f186ae52d1 100644 --- a/holoviews/plotting/bokeh/element.py +++ b/holoviews/plotting/bokeh/element.py @@ -33,7 +33,7 @@ from ..plot import GenericElementPlot, GenericOverlayPlot from ..util import dynamic_update, get_sources from .plot import BokehPlot -from .util import (mpl_to_bokeh, convert_datetime, update_plot, +from .util import (mpl_to_bokeh, convert_datetime, update_plot, get_tab_title, bokeh_version, mplcmap_to_palette, py2js_tickformatter) if bokeh_version >= '0.12': @@ -1112,20 +1112,17 @@ def initialize_plot(self, ranges=None, plot=None, plots=None): panels = [] for key, subplot in self.subplots.items(): + frame = None if self.tabs: subplot.overlaid = False child = subplot.initialize_plot(ranges, plot, plots) + if isinstance(element, CompositeOverlay): + frame = element.get(key, None) + subplot.current_frame = frame if self.batched: self.handles['plot'] = child if self.tabs: - if self.hmap.type is Overlay: - title = ' '.join(key) - else: - title = ', '.join([d.pprint_value_string(k) for d, k in - zip(self.hmap.last.kdims, key)]) + title = get_tab_title(key, frame, self.hmap.last) panels.append(Panel(child=child, title=title)) - if isinstance(element, CompositeOverlay): - frame = element.get(key, None) - subplot.current_frame = frame if self.tabs: self.handles['plot'] = Tabs(tabs=panels) diff --git a/holoviews/plotting/bokeh/plot.py b/holoviews/plotting/bokeh/plot.py index d9fd5bc6b4..b1b8806f44 100644 --- a/holoviews/plotting/bokeh/plot.py +++ b/holoviews/plotting/bokeh/plot.py @@ -532,14 +532,9 @@ def initialize_plot(self, ranges=None): if adjoined: plots = layout_padding(plots) - # Determine the most appropriate composite plot type - # If the object cannot be displayed in a single layout - # it will be split into Tabs, for 1-row or 1-column - # Layouts we use the vplot and hplots. - # If there is a table and multiple rows and columns - # everything will be forced to a vertical layout + # Wrap in appropriate layout model if self.tabs: - panels = [Panel(child=child, title=str(tab_titles.get(r, c))) + panels = [Panel(child=child, title=str(tab_titles.get((r, c)))) for r, row in enumerate(plots) for c, child in enumerate(row) if child is not None] diff --git a/holoviews/plotting/bokeh/util.py b/holoviews/plotting/bokeh/util.py index df1a2feaf2..69b66668e7 100644 --- a/holoviews/plotting/bokeh/util.py +++ b/holoviews/plotting/bokeh/util.py @@ -24,6 +24,7 @@ from bokeh.layouts import WidgetBox from ...core.options import abbreviated_exception +from ...core.overlay import Overlay # Conversion between matplotlib and bokeh markers markers = {'s': {'marker': 'square'}, @@ -346,7 +347,7 @@ def update_plot(old, new): old_r.data_source.data.update(emptied) -def pad_plots(plots, padding=0.85): +def pad_plots(plots, table_padding=0.85, tabs_padding=1.2): """ Accepts a grid of bokeh plots in form of a list of lists and wraps any DataTable or Tabs in a WidgetBox with appropriate @@ -360,10 +361,11 @@ def pad_plots(plots, padding=0.85): width = np.max([p.width if isinstance(p, DataTable) else t.child.plot_width for t in p.tabs]) for p in p.tabs: - p.width = int(padding*width) + p.width = width + width = int(tabs_padding*width) elif isinstance(p, DataTable): width = p.width - p.width = int(padding*width) + p.width = int(table_padding*width) elif p: width = p.plot_width else: @@ -402,3 +404,25 @@ def py2js_tickformatter(formatter, msg=''): match = re.search('(function \(.*\))', jsfunc ) return jsfunc[:match.start()] + 'function ()' + jsfunc[match.end():] + +def get_tab_title(key, frame, overlay): + """ + Computes a title for bokeh tabs from the key in the overlay, the + element and the containing (Nd)Overlay. + """ + if isinstance(overlay, Overlay): + if frame is not None: + title = [] + if frame.label: + title.append(frame.label) + if frame.group != frame.params('group').default: + title.append(frame.group) + else: + title.append(frame.group) + else: + title = key + title = ' '.join(title) + else: + title = ' | '.join([d.pprint_value_string(k) for d, k in + zip(overlay.kdims, key)]) + return title