From b83db605f74e2e72a3c483d49a0b9144b43e7fcc Mon Sep 17 00:00:00 2001 From: Dongdong Tian Date: Mon, 1 Jun 2020 12:22:39 -0400 Subject: [PATCH] Redesign the display mechanism This is a new design of the PyGMT display mechanism, following the discussions in #269, with some changes. **It's working now but not finished yet.** --- Makefile | 2 +- doc/Makefile | 2 +- pygmt/__init__.py | 2 +- pygmt/figure.py | 97 +++++++++++++++++++++----------------- pygmt/tests/test_figure.py | 3 +- 5 files changed, 59 insertions(+), 47 deletions(-) diff --git a/Makefile b/Makefile index d6672c19f5e..71430c56686 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ test: @echo "" @cd $(TESTDIR); python -c "import $(PROJECT); $(PROJECT).show_versions()" @echo "" - cd $(TESTDIR); pytest $(PYTEST_ARGS) $(PROJECT) + cd $(TESTDIR); PYGMT_DISABLE_EXTERNAL_DISPLAY="true" pytest $(PYTEST_ARGS) $(PROJECT) cp $(TESTDIR)/coverage.xml . cp -r $(TESTDIR)/htmlcov . rm -r $(TESTDIR) diff --git a/doc/Makefile b/doc/Makefile index ce8dc86f764..f69ad9fede7 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -2,7 +2,7 @@ # You can set these variables from the command line. SPHINXOPTS = -SPHINXBUILD = sphinx-build +SPHINXBUILD = PYGMT_DISABLE_EXTERNAL_DISPLAY="true" sphinx-build SPHINXAUTOGEN = sphinx-autogen BUILDDIR = _build diff --git a/pygmt/__init__.py b/pygmt/__init__.py index 512865264b0..47689ab7cdc 100644 --- a/pygmt/__init__.py +++ b/pygmt/__init__.py @@ -13,7 +13,7 @@ # Import modules to make the high-level GMT Python API from .session_management import begin as _begin, end as _end -from .figure import Figure +from .figure import Figure, set_display from .filtering import blockmedian from .gridding import surface from .sampling import grdtrack diff --git a/pygmt/figure.py b/pygmt/figure.py index d32588ae85d..ed58c9d9c31 100644 --- a/pygmt/figure.py +++ b/pygmt/figure.py @@ -2,17 +2,14 @@ Define the Figure class that handles all plotting. """ import os +import sys from tempfile import TemporaryDirectory import base64 -try: - from IPython.display import Image -except ImportError: - Image = None from .clib import Session from .base_plotting import BasePlotting -from .exceptions import GMTError, GMTInvalidInput +from .exceptions import GMTInvalidInput from .helpers import ( build_arg_string, fmt_docstring, @@ -27,6 +24,29 @@ # This is needed for the sphinx-gallery scraper in pygmt/sphinx_gallery.py SHOWED_FIGURES = [] +# Variable to control show() display +SHOW_CONFIG = { + "external": True, # Open in an external viewer + "notebook": True, # Notebook display +} + +# Determine the default display mode +try: + IPython = sys.modules["IPython"] + if "IPKernelApp" in IPython.get_ipython().config: # Jupyter Notebook enabled + SHOW_CONFIG["notebook"] = True + SHOW_CONFIG["external"] = False + else: + SHOW_CONFIG["notebook"] = False + SHOW_CONFIG["external"] = True +except KeyError: + SHOW_CONFIG["notebook"] = False + SHOW_CONFIG["external"] = True + +# Check environment variable to override default values +if os.environ.get("PYGMT_DISABLE_EXTERNAL_DISPLAY", "default").lower() == "true": + SHOW_CONFIG["external"] = False + class Figure(BasePlotting): """ @@ -57,7 +77,7 @@ class Figure(BasePlotting): >>> fig = Figure() >>> fig.basemap(region='JP', projection="M3i", frame=True) >>> # The fig.region attribute shows the WESN bounding box for the figure - >>> print(', '.join('{:.2f}'.format(i) for i in fig.region)) + >>> print(', '.join('{:.2f}'.format(i) for i in fig.region)) 122.94, 145.82, 20.53, 45.52 """ @@ -235,63 +255,40 @@ def savefig( if show: launch_external_viewer(fname) - def show(self, dpi=300, width=500, method="static"): + def show(self, dpi=300, width=500): """ Display a preview of the figure. - Inserts the preview in the Jupyter notebook output. You will need to - have IPython installed for this to work. You should have it if you are - using the notebook. - - If ``method='external'``, makes PDF preview instead and opens it in the - default viewer for your operating system (falls back to the default web - browser). Note that the external viewer does not block the current - process, so this won't work in a script. + Inserts the preview in the Jupyter notebook output, otherwise opens it + in the default viewer for your operating system (falls back to the + default web browser). Note that the external viewer does not block the + current process, so this won't work in a script. Parameters ---------- dpi : int The image resolution (dots per inch). width : int - Width of the figure shown in the notebook in pixels. Ignored if - ``method='external'``. - method : str - How the figure will be displayed. Options are (1) ``'static'``: PNG - preview (default); (2) ``'external'``: PDF preview in an external - program. + Width of the figure shown in the notebook in pixels. Returns ------- img : IPython.display.Image - Only if ``method != 'external'``. + Only if in Jupyter notebook. """ # Module level variable to know which figures had their show method # called. Needed for the sphinx-gallery scraper. SHOWED_FIGURES.append(self) - if method not in ["static", "external"]: - raise GMTInvalidInput("Invalid show method '{}'.".format(method)) - if method == "external": + if SHOW_CONFIG["notebook"]: + png = self._repr_png_() + if IPython is not None: + IPython.display.display(IPython.display.Image(data=png, width=width)) + + if SHOW_CONFIG["external"]: pdf = self._preview(fmt="pdf", dpi=dpi, anti_alias=False, as_bytes=False) launch_external_viewer(pdf) - img = None - elif method == "static": - png = self._preview( - fmt="png", dpi=dpi, anti_alias=True, as_bytes=True, transparent=True - ) - if Image is None: - raise GMTError( - " ".join( - [ - "Cannot find IPython.", - "Make sure you have it installed", - "or use 'external=True' to open in an external viewer.", - ] - ) - ) - img = Image(data=png, width=width) - return img def shift_origin(self, xshift=None, yshift=None): """ @@ -374,3 +371,19 @@ def _repr_html_(self): base64_png = base64.encodebytes(raw_png) html = '' return html.format(image=base64_png.decode("utf-8"), width=500) + + +def set_display(mode): + """ + Set the display mode. + """ + if mode == "notebook": + SHOW_CONFIG["notebook"] = True + SHOW_CONFIG["external"] = False + elif mode == "external": + SHOW_CONFIG["notebook"] = False + SHOW_CONFIG["external"] = True + else: + raise GMTInvalidInput( + f'Invalid display mode {mode}, should be either "notebook" or "external".' + ) diff --git a/pygmt/tests/test_figure.py b/pygmt/tests/test_figure.py index d3780a2eed2..feac4c70be6 100644 --- a/pygmt/tests/test_figure.py +++ b/pygmt/tests/test_figure.py @@ -113,8 +113,7 @@ def test_figure_show(): "Test that show creates the correct file name and deletes the temp dir" fig = Figure() fig.basemap(region="10/70/-300/800", projection="X3i/5i", frame="af") - img = fig.show(width=800) - assert img.width == 800 + fig.show(width=800) @pytest.mark.mpl_image_compare