diff --git a/doc/source/tutorial/first_steps.rst b/doc/source/tutorial/first_steps.rst index 9a74435f0ef9fb3784e6b638893c1e7dc2545900..1ddce048c4da222856413471aeb452390ed3e7c7 100644 --- a/doc/source/tutorial/first_steps.rst +++ b/doc/source/tutorial/first_steps.rst @@ -315,12 +315,12 @@ subbands that increases with energy. - Instead of plotting to the screen (which is standard) `~kwant.plotter.plot` can also write to a file specified by the argument - ``file``. For the plotting to the screen to work the module - ``matplotlib.pyplot`` has to be imported. (An informative error message - will remind you if you forget.) The reason for this is pretty technical: - matplotlib's "backend" can only be chosen before ``matplotlib.pyplot`` has - been imported. Would Kwant import that module by itself, it would deprive - you of the possibility to choose a non-default backend later. + ``file``. + + - Due to matplotlib's limitations, Kwant's plotting routines have the + side effect of fixing matplotlib's "backend". If you would like to choose + a different backend than the standard one, you must do so before asking + Kwant to plot anything. .. rubric:: Footnotes diff --git a/kwant/plotter.py b/kwant/plotter.py index 4eef52710f3ed0af045de21649aa8faa152be5aa..b591d585e1f815cf881b09eae4ea7f33a460a1d1 100644 --- a/kwant/plotter.py +++ b/kwant/plotter.py @@ -16,6 +16,7 @@ system in two or three dimensions. """ from collections import defaultdict +import sys import itertools import functools import warnings @@ -35,7 +36,6 @@ try: import matplotlib.cm from matplotlib.figure import Figure from matplotlib import collections - from matplotlib.backends.backend_agg import FigureCanvasAgg from . import _colormaps mpl_available = True try: @@ -659,15 +659,23 @@ def output_fig(fig, output_mode='auto', file=None, savefile_opts=None, """ if not mpl_available: raise RuntimeError('matplotlib is not installed.') + + # We import backends and pyplot only at the last possible moment (=now) + # because this has the side effect of selecting the matplotlib backend for + # good. Warn if backend has not been set yet. This check is the same as + # the one performed inside matplotlib.use. + if 'matplotlib.backends' not in sys.modules: + warnings.warn("Kwant's plotting functions have\nthe side effect of " + "selecting the matplotlib backend. To avoid this " + "warning,\nimport matplotlib.pyplot, " + "matplotlib.backends or call matplotlib.use().", + RuntimeWarning, stacklevel=3) + if output_mode == 'auto': output_mode = 'pyplot' if file is None else 'file' if output_mode == 'pyplot': - try: - fake_fig = matplotlib.pyplot.figure() - except AttributeError: - msg = ('matplotlib.pyplot is unavailable. Execute `import ' - 'matplotlib.pyplot` or use a different output mode.') - raise RuntimeError(msg) + from matplotlib import pyplot + fake_fig = pyplot.figure() fake_fig.canvas.figure = fig fig.canvas = fake_fig.canvas for ax in fig.axes: @@ -676,16 +684,16 @@ def output_fig(fig, output_mode='auto', file=None, savefile_opts=None, except AttributeError: pass if show: - matplotlib.pyplot.show() + pyplot.show() elif output_mode in ['return', 'file']: + from matplotlib.backends.backend_agg import FigureCanvasAgg fig.canvas = FigureCanvasAgg(fig) if output_mode == 'file': - fig.canvas = canvas = FigureCanvasAgg(fig) if savefile_opts is None: savefile_opts = ([], {}) if 'dpi' not in savefile_opts[1]: savefile_opts[1]['dpi'] = fig.dpi - canvas.print_figure(file, *savefile_opts[0], **savefile_opts[1]) + fig.canvas.print_figure(file, *savefile_opts[0], **savefile_opts[1]) else: raise ValueError('Unknown output_mode') return fig @@ -2158,8 +2166,9 @@ def current(syst, current, relwidth=0.05, **kwargs): A figure with the output if `ax` is not set, else None. """ - return streamplot(*interpolate_current(syst, current, relwidth), - **kwargs) + with _common.reraise_warnings(4): + return streamplot(*interpolate_current(syst, current, relwidth), + **kwargs) # TODO (Anton): Fix plotting of parts of the system using color = np.nan. diff --git a/kwant/tests/test_plotter.py b/kwant/tests/test_plotter.py index 12ea35ab54900e5fa29aa09e17f06de174dcc1c6..23b0d01a591be30145080734b8202aa6ea95fabe 100644 --- a/kwant/tests/test_plotter.py +++ b/kwant/tests/test_plotter.py @@ -24,10 +24,11 @@ try: from mpl_toolkits import mplot3d import matplotlib + # This check is the same as the one performed inside matplotlib.use. + matplotlib_backend_chosen = 'matplotlib.backends' in sys.modules # If the user did not already choose a backend, then choose # the one with the least dependencies. - # This check is the same as the one performed inside matplotlib.use. - if 'matplotlib.backends' not in sys.modules: + if not matplotlib_backend_chosen: matplotlib.use('Agg') from matplotlib import pyplot # pragma: no flakes @@ -37,6 +38,11 @@ except ImportError: from kwant import plotter +def test_matplotlib_backend_unset(): + """Simply importing Kwant should not set the matplotlib backend.""" + assert matplotlib_backend_chosen is False + + def test_importable_without_matplotlib(): prefix, sep, suffix = plotter.__file__.rpartition('.') if suffix in ['pyc', 'pyo']: