diff --git a/conda.recipe/meta.yaml b/conda.recipe/meta.yaml index a39712311f..b513ee0655 100644 --- a/conda.recipe/meta.yaml +++ b/conda.recipe/meta.yaml @@ -22,7 +22,7 @@ requirements: run: - python - param >=1.6.1,<2.0 - - pyviz_comms + - pyviz_comms >= 0.6.0 - numpy - matplotlib>=2.1 - bokeh >=0.12.15,<=1.0 diff --git a/holoviews/ipython/__init__.py b/holoviews/ipython/__init__.py index ad0529cbad..c5798879a0 100644 --- a/holoviews/ipython/__init__.py +++ b/holoviews/ipython/__init__.py @@ -2,8 +2,9 @@ from unittest import SkipTest import param -from IPython import version_info import holoviews +from pyviz_comms import nb_mime_js +from IPython import version_info from param import ipython as param_ext from IPython.display import HTML, publish_display_data @@ -237,6 +238,10 @@ def load_hvjs(cls, logo=False, bokeh_logo=False, mpl_logo=False, plotly_logo=Fal widgetjs, widgetcss = Renderer.html_assets(extras=False, backends=[], script=True) else: widgetjs, widgetcss = '', '' + + # Add classic notebook MIME renderer + widgetjs += nb_mime_js + templateLoader = jinja2.FileSystemLoader(os.path.dirname(os.path.abspath(__file__))) jinjaEnv = jinja2.Environment(loader=templateLoader) template = jinjaEnv.get_template('load_notebook.html') diff --git a/holoviews/plotting/bokeh/callbacks.py b/holoviews/plotting/bokeh/callbacks.py index 573105d7b5..0b35850fed 100644 --- a/holoviews/plotting/bokeh/callbacks.py +++ b/holoviews/plotting/bokeh/callbacks.py @@ -61,11 +61,18 @@ def __init__(self, plot, streams, source, **params): self.streams = streams if plot.renderer.mode != 'server': self.comm = plot.renderer.comm_manager.get_client_comm(on_msg=self.on_msg) + else: + self.comm = None self.source = source self.handle_ids = defaultdict(dict) self.reset() + def cleanup(self): + if self.comm: + self.comm.close() + + def reset(self): if self.handle_ids: handles = self._init_plot_handles() diff --git a/holoviews/plotting/bokeh/plot.py b/holoviews/plotting/bokeh/plot.py index bd821c3c1d..2ea9150b61 100644 --- a/holoviews/plotting/bokeh/plot.py +++ b/holoviews/plotting/bokeh/plot.py @@ -273,6 +273,7 @@ def cleanup(self): callbacks = {k: cb for k, cb in callback._callbacks.items() if cb is not callback} Callback._callbacks = callbacks + callback.cleanup() def _fontsize(self, key, label='fontsize', common=True): diff --git a/holoviews/plotting/plot.py b/holoviews/plotting/plot.py index 0e66f738a7..56580f6b1f 100644 --- a/holoviews/plotting/plot.py +++ b/holoviews/plotting/plot.py @@ -76,6 +76,8 @@ def cleanup(self): stream._subscribers = [ (p, subscriber) for p, subscriber in stream._subscribers if util.get_method_owner(subscriber) not in plots] + if self.comm: + self.comm.close() @property diff --git a/holoviews/plotting/renderer.py b/holoviews/plotting/renderer.py index f64ad8bc75..e530edeae6 100644 --- a/holoviews/plotting/renderer.py +++ b/holoviews/plotting/renderer.py @@ -16,7 +16,7 @@ from .widgets import NdWidget, ScrubberWidget, SelectionWidget from . import Plot -from pyviz_comms import CommManager, JupyterCommManager +from pyviz_comms import CommManager, JupyterCommManager, embed_js from .util import displayable, collate, initialize_dynamic from param.parameterized import bothmethod @@ -69,19 +69,6 @@ """ -embed_js = """ -// Ugly hack - see #2574 for more information -if (!(document.getElementById('{plot_id}')) && !(document.getElementById('_anim_img{widget_id}'))) {{ - console.log("Creating DOM nodes dynamically for assumed nbconvert export. To generate clean HTML output set HV_DOC_HTML as an environment variable.") - var htmlObject = document.createElement('div'); - htmlObject.innerHTML = `{html}`; - var scriptTags = document.getElementsByTagName('script'); - var parentTag = scriptTags[scriptTags.length-1].parentNode; - parentTag.append(htmlObject) -}} -""" - - class Renderer(Exporter): """ The job of a Renderer is to turn the plotting state held within diff --git a/holoviews/plotting/widgets/widgets.js b/holoviews/plotting/widgets/widgets.js index 0391ca4723..2351f69708 100644 --- a/holoviews/plotting/widgets/widgets.js +++ b/holoviews/plotting/widgets/widgets.js @@ -551,114 +551,3 @@ for (var k in _namespace) { window.HoloViews[k] = _namespace[k]; } } - -var JS_MIME_TYPE = 'application/javascript'; -var HTML_MIME_TYPE = 'text/html'; -var EXEC_MIME_TYPE = 'application/vnd.holoviews_exec.v0+json'; -var CLASS_NAME = 'output'; - -/** - * Render data to the DOM node - */ -function render(props, node) { - var div = document.createElement("div"); - var script = document.createElement("script"); - node.appendChild(div); - node.appendChild(script); -} - -/** - * Handle when a new output is added - */ -function handle_add_output(event, handle) { - var output_area = handle.output_area; - var output = handle.output; - if (!output.data.hasOwnProperty(EXEC_MIME_TYPE)) { - return - } - var id = output.metadata[EXEC_MIME_TYPE]["id"]; - var toinsert = output_area.element.find("." + CLASS_NAME.split(' ')[0]); - if (id !== undefined) { - var nchildren = toinsert.length; - toinsert[nchildren-1].children[0].innerHTML = output.data[HTML_MIME_TYPE]; - toinsert[nchildren-1].children[1].textContent = output.data[JS_MIME_TYPE]; - output_area._hv_plot_id = id; - if ((window.Bokeh !== undefined) && (id in Bokeh.index)) { - HoloViews.plot_index[id] = Bokeh.index[id]; - } else { - HoloViews.plot_index[id] = null; - } - } -} - -/** - * Handle when an output is cleared or removed - */ -function handle_clear_output(event, handle) { - var id = handle.cell.output_area._hv_plot_id; - if ((id === undefined) || !(id in HoloViews.plot_index)) { return; } - var comm = window.HoloViews.comm_manager.get_client_comm("hv-extension-comm", "hv-extension-comm", function () {}); - if (comm !== null) { - comm.send({event_type: 'delete', 'id': id}); - } - delete HoloViews.plot_index[id]; - if ((window.Bokeh !== undefined) & (id in window.Bokeh.index)) { - window.Bokeh.index[id].model.document.clear(); - delete Bokeh.index[id]; - } -} - -/** - * Handle kernel restart event - */ -function handle_kernel_cleanup(event, handle) { - delete HoloViews.comms["hv-extension-comm"]; - window.HoloViews.plot_index = {} -} - -/** - * Handle update_display_data messages - */ -function handle_update_output(event, handle) { - handle_clear_output(event, {cell: {output_area: handle.output_area}}) - handle_add_output(event, handle) -} - -function register_renderer(events, OutputArea) { - function append_mime(data, metadata, element) { - // create a DOM node to render to - var toinsert = this.create_output_subarea( - metadata, - CLASS_NAME, - EXEC_MIME_TYPE - ); - this.keyboard_manager.register_events(toinsert); - // Render to node - var props = {data: data, metadata: metadata[EXEC_MIME_TYPE]}; - render(props, toinsert[0]); - element.append(toinsert); - return toinsert - } - - events.on('output_added.OutputArea', handle_add_output); - events.on('output_updated.OutputArea', handle_update_output); - events.on('clear_output.CodeCell', handle_clear_output); - events.on('delete.Cell', handle_clear_output); - events.on('kernel_ready.Kernel', handle_kernel_cleanup); - - OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, { - safe: true, - index: 0 - }); -} - -if (window.Jupyter !== undefined) { - try { - var events = require('base/js/events'); - var OutputArea = require('notebook/js/outputarea').OutputArea; - if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) { - register_renderer(events, OutputArea); - } - } catch(err) { - } -} diff --git a/setup.py b/setup.py index 1ebd78d32d..8d13cd5f3f 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ setup_args = {} -install_requires = ['param>=1.6.1,<2.0', 'numpy>=1.0', 'pyviz_comms'] +install_requires = ['param>=1.6.1,<2.0', 'numpy>=1.0', 'pyviz_comms>=0.6.0'] extras_require = {} # Notebook dependencies of IPython 3