Skip to content

Commit 007d07e

Browse files
committed
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.**
1 parent c191d3e commit 007d07e

File tree

7 files changed

+90
-60
lines changed

7 files changed

+90
-60
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ test:
2929
@echo ""
3030
@cd $(TESTDIR); python -c "import $(PROJECT); $(PROJECT).show_versions()"
3131
@echo ""
32-
cd $(TESTDIR); pytest $(PYTEST_ARGS) $(PROJECT)
32+
cd $(TESTDIR); PYGMT_DISABLE_EXTERNAL_DISPLAY="true" pytest $(PYTEST_ARGS) $(PROJECT)
3333
cp $(TESTDIR)/coverage.xml .
3434
cp -r $(TESTDIR)/htmlcov .
3535
rm -r $(TESTDIR)

doc/Makefile

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# You can set these variables from the command line.
44
SPHINXOPTS =
5-
SPHINXBUILD = sphinx-build
5+
SPHINXBUILD = PYGMT_DISABLE_EXTERNAL_DISPLAY="true" sphinx-build
66
SPHINXAUTOGEN = sphinx-autogen
77
BUILDDIR = _build
88

@@ -45,7 +45,6 @@ api:
4545
@echo
4646
$(SPHINXAUTOGEN) -i -t _templates -o api/generated api/*.rst
4747

48-
4948
linkcheck:
5049
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
5150
@echo

doc/api/index.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ Saving and displaying the figure:
5353
Figure.show
5454
Figure.psconvert
5555

56+
Utilities for setting display options when working in Jupyter notebooks:
57+
58+
.. autosummary::
59+
:toctree: generated
60+
61+
set_display
62+
5663

5764
Data Processing
5865
---------------

pygmt/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
# Import modules to make the high-level GMT Python API
1515
from .session_management import begin as _begin, end as _end
16-
from .figure import Figure
16+
from .figure import Figure, set_display
1717
from .filtering import blockmedian
1818
from .gridding import surface
1919
from .sampling import grdtrack

pygmt/figure.py

Lines changed: 73 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,14 @@
22
Define the Figure class that handles all plotting.
33
"""
44
import os
5+
import sys
56
from tempfile import TemporaryDirectory
67
import base64
78

8-
try:
9-
from IPython.display import Image
10-
except ImportError:
11-
Image = None
129

1310
from .clib import Session
1411
from .base_plotting import BasePlotting
15-
from .exceptions import GMTError, GMTInvalidInput
12+
from .exceptions import GMTInvalidInput
1613
from .helpers import (
1714
build_arg_string,
1815
fmt_docstring,
@@ -27,6 +24,31 @@
2724
# This is needed for the sphinx-gallery scraper in pygmt/sphinx_gallery.py
2825
SHOWED_FIGURES = []
2926

27+
# Configuration options for Jupyter notebook support
28+
SHOW_CONFIG = {
29+
"external": True, # Open in an external viewer
30+
"notebook": True, # Notebook display
31+
"dpi" : 200, # default DPI
32+
}
33+
34+
# Determine the default display mode
35+
try:
36+
IPython = sys.modules["IPython"]
37+
if "IPKernelApp" in IPython.get_ipython().config: # Jupyter Notebook enabled
38+
SHOW_CONFIG["notebook"] = True
39+
SHOW_CONFIG["external"] = False
40+
else:
41+
SHOW_CONFIG["notebook"] = False
42+
SHOW_CONFIG["external"] = True
43+
except KeyError:
44+
SHOW_CONFIG["notebook"] = False
45+
SHOW_CONFIG["external"] = True
46+
47+
# If the environment variable is set to "true", disable the external viewer. Use this
48+
# for running the tests and building the docs to avoid pop up windows.
49+
if os.environ.get("PYGMT_DISABLE_EXTERNAL_DISPLAY", "default").lower() == "true":
50+
SHOW_CONFIG["external"] = False
51+
3052

3153
class Figure(BasePlotting):
3254
"""
@@ -57,7 +79,7 @@ class Figure(BasePlotting):
5779
>>> fig = Figure()
5880
>>> fig.basemap(region='JP', projection="M3i", frame=True)
5981
>>> # The fig.region attribute shows the WESN bounding box for the figure
60-
>>> print(', '.join('{:.2f}'.format(i) for i in fig.region))
82+
>>> print(', '.join('{:.2f}'.format(i) for i in fig.region))
6183
122.94, 145.82, 20.53, 45.52
6284
6385
"""
@@ -235,63 +257,36 @@ def savefig(
235257
if show:
236258
launch_external_viewer(fname)
237259

238-
def show(self, dpi=300, width=500, method="static"):
260+
def show(self):
239261
"""
240262
Display a preview of the figure.
241263
242-
Inserts the preview in the Jupyter notebook output. You will need to
243-
have IPython installed for this to work. You should have it if you are
244-
using the notebook.
245-
246-
If ``method='external'``, makes PDF preview instead and opens it in the
247-
default viewer for your operating system (falls back to the default web
248-
browser). Note that the external viewer does not block the current
249-
process, so this won't work in a script.
264+
Inserts the preview in the Jupyter notebook output, otherwise opens it
265+
in the default viewer for your operating system (falls back to the
266+
default web browser). Note that the external viewer does not block the
267+
current process, so this won't work in a script.
250268
251-
Parameters
252-
----------
253-
dpi : int
254-
The image resolution (dots per inch).
255-
width : int
256-
Width of the figure shown in the notebook in pixels. Ignored if
257-
``method='external'``.
258-
method : str
259-
How the figure will be displayed. Options are (1) ``'static'``: PNG
260-
preview (default); (2) ``'external'``: PDF preview in an external
261-
program.
269+
:func:`pygmt.set_display` can select the default display mode (either
270+
"notebook" or "external").
262271
263-
Returns
264-
-------
265-
img : IPython.display.Image
266-
Only if ``method != 'external'``.
272+
The external viewer can also be disabled by setting the
273+
``PYGMT_DISABLE_EXTERNAL_DISPLAY`` environment variable to ``true``.
274+
This is mainly used for running our tests and building the
275+
documentation.
267276
268277
"""
269278
# Module level variable to know which figures had their show method
270279
# called. Needed for the sphinx-gallery scraper.
271280
SHOWED_FIGURES.append(self)
272281

273-
if method not in ["static", "external"]:
274-
raise GMTInvalidInput("Invalid show method '{}'.".format(method))
275-
if method == "external":
276-
pdf = self._preview(fmt="pdf", dpi=dpi, anti_alias=False, as_bytes=False)
282+
if SHOW_CONFIG["notebook"]:
283+
png = self._repr_png_()
284+
if IPython is not None:
285+
IPython.display.display(IPython.display.Image(data=png, width=width))
286+
287+
if SHOW_CONFIG["external"]:
288+
pdf = self._preview(fmt="pdf", dpi=SHOW_CONFIG["dpi"], anti_alias=False, as_bytes=False)
277289
launch_external_viewer(pdf)
278-
img = None
279-
elif method == "static":
280-
png = self._preview(
281-
fmt="png", dpi=dpi, anti_alias=True, as_bytes=True, transparent=True
282-
)
283-
if Image is None:
284-
raise GMTError(
285-
" ".join(
286-
[
287-
"Cannot find IPython.",
288-
"Make sure you have it installed",
289-
"or use 'external=True' to open in an external viewer.",
290-
]
291-
)
292-
)
293-
img = Image(data=png, width=width)
294-
return img
295290

296291
def shift_origin(self, xshift=None, yshift=None):
297292
"""
@@ -362,7 +357,7 @@ def _repr_png_(self):
362357
Show a PNG preview if the object is returned in an interactive shell.
363358
For the Jupyter notebook or IPython Qt console.
364359
"""
365-
png = self._preview(fmt="png", dpi=70, anti_alias=True, as_bytes=True)
360+
png = self._preview(fmt="png", dpi=SHOW_CONFIG["dpi"], anti_alias=True, as_bytes=True)
366361
return png
367362

368363
def _repr_html_(self):
@@ -374,3 +369,29 @@ def _repr_html_(self):
374369
base64_png = base64.encodebytes(raw_png)
375370
html = '<img src="data:image/png;base64,{image}" width="{width}px">'
376371
return html.format(image=base64_png.decode("utf-8"), width=500)
372+
373+
374+
def set_display(mode, dpi=200):
375+
"""Set the display mode.
376+
377+
Parameters
378+
----------
379+
mode : str
380+
Choose from "notebook" (for inline display in Jupyter notebook)
381+
or "external" (for displaying preview using the external viewer).
382+
383+
dpi : int
384+
Set the default DPI (dots-per-inch) used for PNG image previews that are
385+
inserted into the notebook.
386+
"""
387+
if mode == "notebook":
388+
SHOW_CONFIG["notebook"] = True
389+
SHOW_CONFIG["external"] = False
390+
elif mode == "external":
391+
SHOW_CONFIG["notebook"] = False
392+
SHOW_CONFIG["external"] = True
393+
else:
394+
raise GMTInvalidInput(
395+
f'Invalid display mode {mode}, should be either "notebook" or "external".'
396+
)
397+
SHOW_CONFIG["dpi"] = dpi

pygmt/helpers/utils.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""
22
Utilities and common tasks for wrapping the GMT modules.
33
"""
4+
import os
45
import sys
56
import shutil
67
import subprocess
@@ -199,11 +200,14 @@ def launch_external_viewer(fname):
199200
# with noise
200201
run_args = dict(stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
201202

203+
platform = sys.platform
202204
# Open the file with the default viewer.
203205
# Fall back to the browser if can't recognize the operating system.
204-
if sys.platform.startswith("linux") and shutil.which("xdg-open"):
206+
if platform.startswith("linux") and shutil.which("xdg-open"):
205207
subprocess.run(["xdg-open", fname], check=False, **run_args)
206-
elif sys.platform == "darwin": # Darwin is macOS
208+
elif platform == "darwin": # Darwin is macOS
207209
subprocess.run(["open", fname], check=False, **run_args)
210+
elif platform == "win32":
211+
os.startfile(fname) # pylint: disable=no-member
208212
else:
209213
webbrowser.open_new_tab("file://{}".format(fname))

pygmt/tests/test_figure.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,7 @@ def test_figure_show():
113113
"Test that show creates the correct file name and deletes the temp dir"
114114
fig = Figure()
115115
fig.basemap(region="10/70/-300/800", projection="X3i/5i", frame="af")
116-
img = fig.show(width=800)
117-
assert img.width == 800
116+
fig.show()
118117

119118

120119
@pytest.mark.mpl_image_compare

0 commit comments

Comments
 (0)