From 486e2c6748f42720f52cd2f430439d67714d3860 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 12 Apr 2017 14:10:43 +0100 Subject: [PATCH 1/4] Added BokehRenderer.app method to create bokeh apps in scripts and notebooks --- holoviews/plotting/bokeh/renderer.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/holoviews/plotting/bokeh/renderer.py b/holoviews/plotting/bokeh/renderer.py index 7f0b97529b..15492065c2 100644 --- a/holoviews/plotting/bokeh/renderer.py +++ b/holoviews/plotting/bokeh/renderer.py @@ -4,10 +4,13 @@ import param from param.parameterized import bothmethod + +from bokeh.application.handlers import FunctionHandler +from bokeh.application import Application from bokeh.charts import Chart from bokeh.document import Document from bokeh.embed import notebook_div -from bokeh.io import load_notebook, curdoc +from bokeh.io import load_notebook, curdoc, show from bokeh.models import (Row, Column, Plot, Model, ToolbarBox, WidgetBox, Div, DataTable, Tabs) from bokeh.plotting import Figure @@ -92,6 +95,28 @@ def get_widget(self_or_cls, plot, widget_type, **kwargs): return super(BokehRenderer, self_or_cls).get_widget(plot, widget_type, **kwargs) + @bothmethod + def app(self_or_cls, plot, notebook=False): + """ + Creates a bokeh app from a HoloViews object or plot. By + default simply uses attaches plot to bokeh's curdoc and + returns the Document, if notebook option is supplied creates + an Application instance, displays it and returns it. + """ + renderer = self_or_cls.instance(mode='server') + if not notebook: + doc, _ = renderer(plot) + return doc + + def modify_doc(doc): + renderer(plot, doc=doc) + + handler = FunctionHandler(modify_doc) + app = Application(handler) + show(app) + return app + + def server_doc(self, plot, doc=None): """ Get server document. From 50130608c9a6d6df755faf7755438836f8be9cca Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 12 Apr 2017 14:31:52 +0100 Subject: [PATCH 2/4] Ensure that importing single backend sets it as active --- holoviews/plotting/bokeh/__init__.py | 3 +++ holoviews/plotting/mpl/__init__.py | 3 +++ holoviews/plotting/plotly/__init__.py | 3 +++ 3 files changed, 9 insertions(+) diff --git a/holoviews/plotting/bokeh/__init__.py b/holoviews/plotting/bokeh/__init__.py index 79fdd60839..be8360e5b1 100644 --- a/holoviews/plotting/bokeh/__init__.py +++ b/holoviews/plotting/bokeh/__init__.py @@ -33,6 +33,9 @@ Store.renderers['bokeh'] = BokehRenderer.instance() +if len(Store.renderers) == 1: + Store.current_backend = 'bokeh' + associations = {Overlay: OverlayPlot, NdOverlay: OverlayPlot, GridSpace: GridPlot, diff --git a/holoviews/plotting/mpl/__init__.py b/holoviews/plotting/mpl/__init__.py index 4e3d9919ee..95510562d0 100644 --- a/holoviews/plotting/mpl/__init__.py +++ b/holoviews/plotting/mpl/__init__.py @@ -81,6 +81,9 @@ def get_color_cycle(): Store.renderers['matplotlib'] = MPLRenderer.instance() +if len(Store.renderers) == 1: + Store.current_backend = 'matplotlib' + # Defines a wrapper around GridPlot and RasterGridPlot # switching to RasterGridPlot if the plot only contains # Raster Elements diff --git a/holoviews/plotting/plotly/__init__.py b/holoviews/plotting/plotly/__init__.py index 9c28b961b5..1f5b59b047 100644 --- a/holoviews/plotting/plotly/__init__.py +++ b/holoviews/plotting/plotly/__init__.py @@ -13,6 +13,9 @@ Store.renderers['plotly'] = PlotlyRenderer.instance() +if len(Store.renderers) == 1: + Store.current_backend = 'plotly' + Store.register({Points: PointPlot, Scatter: PointPlot, Curve: CurvePlot, From 949435bd52fdb4fa0f9d6900387479b53e0f344d Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 12 Apr 2017 14:40:24 +0100 Subject: [PATCH 3/4] Updated bokeh example apps --- .../plotting/bokeh/examples/apps/{apps => }/crossfilter.py | 2 -- holoviews/plotting/bokeh/examples/apps/{apps => }/player.py | 0 .../bokeh/examples/apps/{apps => }/selection_stream.py | 6 +++--- 3 files changed, 3 insertions(+), 5 deletions(-) rename holoviews/plotting/bokeh/examples/apps/{apps => }/crossfilter.py (97%) rename holoviews/plotting/bokeh/examples/apps/{apps => }/player.py (100%) rename holoviews/plotting/bokeh/examples/apps/{apps => }/selection_stream.py (80%) diff --git a/holoviews/plotting/bokeh/examples/apps/apps/crossfilter.py b/holoviews/plotting/bokeh/examples/apps/crossfilter.py similarity index 97% rename from holoviews/plotting/bokeh/examples/apps/apps/crossfilter.py rename to holoviews/plotting/bokeh/examples/apps/crossfilter.py index f9fe0ab3b0..96efaaadd6 100644 --- a/holoviews/plotting/bokeh/examples/apps/apps/crossfilter.py +++ b/holoviews/plotting/bokeh/examples/apps/crossfilter.py @@ -35,7 +35,6 @@ continuous = [x for x in columns if x not in discrete] quantileable = [x for x in continuous if len(df[x].unique()) > 20] -hv.Store.current_backend = 'bokeh' renderer = hv.Store.renderers['bokeh'] options = hv.Store.options(backend='bokeh') options.Points = hv.Options('plot', width=800, height=600, size_index=None,) @@ -52,7 +51,6 @@ def create_figure(): opts['scaling_factor'] = (1./df[size.value].max())*200 points = hv.Points(df, kdims=kdims, label=label)(plot=opts, style=style) plot = renderer.get_plot(points) - plot.initialize_plot() return plot.state def update(attr, old, new): diff --git a/holoviews/plotting/bokeh/examples/apps/apps/player.py b/holoviews/plotting/bokeh/examples/apps/player.py similarity index 100% rename from holoviews/plotting/bokeh/examples/apps/apps/player.py rename to holoviews/plotting/bokeh/examples/apps/player.py diff --git a/holoviews/plotting/bokeh/examples/apps/apps/selection_stream.py b/holoviews/plotting/bokeh/examples/apps/selection_stream.py similarity index 80% rename from holoviews/plotting/bokeh/examples/apps/apps/selection_stream.py rename to holoviews/plotting/bokeh/examples/apps/selection_stream.py index 5deb34fd7c..6d1f125868 100644 --- a/holoviews/plotting/bokeh/examples/apps/apps/selection_stream.py +++ b/holoviews/plotting/bokeh/examples/apps/selection_stream.py @@ -3,8 +3,7 @@ import holoviews.plotting.bokeh from holoviews.streams import Selection1D -hv.Store.current_backend = 'bokeh' -renderer = hv.Store.renderers['bokeh'].instance(mode='server') +renderer = hv.Store.renderers['bokeh'] hv.Store.options(backend='bokeh').Points = hv.Options('plot', tools=['box_select']) data = np.random.multivariate_normal((0, 0), [[1, 0.1], [0.1, 1]], (1000,)) @@ -13,5 +12,6 @@ mean_sel = hv.DynamicMap(lambda index: hv.HLine(points['y'][index].mean() if index else -10), kdims=[], streams=[sel]) -doc,_ = renderer((points * mean_sel)) + +doc = renderer.app((points * mean_sel)) doc.title = 'HoloViews Selection Stream' From 0a0320febee5249ce370965896b67895be76af01 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Wed, 12 Apr 2017 15:33:42 +0100 Subject: [PATCH 4/4] Added docstrings for bokeh app examples --- holoviews/plotting/bokeh/examples/apps/crossfilter.py | 7 +++++++ holoviews/plotting/bokeh/examples/apps/player.py | 10 ++++++---- .../plotting/bokeh/examples/apps/selection_stream.py | 6 ++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/holoviews/plotting/bokeh/examples/apps/crossfilter.py b/holoviews/plotting/bokeh/examples/apps/crossfilter.py index 96efaaadd6..2b8ef044d5 100644 --- a/holoviews/plotting/bokeh/examples/apps/crossfilter.py +++ b/holoviews/plotting/bokeh/examples/apps/crossfilter.py @@ -1,3 +1,10 @@ +""" +An example demonstrating how to put together a crossfilter app based +on the Auto MPG dataset. Demonstrates how to dynamically generate +bokeh plots using the HoloViews API and replacing the bokeh plot +based on the current widget selections. +""" + import numpy as np import pandas as pd import holoviews as hv diff --git a/holoviews/plotting/bokeh/examples/apps/player.py b/holoviews/plotting/bokeh/examples/apps/player.py index 800e22a190..f11a48e9c2 100644 --- a/holoviews/plotting/bokeh/examples/apps/player.py +++ b/holoviews/plotting/bokeh/examples/apps/player.py @@ -1,11 +1,13 @@ # -*- coding: utf-8 -*- +""" +Example of a simple player widget demonstrating how to connnect +a simple HoloViews plot with custom widgets and combine them +into a bokeh layout. +""" import numpy as np from bokeh.io import curdoc from bokeh.layouts import layout -from bokeh.models import ( - ColumnDataSource, HoverTool, SingleIntervalTicker, Slider, Button, Label, - CategoricalColorMapper, -) +from bokeh.models import Slider, Button import holoviews as hv import holoviews.plotting.bokeh diff --git a/holoviews/plotting/bokeh/examples/apps/selection_stream.py b/holoviews/plotting/bokeh/examples/apps/selection_stream.py index 6d1f125868..4bbaec492d 100644 --- a/holoviews/plotting/bokeh/examples/apps/selection_stream.py +++ b/holoviews/plotting/bokeh/examples/apps/selection_stream.py @@ -1,3 +1,9 @@ +""" +Example app demonstrating how to use the HoloViews API to generate +a bokeh app with complex interactivity. Uses a Selection1D stream +to compute the mean y-value of the current selection. +""" + import numpy as np import holoviews as hv import holoviews.plotting.bokeh