Skip to content

Redesign the display mechanism by Leo #528

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

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -26,7 +26,7 @@ test:
@echo ""
@cd $(TESTDIR); python -c "import $(PROJECT); $(PROJECT).print_clib_info()"
@echo ""
cd $(TESTDIR); pytest $(PYTEST_ARGS) $(PROJECT)
cd $(TESTDIR); PYGMT_EXTERNAL_VIEWER='false' pytest $(PYTEST_ARGS) $(PROJECT)
cp $(TESTDIR)/.coverage* .
rm -r $(TESTDIR)

Expand Down
11 changes: 1 addition & 10 deletions doc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SPHINXBUILD = PYGMT_EXTERNAL_VIEWER='false' sphinx-build
SPHINXAUTOGEN = sphinx-autogen
BUILDDIR = _build

Expand Down Expand Up @@ -45,17 +45,8 @@ api:
@echo
$(SPHINXAUTOGEN) -i -t _templates -o api/generated api/*.rst


linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."

doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."

serve:
cd $(BUILDDIR)/html && python -m http.server 8009
7 changes: 7 additions & 0 deletions doc/api/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ Saving and displaying the figure:
Figure.show
Figure.psconvert

Utilities for working in Jupyter notebooks:

.. autosummary::
:toctree: generated

enable_notebook


Data Processing
---------------
Expand Down
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
projections/index.rst
tutorials/coastlines.rst
tutorials/plot.rst
notebook-support.ipynb

.. toctree::
:maxdepth: 2
Expand Down
118 changes: 118 additions & 0 deletions doc/notebook-support.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Jupyter notebook support\n",
"\n",
"PyGMT includes support for displaying your figures on [Jupyter notebooks](https://jupyter.org/)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pygmt"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Figures can be displayed inline in the notebook by placing the [pygmt.Figure](api/generated/pygmt.Figure.rst) object at the end of a cell:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = pygmt.Figure()\n",
"fig.basemap(region=\"g\", projection=\"W10i\", frame=True)\n",
"fig.grdimage(\"@earth_relief_60m\", cmap=\"geo\")\n",
"fig"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Or by calling [pygmt.Figure.show](api/generated/pygmt.Figure.show.rst). In this case, you might not want a figure to pop-up in an external window every time. To enable notebook support and prevent pop-up figures, use [pygmt.enable_notebook](api/generated/pygmt.enable_notebook.rst)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pygmt.enable_notebook()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = pygmt.Figure()\n",
"fig.basemap(region=\"g\", projection=\"W10i\", frame=True)\n",
"fig.coast(land=\"#666666\", water=\"skyblue\")\n",
"fig.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can control the resolution of the images inserted into the notebook:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pygmt.enable_notebook(dpi=70)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig = pygmt.Figure()\n",
"fig.basemap(region=\"g\", projection=\"W10i\", frame=True)\n",
"fig.coast(land=\"#666666\", water=\"skyblue\")\n",
"fig.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.7"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
2 changes: 1 addition & 1 deletion pygmt/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, enable_notebook
from .gridding import surface
from .modules import info, grdinfo, which
from . import datasets
Expand Down
125 changes: 57 additions & 68 deletions pygmt/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
"""
import os
from tempfile import TemporaryDirectory
import base64

try:
from IPython.display import Image
import IPython

HAS_IPYTHON = True
except ImportError:
Image = None
HAS_IPYTHON = False

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,
Expand All @@ -26,6 +27,31 @@
# A registry of all figures that have had "show" called in this session.
# This is needed for the sphinx-gallery scraper in pygmt/sphinx_gallery.py
SHOWED_FIGURES = []
# Configuration options for Jupyter notebook support
SHOW_CONFIG = {"dpi": 200, "external": True, "display": False}
# If the environment variable is set to "false", disable the external viewer. Use this
# for running the tests and building the docs to avoid pop up windows.
if os.environ.get("PYGMT_EXTERNAL_VIEWER", "default") == "false":
SHOW_CONFIG["external"] = False


def enable_notebook(dpi=200):
"""
Enable extended support for the Jupyter notebook.

Suppresses an external window from popping-up when :meth:`pygmt.Figure.show` is
called. Can also control the resolution of displayed images in the notebook.

Parameters
----------
dpi : int
Set the default DPI (dots-per-inch) used for PNG image previews that are
inserted into the notebook.

"""
SHOW_CONFIG["dpi"] = dpi
SHOW_CONFIG["external"] = False
SHOW_CONFIG["display"] = True


class Figure(BasePlotting):
Expand Down Expand Up @@ -164,9 +190,7 @@ def psconvert(self, **kwargs):
with Session() as lib:
lib.call_module("psconvert", build_arg_string(kwargs))

def savefig(
self, fname, transparent=False, crop=True, anti_alias=True, show=False, **kwargs
):
def savefig(self, fname, transparent=False, crop=True, anti_alias=True, **kwargs):
"""
Save the figure to a file.

Expand All @@ -177,8 +201,8 @@ def savefig(
BMP (``.bmp``), TIFF (``.tif``), EPS (``.eps``), and KML (``.kml``).
The KML output generates a companion PNG file.

You can pass in any keyword arguments that
:meth:`~gmt.Figure.psconvert` accepts.
You can pass in any keyword arguments that :meth:`~gmt.Figure.psconvert`
accepts.

Parameters
----------
Expand All @@ -195,8 +219,6 @@ def savefig(
JPG, TIf). More specifically, uses options ``Qt=2, Qg=2`` in
:meth:`~gmt.Figure.psconvert`. Ignored if creating vector graphics.
Overrides values of ``Qt`` and ``Qg`` passed in through ``kwargs``.
show: bool
If True, will open the figure in an external viewer.
dpi : int
Set raster resolution in dpi. Default is 720 for PDF, 300 for
others.
Expand All @@ -223,66 +245,41 @@ def savefig(
kwargs["W"] = "+k"

self.psconvert(prefix=prefix, fmt=fmt, crop=crop, **kwargs)
if show:
launch_external_viewer(fname)

def show(self, dpi=300, width=500, method="static"):
def show(self):
"""
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.
By default, opens a PDF preview of the figure in the default PDF viewer. Behaves
differently depending on the operating system:

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.
* Linux: Uses ``xdg-open`` (which might need to be installed).
* Mac: Uses the ``open`` command.
* Windows: Uses Python's :func:`os.startfile` function.

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.
If we can't determine your OS or ``xdg-open`` is not available on Linux, falls
back to using the default web browser to open the file.

Returns
-------
img : IPython.display.Image
Only if ``method != 'external'``.
If :func:`pygmt.enable_notebook` was called, will not open the external viewer
and will instead use ``IPython.display.display`` to display the figure on th
Jupyter notebook or IPython Qt console.

The external viewer can also be disabled by setting the
``PYGMT_EXTERNAL_VIEWER`` environment variable to ``false``. This is mainly used
for running our tests and building the documentation.
"""
# 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
if SHOW_CONFIG["external"]:
pdf = self._preview(
fmt="pdf", dpi=SHOW_CONFIG["dpi"], anti_alias=False, as_bytes=False
)
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
launch_external_viewer(pdf)
if HAS_IPYTHON and SHOW_CONFIG["display"]:
png = self._repr_png_()
IPython.display.display(IPython.display.Image(data=png))

def shift_origin(self, xshift=None, yshift=None):
"""
Expand Down Expand Up @@ -351,15 +348,7 @@ def _repr_png_(self):
Show a PNG preview if the object is returned in an interactive shell.
For the Jupyter notebook or IPython Qt console.
"""
png = self._preview(fmt="png", dpi=70, anti_alias=True, as_bytes=True)
png = self._preview(
fmt="png", dpi=SHOW_CONFIG["dpi"], anti_alias=True, as_bytes=True
)
return png

def _repr_html_(self):
"""
Show the PNG image embedded in HTML with a controlled width.
Looks better than the raw PNG.
"""
raw_png = self._preview(fmt="png", dpi=300, anti_alias=True, as_bytes=True)
base64_png = base64.encodebytes(raw_png)
html = '<img src="data:image/png;base64,{image}" width="{width}px">'
return html.format(image=base64_png.decode("utf-8"), width=500)
Loading