Skip to content

Commit

Permalink
Merge a3ad329 into 32758c4
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Feb 6, 2017
2 parents 32758c4 + a3ad329 commit 2011570
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 24 deletions.
13 changes: 5 additions & 8 deletions holoviews/plotting/bokeh/plot.py
Expand Up @@ -16,7 +16,7 @@
from ..plot import DimensionedPlot, GenericCompositePlot, GenericLayoutPlot
from ..util import get_dynamic_mode, initialize_sampled
from .renderer import BokehRenderer
from .util import bokeh_version, layout_padding, pad_plots
from .util import bokeh_version, layout_padding, pad_plots, filter_toolboxes

if bokeh_version >= '0.12':
from bokeh.layouts import gridplot
Expand Down Expand Up @@ -284,8 +284,8 @@ def sync_tools(self, subplots):

def initialize_plot(self, ranges=None, plots=[]):
ranges = self.compute_ranges(self.layout, self.keys[-1], None)
plots = [[] for r in range(self.cols)]
passed_plots = list(plots)
plots = [[] for r in range(self.cols)]
for i, coord in enumerate(self.layout.keys(full_grid=True)):
r = i % self.cols
subplot = self.subplots.get(wrap_tuple(coord), None)
Expand Down Expand Up @@ -421,8 +421,6 @@ def _create_subplots(self, layout, positions, layout_dimensions, ranges, num=0):

subplot_opts = dict(adjoined=main_plot)
# Options common for any subplot
if type(element) in (NdLayout, Layout):
raise SkipRendering("Cannot plot nested Layouts.")
vtype = element.type if isinstance(element, HoloMap) else element.__class__
plot_type = Store.registry[self.renderer.backend].get(vtype, None)
plotopts = self.lookup_options(element, 'plot').options
Expand All @@ -449,8 +447,6 @@ def _create_subplots(self, layout, positions, layout_dimensions, ranges, num=0):
self.warning("Bokeh plotting class for %s type not found, object will "
"not be rendered." % vtype.__name__)
continue
if plot_type in [GridPlot, LayoutPlot]:
self.tabs = True
num = num if len(self.coords) > 1 else 0
subplot = plot_type(element, keys=self.keys,
dimensions=self.dimensions,
Expand All @@ -470,10 +466,10 @@ def _create_subplots(self, layout, positions, layout_dimensions, ranges, num=0):
return subplots, adjoint_clone


def initialize_plot(self, ranges=None):
def initialize_plot(self, plots=None, ranges=None):
ranges = self.compute_ranges(self.layout, self.keys[-1], None)
passed_plots = [] if plots is None else plots
plots = [[] for _ in range(self.rows)]
passed_plots = []
tab_titles = {}
insert_rows, insert_cols = [], []
adjoined = False
Expand Down Expand Up @@ -541,6 +537,7 @@ def initialize_plot(self, ranges=None):
if child is not None]
layout_plot = Tabs(tabs=panels)
elif bokeh_version >= '0.12':
plots = filter_toolboxes(plots)
plots, width = pad_plots(plots)
layout_plot = gridplot(children=plots, width=width)
elif len(plots) == 1 and not adjoined:
Expand Down
60 changes: 45 additions & 15 deletions holoviews/plotting/bokeh/util.py
Expand Up @@ -17,7 +17,7 @@
from bokeh.core.json_encoder import serialize_json # noqa (API import)
from bokeh.document import Document
from bokeh.models.plots import Plot
from bokeh.models import GlyphRenderer, Model, HasProps
from bokeh.models import (GlyphRenderer, Model, HasProps, Column, Row, ToolbarBox)
from bokeh.models.widgets import DataTable, Tabs
from bokeh.plotting import Figure
if bokeh_version >= '0.12':
Expand Down Expand Up @@ -348,7 +348,36 @@ def update_plot(old, new):
old_r.data_source.data.update(emptied)


def pad_plots(plots, table_padding=0.85, tabs_padding=1.2):
def pad_width(model, table_padding=0.85, tabs_padding=1.2):
"""
Computes the width of a model and sets up appropriate padding
for Tabs and DataTable types.
"""
if isinstance(model, Row):
vals = [pad_width(child) for child in model.children]
width = np.max([v for v in vals if v is not None])
elif isinstance(model, Column):
vals = [pad_width(child) for child in model.children]
width = np.sum([v for v in vals if v is not None])
elif isinstance(model, Tabs):
vals = [pad_width(t) for t in model.tabs]
width = np.max([v for v in vals if v is not None])
for model in model.tabs:
model.width = width
width = int(tabs_padding*width)
elif isinstance(model, DataTable):
width = model.width
model.width = int(table_padding*width)
elif isinstance(model, WidgetBox):
width = model.width
elif model:
width = model.plot_width
else:
width = 0
return width


