diff --git a/glue_plotly/__init__.py b/glue_plotly/__init__.py index d8411c3..29ccfd1 100644 --- a/glue_plotly/__init__.py +++ b/glue_plotly/__init__.py @@ -69,8 +69,8 @@ def setup_qt(): } try: - from glue_vispy_viewers.scatter.scatter_viewer import VispyScatterViewer - from glue_vispy_viewers.volume.volume_viewer import VispyVolumeViewer + from glue_vispy_viewers.scatter.qt.scatter_viewer import VispyScatterViewer + from glue_vispy_viewers.volume.qt.volume_viewer import VispyVolumeViewer except ImportError: pass else: 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/qt/tests/test_scatter3d.py b/glue_plotly/html_exporters/qt/tests/test_scatter3d.py index 6ae1559..b0f512a 100644 --- a/glue_plotly/html_exporters/qt/tests/test_scatter3d.py +++ b/glue_plotly/html_exporters/qt/tests/test_scatter3d.py @@ -6,7 +6,7 @@ pytest.importorskip('glue_vispy_viewers') -from glue_vispy_viewers.scatter.scatter_viewer import VispyScatterViewer # noqa: E402 +from glue_vispy_viewers.scatter.qt.scatter_viewer import VispyScatterViewer # noqa: E402 from .test_base import TestQtExporter # noqa: E402 diff --git a/glue_plotly/html_exporters/qt/tests/test_volume.py b/glue_plotly/html_exporters/qt/tests/test_volume.py index bb2372b..cc38382 100644 --- a/glue_plotly/html_exporters/qt/tests/test_volume.py +++ b/glue_plotly/html_exporters/qt/tests/test_volume.py @@ -7,7 +7,7 @@ importorskip('glue_qt') importorskip('glue_vispy_viewers') -from glue_vispy_viewers.volume.volume_viewer import VispyVolumeViewer # noqa: E402 +from glue_vispy_viewers.volume.qt.volume_viewer import VispyVolumeViewer # noqa: E402 from numpy import arange, ones # noqa: E402 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/utils.py b/glue_plotly/utils.py index 6622cf7..c98b997 100644 --- a/glue_plotly/utils.py +++ b/glue_plotly/utils.py @@ -1,6 +1,14 @@ from re import match, sub -__all__ = ['cleaned_labels', 'mpl_ticks_values'] +__all__ = [ + 'cleaned_labels', + 'mpl_ticks_values', + 'opacity_value_string', + 'rgba_string_to_values', + 'is_rgba_hex', + 'is_rgb_hex', + 'rgba_hex_to_rgb_hex', +] def cleaned_labels(labels): 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 diff --git a/setup.cfg b/setup.cfg index fad8bb4..6922f17 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,11 +23,12 @@ test = pytest pytest-cov mock - glue-vispy-viewers qt = glue-qt PySide2;python_version=="2" PyQt5;python_version>="3" +3d = + glue-vispy-viewers>=1.2.1 jupyter = glue-jupyter ipyvuetify diff --git a/tox.ini b/tox.ini index cbbd189..37f9ed1 100644 --- a/tox.ini +++ b/tox.ini @@ -10,7 +10,7 @@ passenv = changedir = test: .tmp/{envname} extras = - test: test,qt,jupyter + test: test,qt,3d,jupyter commands = glue116: pip install glue-core==1.16.* glue-jupyter<=0.19 matplotlib<3.9 glue117: pip install glue-core==1.17.* glue-jupyter<=0.20.1