Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Figure.show for displaying previews in Jupyter notebooks and external viewers #529

Merged
merged 63 commits into from
Apr 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
7ca1721
Improve show() function for displaying previews in notebooks or using…
seisman Jun 1, 2020
75b0b38
Fix
seisman Oct 1, 2020
c7cc49a
Merge branch 'master' into new-display
seisman Oct 4, 2020
6f8f182
Merge branch 'master' into new-display
seisman Dec 3, 2020
97ba370
Merge branch 'master' into new-display
seisman Dec 16, 2020
4e0e776
Merge branch 'master' into new-display
seisman Dec 16, 2020
ee77d9a
Merge branch 'master' into new-display
seisman Dec 23, 2020
a09eb43
Merge branch 'master' into new-display
seisman Dec 23, 2020
6bb3d49
Merge branch 'master' into new-display
seisman Dec 29, 2020
d9a1f94
Merge branch 'master' into new-display
seisman Dec 31, 2020
2d8d38b
Merge branch 'master' into new-display
seisman Jan 7, 2021
fe69d5b
Merge branch 'master' into new-display
seisman Jan 8, 2021
d6b48a8
Merge branch 'master' into new-display
weiji14 Jan 20, 2021
fcef995
Lint code
weiji14 Jan 20, 2021
c19fe6f
Merge branch 'master' into new-display
seisman Feb 12, 2021
50f79d1
Formatting the codes
seisman Feb 12, 2021
b96130a
Merge branch 'master' into new-display
seisman Feb 13, 2021
7ba31e1
Merge branch 'master' into new-display
seisman Feb 16, 2021
4177a46
Change PYGMT_DISABLE_EXTERNAL_DISPLAY to PYGMT_USE_EXTERNAL_DISPLAY
seisman Feb 16, 2021
4b066a5
Merge branch 'master' into new-display
seisman Feb 17, 2021
4ca9367
Fix errors in previous merge
seisman Feb 17, 2021
e6d1ef9
Fix comments
seisman Feb 17, 2021
753dbf9
Updates
seisman Feb 18, 2021
4c1c981
Merge branch 'master' into new-display
seisman Feb 19, 2021
5c9bed7
fix
seisman Feb 19, 2021
cb37eed
Fix
seisman Feb 19, 2021
d886418
Merge branch 'master' into new-display
seisman Feb 22, 2021
87abc86
Merge branch 'master' into new-display
seisman Feb 23, 2021
6b9fb8c
Merge branch 'master' into new-display
seisman Feb 24, 2021
bf6f2b1
Merge branch 'master' into new-display
seisman Mar 6, 2021
da8a6d7
Merge branch 'master' into new-display
seisman Mar 9, 2021
fccde41
Set PYGMT_USE_EXTERNAL_DISPLAY in html target
seisman Mar 9, 2021
e34b888
Improve docs [skip ci]
seisman Mar 9, 2021
77adbaf
Merge branch 'master' into new-display
seisman Mar 13, 2021
011c7a9
Merge branch 'master' into new-display
seisman Mar 15, 2021
e1c67a5
Update docstrings
seisman Mar 15, 2021
6f638f3
Fix the return value of Figure.show() in inset.py
seisman Mar 15, 2021
ca0ecb3
Improve dpi and width
seisman Mar 15, 2021
cdae975
Merge branch 'master' into new-display
seisman Mar 15, 2021
be677e4
Merge branch 'master' into new-display
seisman Mar 15, 2021
777d6bd
Formatting
seisman Mar 15, 2021
b04f5b9
Set IPython to None if not available
seisman Mar 15, 2021
c2b6319
Check the case that notebook is selected but IPython is not available
seisman Mar 15, 2021
4e765c2
Fix a lint issue
seisman Mar 16, 2021
7da2358
Merge branch 'master' into new-display
seisman Mar 16, 2021
5861c6b
Remove the changes for dpi and width settings
seisman Mar 16, 2021
794cae6
Merge branch 'master' into new-display
seisman Mar 18, 2021
6e59fa3
Use a single parmeter SHOW_CONFIG['method'] for display method
seisman Mar 18, 2021
0c62718
Figure.show() now returns the Image object
seisman Mar 19, 2021
63db193
Improve docstrings
seisman Mar 19, 2021
3bd180a
Document the return of Figure.show()
seisman Mar 19, 2021
d748ae0
Improve function docstrings
seisman Mar 19, 2021
e6b3f65
IPython.display.display is no longer used
seisman Mar 19, 2021
05b3962
Merge branch 'master' into new-display
seisman Mar 21, 2021
92c7258
Merge branch 'master' into new-display
seisman Apr 2, 2021
7d53b1a
Figure.show() now returns nothing
seisman Apr 2, 2021
14af2ca
Merge branch 'master' into new-display
seisman Apr 3, 2021
db038b0
Update docstrings using bold text
seisman Apr 3, 2021
1b50edb
Merge branch 'master' into new-display
seisman Apr 6, 2021
9b44165
Fix a comment [skip ci]
seisman Apr 7, 2021
81fb312
Improve Fig.show() comment
seisman Apr 7, 2021
9c17112
Merge branch 'master' into new-display
seisman Apr 9, 2021
6fa020d
Add a test for invalid arguments of set_display()
seisman Apr 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ test:
@echo ""
@cd $(TESTDIR); python -c "import $(PROJECT); $(PROJECT).show_versions()"
@echo ""
cd $(TESTDIR); pytest $(PYTEST_COV_ARGS) $(PROJECT)
cd $(TESTDIR); PYGMT_USE_EXTERNAL_DISPLAY="false" pytest $(PYTEST_COV_ARGS) $(PROJECT)
cp $(TESTDIR)/coverage.xml .
cp -r $(TESTDIR)/htmlcov .
rm -r $(TESTDIR)
Expand Down
3 changes: 2 additions & 1 deletion doc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ html: api
@echo
@echo "Building HTML files."
@echo
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
# Set PYGMT_USE_EXTERNAL_DISPLAY to "false" to disable external display
PYGMT_USE_EXTERNAL_DISPLAY="false" $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."