def pad_plots(plots):
"""
Accepts a grid of bokeh plots in form of a list of lists and
wraps any DataTable or Tabs in a WidgetBox with appropriate
Expand All @@ -358,19 +387,7 @@ def pad_plots(plots, table_padding=0.85, tabs_padding=1.2):
for row in plots:
row_widths = []
for p in row:
if isinstance(p, Tabs):
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 = width
width = int(tabs_padding*width)
elif isinstance(p, DataTable):
width = p.width
p.width = int(table_padding*width)
elif p:
width = p.plot_width
else:
width = 0
width = pad_width(p)
row_widths.append(width)
widths.append(row_widths)
plots = [[WidgetBox(p, width=w) if isinstance(p, (DataTable, Tabs)) else p
Expand All @@ -379,6 +396,19 @@ def pad_plots(plots, table_padding=0.85, tabs_padding=1.2):
return plots, total_width


def filter_toolboxes(plots):
"""
Filters out toolboxes out of a list of plots to be able to compose
them into a larger plot.
"""
if isinstance(plots, list):
plots = [filter_toolboxes(plot) for plot in plots]
elif hasattr(plots, 'children'):
plots.children = [filter_toolboxes(child) for child in plots.children
if not isinstance(child, ToolbarBox)]
return plots


def py2js_tickformatter(formatter, msg=''):
"""
Uses flexx.pyscript to compile a python tick formatter to JS code
Expand Down
57 changes: 56 additions & 1 deletion tests/testplotinstantiation.py
Expand Up @@ -35,9 +35,13 @@
import holoviews.plotting.bokeh
bokeh_renderer = Store.renderers['bokeh']
from holoviews.plotting.bokeh.callbacks import Callback
from bokeh.models import Div, ColumnDataSource, FactorRange, Range1d
from bokeh.models import (
Div, ColumnDataSource, FactorRange, Range1d, Row, Column,
ToolbarBox, Spacer
)
from bokeh.models.mappers import LinearColorMapper, LogColorMapper
from bokeh.models.tools import HoverTool
from bokeh.plotting import Figure
except:
bokeh_renderer = None

Expand Down Expand Up @@ -665,6 +669,56 @@ def test_curve_heterogeneous_datetime_types_with_pd_overlay(self):
self.assertEqual(plot.handles['x_range'].start, np.datetime64(dt.datetime(2016, 1, 1)))
self.assertEqual(plot.handles['x_range'].end, np.datetime64(dt.datetime(2016, 1, 13)))

def test_layout_gridspaces(self):
layout = (GridSpace({(i, j): Curve(range(i+j)) for i in range(1, 3)
for j in range(2,4)}) +
GridSpace({(i, j): Curve(range(i+j)) for i in range(1, 3)
for j in range(2,4)}) +
Curve(range(10))).cols(2)
layout_plot = bokeh_renderer.get_plot(layout)
plot = layout_plot.state

# Unpack until getting down to two rows
self.assertIsInstance(plot, Column)
self.assertEqual(len(plot.children), 2)
toolbar, column = plot.children
self.assertIsInstance(toolbar, ToolbarBox)
self.assertIsInstance(column, Column)
self.assertEqual(len(column.children), 2)
row1, row2 = column.children
self.assertIsInstance(row1, Row)
self.assertIsInstance(row2, Row)

# Check the row of GridSpaces
self.assertEqual(len(row1.children), 2)
grid1, grid2 = row1.children
self.assertIsInstance(grid1, Column)
self.assertIsInstance(grid2, Column)
self.assertEqual(len(grid1.children), 1)
self.assertEqual(len(grid2.children), 1)
grid1, grid2 = grid1.children[0], grid2.children[0]
self.assertIsInstance(grid1, Column)
self.assertIsInstance(grid2, Column)
for grid in [grid1, grid2]:
self.assertEqual(len(grid.children), 2)
grow1, grow2 = grid.children
self.assertIsInstance(grow1, Row)
self.assertIsInstance(grow2, Row)
self.assertEqual(len(grow1.children), 2)
self.assertEqual(len(grow2.children), 2)
gfig1, gfig2 = grow1.children
gfig3, gfig4 = grow2.children
self.assertIsInstance(gfig1, Figure)
self.assertIsInstance(gfig2, Figure)
self.assertIsInstance(gfig3, Figure)
self.assertIsInstance(gfig4, Figure)

# Check the row of Curve and a spacer
self.assertEqual(len(row2.children), 2)
fig, spacer = row2.children
self.assertIsInstance(fig, Figure)
self.assertIsInstance(spacer, Spacer)

def test_layout_instantiate_subplots(self):
layout = (Curve(range(10)) + Curve(range(10)) + Image(np.random.rand(10,10)) +
Curve(range(10)) + Curve(range(10)))
Expand All @@ -681,6 +735,7 @@ def test_layout_instantiate_subplots_transposed(self):




class TestPlotlyPlotInstantiation(ComparisonTestCase):

def setUp(self):
Expand Down

0 comments on commit 2011570

Please sign in to comment.