diff --git a/glue_plotly/html_exporters/bqplot/histogram.py b/glue_plotly/html_exporters/bqplot/histogram.py
index 496394c..57c3388 100644
--- a/glue_plotly/html_exporters/bqplot/histogram.py
+++ b/glue_plotly/html_exporters/bqplot/histogram.py
@@ -6,11 +6,11 @@
from plotly.offline import plot
import plotly.graph_objs as go
-from .base import PlotlyBaseBqplotExport
+from ...jupyter_base_export_tool import JupyterBaseExportTool
@viewer_tool
-class PlotlyHistogramBqplotExport(PlotlyBaseBqplotExport):
+class PlotlyHistogramBqplotExport(JupyterBaseExportTool):
tool_id = 'save:bqplot_plotlyhist'
def save_figure(self, filepath):
diff --git a/glue_plotly/html_exporters/bqplot/image.py b/glue_plotly/html_exporters/bqplot/image.py
index df5bfcc..6697c71 100644
--- a/glue_plotly/html_exporters/bqplot/image.py
+++ b/glue_plotly/html_exporters/bqplot/image.py
@@ -7,11 +7,11 @@
import plotly.graph_objs as go
from plotly.subplots import make_subplots
-from .base import PlotlyBaseBqplotExport
+from ...jupyter_base_export_tool import JupyterBaseExportTool
@viewer_tool
-class PlotlyImageBqplotExport(PlotlyBaseBqplotExport):
+class PlotlyImageBqplotExport(JupyterBaseExportTool):
tool_id = 'save:bqplot_plotlyimage2d'
def save_figure(self, filepath):
diff --git a/glue_plotly/html_exporters/bqplot/profile.py b/glue_plotly/html_exporters/bqplot/profile.py
index c72b0c6..49b6faf 100644
--- a/glue_plotly/html_exporters/bqplot/profile.py
+++ b/glue_plotly/html_exporters/bqplot/profile.py
@@ -6,11 +6,11 @@
from plotly.offline import plot
import plotly.graph_objs as go
-from .base import PlotlyBaseBqplotExport
+from ...jupyter_base_export_tool import JupyterBaseExportTool
@viewer_tool
-class PlotlyProfileBqplotExport(PlotlyBaseBqplotExport):
+class PlotlyProfileBqplotExport(JupyterBaseExportTool):
tool_id = 'save:bqplot_plotlyprofile'
def save_figure(self, filepath):
diff --git a/glue_plotly/html_exporters/bqplot/scatter2d.py b/glue_plotly/html_exporters/bqplot/scatter2d.py
index c175fab..9374f34 100644
--- a/glue_plotly/html_exporters/bqplot/scatter2d.py
+++ b/glue_plotly/html_exporters/bqplot/scatter2d.py
@@ -6,11 +6,11 @@
from plotly.offline import plot
import plotly.graph_objs as go
-from .base import PlotlyBaseBqplotExport
+from ...jupyter_base_export_tool import JupyterBaseExportTool
@viewer_tool
-class PlotlyScatter2DBqplotExport(PlotlyBaseBqplotExport):
+class PlotlyScatter2DBqplotExport(JupyterBaseExportTool):
tool_id = 'save:bqplot_plotly2d'
def save_figure(self, filepath):
diff --git a/glue_plotly/html_exporters/bqplot/base.py b/glue_plotly/jupyter_base_export_tool.py
similarity index 93%
rename from glue_plotly/html_exporters/bqplot/base.py
rename to glue_plotly/jupyter_base_export_tool.py
index d65b5bb..1082be6 100644
--- a/glue_plotly/html_exporters/bqplot/base.py
+++ b/glue_plotly/jupyter_base_export_tool.py
@@ -7,14 +7,16 @@
from ipywidgets import HBox, Layout # noqa
from IPython.display import display # noqa
from ipyfilechooser import FileChooser # noqa
+from glue_plotly import PLOTLY_LOGO
-from glue_plotly import PLOTLY_LOGO # noqa
+__all__ = ["JupyterBaseExportTool"]
-class PlotlyBaseBqplotExport(Tool):
+class JupyterBaseExportTool(Tool):
+
icon = PLOTLY_LOGO
- action_text = 'Save Plotly HTML page'
- tool_tip = 'Save Plotly HTML page'
+ action_text = "Save Plotly HTML page"
+ tool_tip = "Save Plotly HTML page"
def activate(self):
file_chooser = FileChooser(getcwd())
diff --git a/glue_plotly/viewers/common/tools.py b/glue_plotly/viewers/common/tools.py
index 34dfb80..592c74b 100644
--- a/glue_plotly/viewers/common/tools.py
+++ b/glue_plotly/viewers/common/tools.py
@@ -4,6 +4,13 @@
from glue.config import viewer_tool
from glue.core.subset import PolygonalROI, RectangularROI, XRangeROI, YRangeROI
from glue.viewers.common.tool import CheckableTool, Tool
+from glue_plotly.jupyter_base_export_tool import JupyterBaseExportTool
+
+import plotly.graph_objects as go
+import ipyvuetify as v # noqa
+from ipywidgets import HBox, Layout # noqa
+from IPython.display import display # noqa
+from ipyfilechooser import FileChooser # noqa
class PlotlyDragMode(CheckableTool):
@@ -252,3 +259,29 @@ def activate(self):
def deactivate(self):
self.viewer.figure.update_layout(hovermode=False)
+
+
+@viewer_tool
+class PlotlySaveTool(JupyterBaseExportTool):
+
+ icon = 'glue_filesave'
+ tool_id = 'plotly:save'
+ action_text = 'Save as interactive HTML'
+ tool_tip = 'Save as interactive HTML'
+
+ def save_figure(self, filepath):
+ if not filepath:
+ return
+
+ # We restrict things like modebar and axis functionality in
+ # the viewer so that we can enable/disable them via tools.
+ # For the HTML export, we want to re-enable these.
+ # We clone the viewer figure so that we don't modify the viewer itself.
+ figure = go.Figure(self.viewer.figure)
+ for setting in ('modebar', 'dragmode', 'newselection'):
+ figure.update_layout({setting: None})
+ for ax in ('x', 'y', 'z'):
+ attr = f"{ax}axis"
+ if hasattr(figure.layout, attr):
+ getattr(figure.layout, attr).update(fixedrange=False)
+ figure.write_html(filepath)
diff --git a/glue_plotly/viewers/histogram/viewer.py b/glue_plotly/viewers/histogram/viewer.py
index 1d617e7..075be2d 100644
--- a/glue_plotly/viewers/histogram/viewer.py
+++ b/glue_plotly/viewers/histogram/viewer.py
@@ -15,7 +15,9 @@
@viewer_registry("plotly_histogram")
class PlotlyHistogramView(PlotlyBaseView):
- tools = ['plotly:home', 'plotly:zoom', 'plotly:pan', 'plotly:xrange', 'plotly:hover']
+ tools = ['plotly:save', 'plotly:home',
+ 'plotly:zoom', 'plotly:pan',
+ 'plotly:xrange', 'plotly:hover']
allow_duplicate_data = False
allow_duplicate_subset = False
diff --git a/glue_plotly/viewers/scatter/viewer.py b/glue_plotly/viewers/scatter/viewer.py
index b1e0da7..4074fd3 100644
--- a/glue_plotly/viewers/scatter/viewer.py
+++ b/glue_plotly/viewers/scatter/viewer.py
@@ -19,8 +19,11 @@
@viewer_registry("plotly_scatter")
class PlotlyScatterView(PlotlyBaseView):
- tools = ['plotly:home', 'plotly:zoom', 'plotly:pan', 'plotly:xrange',
- 'plotly:yrange', 'plotly:rectangle', 'plotly:lasso', 'plotly:hover']
+ tools = ['plotly:save', 'plotly:home',
+ 'plotly:zoom', 'plotly:pan',
+ 'plotly:xrange', 'plotly:yrange',
+ 'plotly:rectangle', 'plotly:lasso',
+ 'plotly:hover']
allow_duplicate_data = False
allow_duplicate_subset = False