Expand Down
7 changes: 7 additions & 0 deletions doc/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,13 @@ Saving and displaying the figure:
Figure.show
Figure.psconvert

Configuring the display settings:

.. autosummary::
:toctree: generated

set_display
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking whether to put set_display alongside config and have a heading like "Advanced configuration" or something like that.



Data Processing
---------------
Expand Down
2 changes: 1 addition & 1 deletion pygmt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
# Import modules to make the high-level GMT Python API
from pygmt import datasets
from pygmt.accessors import GMTDataArrayAccessor
from pygmt.figure import Figure
from pygmt.figure import Figure, set_display
from pygmt.session_management import begin as _begin
from pygmt.session_management import end as _end
from pygmt.src import (
Expand Down
130 changes: 91 additions & 39 deletions pygmt/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
from tempfile import TemporaryDirectory

try:
from IPython.display import Image
except ImportError:
Image = None
import IPython
except KeyError:
IPython = None # pylint: disable=invalid-name


from pygmt.clib import Session
from pygmt.exceptions import GMTError, GMTInvalidInput
Expand All @@ -25,6 +26,23 @@
# This is needed for the sphinx-gallery scraper in pygmt/sphinx_gallery.py
SHOWED_FIGURES = []

# Configurations for figure display
SHOW_CONFIG = {
"method": "external", # Open in an external viewer by default
}

# Show figures in Jupyter notebooks if available
if IPython:
get_ipython = IPython.get_ipython() # pylint: disable=invalid-name
if get_ipython and "IPKernelApp" in get_ipython.config: # Jupyter Notebook enabled
SHOW_CONFIG["method"] = "notebook"

# Set environment variable PYGMT_USE_EXTERNAL_DISPLAY to 'false' to disable
# external display. Use it when running the tests and building the docs to
# avoid popping up windows.
if os.environ.get("PYGMT_USE_EXTERNAL_DISPLAY", "true").lower() == "false":
SHOW_CONFIG["method"] = "none"


class Figure:
"""
Expand Down Expand Up @@ -235,62 +253,72 @@ def savefig(
if show:
launch_external_viewer(fname)

def show(self, dpi=300, width=500, method="static"):
def show(self, dpi=300, width=500, method=None):
"""
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.
Inserts the preview in the Jupyter notebook output if available,
otherwise opens it in the default viewer for your operating system
(falls back to the default web browser).

:func:`pygmt.set_display` can select the default display method
(**notebook**, **external**, or **none**).

The ``method`` parameter can also override the default display method
for the current figure. Parameters ``dpi`` and ``width`` can be used
to control the resolution and dimension of the figure in the notebook.

Note: The external viewer can be disabled by setting the
PYGMT_USE_EXTERNAL_DISPLAY environment variable to **false**.
This is useful when running unit tests and building the documentation
in consoles without a Graphical User Interface.

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.
Note that the external viewer does not block the current process.

Parameters
----------
dpi : int
The image resolution (dots per inch).
The image resolution (dots per inch) in Jupyter notebooks.
width : int
Width of the figure shown in the notebook in pixels. Ignored if
``method='external'``.
The image width (in pixels) in Jupyter notebooks.
method : str
How the figure will be displayed. Options are (1) ``'static'``: PNG
preview (default); (2) ``'external'``: PDF preview in an external
program.
How the current figure will be displayed. Options are

Returns
-------
img : IPython.display.Image
Only if ``method != 'external'``.
- **external**: PDF preview in an external program [default]
- **notebook**: PNG preview [default in Jupyter notebooks]
- **none**: Disable image preview
"""
# 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":
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
# Set the display method
if method is None:
method = SHOW_CONFIG["method"]

if method not in ["external", "notebook", "none"]:
raise GMTInvalidInput(
(
f"Invalid display method '{method}', "
"should be either 'notebook', 'external', or 'none'."
)
)
if Image is None:

if method in ["notebook", "none"]:
if IPython is None:
raise GMTError(
" ".join(
[
"Cannot find IPython.",
"Make sure you have it installed",
"or use 'method=\"external\"' to open in an external viewer.",
]
(
"Notebook display is selected, but IPython is not available. "
"Make sure you have IPython installed, "
"or run the script in a Jupyter notebook."
)
)
img = Image(data=png, width=width)
return img
png = self._preview(fmt="png", dpi=dpi, anti_alias=True, as_bytes=True)
IPython.display.display(IPython.display.Image(data=png, width=width))

if method == "external":
pdf = self._preview(fmt="pdf", dpi=dpi, anti_alias=False, as_bytes=False)
launch_external_viewer(pdf)

def shift_origin(self, xshift=None, yshift=None):
"""
Expand Down Expand Up @@ -396,3 +424,27 @@ def _repr_html_(self):
subplot,
text,
)


def set_display(method=None):
"""
Set the display method.

Parameters
----------
method : str or None
The method to display an image. Choose from:

- **external**: PDF preview in an external program [default]
- **notebook**: PNG preview [default in Jupyter notebooks]
- **none**: Disable image preview
"""
if method in ["notebook", "external", "none"]:
SHOW_CONFIG["method"] = method
elif method is not None:
raise GMTInvalidInput(
(
f"Invalid display mode '{method}', "
"should be either 'notebook', 'external' or 'none'."
)
)
14 changes: 11 additions & 3 deletions pygmt/tests/test_figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import numpy as np
import numpy.testing as npt
import pytest
from pygmt import Figure
from pygmt import Figure, set_display
from pygmt.exceptions import GMTInvalidInput


Expand Down Expand Up @@ -142,8 +142,7 @@ def test_figure_show():
"""
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()
seisman marked this conversation as resolved.
Show resolved Hide resolved


@pytest.mark.mpl_image_compare
Expand Down Expand Up @@ -175,3 +174,12 @@ def test_figure_show_invalid_method():
fig.basemap(region="10/70/-300/800", projection="X3i/5i", frame="af")
with pytest.raises(GMTInvalidInput):
fig.show(method="test")


def test_figure_set_display_invalid():
"""
Test to check if an error is raised when an invalid method is passed to
set_display.
"""
with pytest.raises(GMTInvalidInput):
set_display(method="invalid")