From cd607039865c70c55191e6e2c008be522347c0e1 Mon Sep 17 00:00:00 2001 From: caron <j.caron@fz-juelich.de> Date: Wed, 4 Mar 2020 08:57:49 +0100 Subject: [PATCH] Fix for matplotlib installation. Moved .mplstyle files inside vis. setup.cfg: added options.packages.find removed 3Dplot requirements (for now). setup.py: moved stylesheet installation to vis.tools, now optional. vis.tools: also added a use_style contextmanager that dynamically uses local styles (in package) or styles installed in user config folder. colors/decorators/plot2d: now use the new use_style contextmanager. --- .gitlab-ci.yml | 3 +- setup.cfg | 9 +-- setup.py | 16 +---- src/empyre/vis/colors.py | 6 +- src/empyre/vis/decorators.py | 3 +- .../vis/mplstyles}/empyre-image.mplstyle | 0 .../vis/mplstyles}/empyre-plot.mplstyle | 0 .../vis/mplstyles}/empyre-save.mplstyle | 0 src/empyre/vis/plot2d.py | 13 +++-- src/empyre/vis/tools.py | 58 +++++++++++++++++-- 10 files changed, 73 insertions(+), 35 deletions(-) rename {mplstyles => src/empyre/vis/mplstyles}/empyre-image.mplstyle (100%) rename {mplstyles => src/empyre/vis/mplstyles}/empyre-plot.mplstyle (100%) rename {mplstyles => src/empyre/vis/mplstyles}/empyre-save.mplstyle (100%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1b9c670..eb5731b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -50,6 +50,7 @@ test_docs: test_install: stage: test image: continuumio/miniconda3:latest + before_script: [] # before_script not needed here! script: - pip install .[all] @@ -70,7 +71,7 @@ pages: pypi: stage: deploy image: continuumio/miniconda3:latest - before_script: [] + before_script: [] # before_script not needed here! script: - pip install twine - python setup.py sdist bdist_wheel diff --git a/setup.cfg b/setup.cfg index 95906f1..9bc36ea 100644 --- a/setup.cfg +++ b/setup.cfg @@ -36,7 +36,6 @@ packages = find: python_requires = >=3.7 setup_requires = setuptools - matplotlib>=3 tests_require = coverage pytest @@ -51,10 +50,10 @@ install_requires = scipy Pillow +[options.packages.find] +where = src + [options.extras_require] -3Dplot = - qt==4.8 - mayavi==4.5 io = hyperspy fftw = @@ -62,8 +61,6 @@ fftw = colors = cmocean all = - qt==4.8 - mayavi==4.5 hyperspy pyfftw cmocean diff --git a/setup.py b/setup.py index bf06da1..7733513 100644 --- a/setup.py +++ b/setup.py @@ -2,9 +2,8 @@ # coding=utf-8 """Setup for testing, building, distributing and installing the 'EMPyRe'-package""" + import os -import glob -import shutil import subprocess from setuptools import setup, config @@ -27,16 +26,3 @@ with open(os.path.join(os.path.dirname(__file__), 'src', 'empyre', 'version.py') # Run setup (reads metadata & options from setup.cfg): print(R'running setup.py') setup() - - -import matplotlib as mpl -# Find matplotlib styles: -BASE_LIBRARY_PATH = os.path.join(mpl.get_data_path(), 'stylelib') -STYLE_PATH = os.path.join(os.getcwd(), 'mplstyles') -style_files = glob.glob(os.path.join(STYLE_PATH, '*.mplstyle')) -# Copy them to the local matplotlib styles folder: -print(f'copying matplotlib stylesheets to {BASE_LIBRARY_PATH}') -for style_path in style_files: - _, fname = os.path.split(style_path) - dest = os.path.join(BASE_LIBRARY_PATH, fname) - shutil.copy(style_path, dest) diff --git a/src/empyre/vis/colors.py b/src/empyre/vis/colors.py index 46b7d96..344a41f 100644 --- a/src/empyre/vis/colors.py +++ b/src/empyre/vis/colors.py @@ -24,6 +24,8 @@ from matplotlib.ticker import FixedLocator from mpl_toolkits.mplot3d import Axes3D # noqa: F401 from matplotlib.patches import Circle +from .tools import use_style + __all__ = ['Colormap3D', 'ColormapCubehelix', 'ColormapPerception', 'ColormapHLS', 'ColormapClassic', 'ColormapTransparent', 'cmaps', 'CMAP_CIRCULAR_DEFAULT', 'interpolate_color'] @@ -132,7 +134,7 @@ class Colormap3D(colors.Colormap, metaclass=abc.ABCMeta): if grayscale: color_wheel = color_wheel.convert('LA') # Plot the color wheel: - with plt.style.context('empyre-image'): # Only works on axes created WITHIN context! + with use_style('empyre-image'): # Only works on axes created WITHIN context! if axis is None: # If no axis is set, find the current or create a new one: fig = plt.figure() axis = fig.add_subplot(1, 1, 1, aspect='equal') @@ -293,7 +295,7 @@ class ColormapCubehelix(colors.LinearSegmentedColormap, Colormap3D): """ self._log.debug('Calling plot_helix') - with plt.style.context('empyre-plot'): + with use_style('empyre-plot'): fig = plt.figure(figsize=figsize, constrained_layout=True) gs = fig.add_gridspec(2, 1, height_ratios=[8, 1]) # Main plot: diff --git a/src/empyre/vis/decorators.py b/src/empyre/vis/decorators.py index a234bb8..a3509fe 100644 --- a/src/empyre/vis/decorators.py +++ b/src/empyre/vis/decorators.py @@ -17,6 +17,7 @@ from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar from mpl_toolkits.axes_grid1.inset_locator import inset_axes from . import colors +from .tools import use_style from ..fields.field import Field @@ -338,7 +339,7 @@ def colorbar(im, fig=None, cbar_axis=None, axes=None, position='right', pad=0.02 bounds = [left, bottom-pad-thickness, right-left, thickness] cbar_axis = fig.add_axes(bounds) # Create the colorbar: - with plt.style.context('empyre-image'): + with use_style('empyre-image'): if position in ('left', 'right'): cb = plt.colorbar(im, cax=cbar_axis, orientation='vertical') cb.ax.yaxis.set_ticks_position(position) diff --git a/mplstyles/empyre-image.mplstyle b/src/empyre/vis/mplstyles/empyre-image.mplstyle similarity index 100% rename from mplstyles/empyre-image.mplstyle rename to src/empyre/vis/mplstyles/empyre-image.mplstyle diff --git a/mplstyles/empyre-plot.mplstyle b/src/empyre/vis/mplstyles/empyre-plot.mplstyle similarity index 100% rename from mplstyles/empyre-plot.mplstyle rename to src/empyre/vis/mplstyles/empyre-plot.mplstyle diff --git a/mplstyles/empyre-save.mplstyle b/src/empyre/vis/mplstyles/empyre-save.mplstyle similarity index 100% rename from mplstyles/empyre-save.mplstyle rename to src/empyre/vis/mplstyles/empyre-save.mplstyle diff --git a/src/empyre/vis/plot2d.py b/src/empyre/vis/plot2d.py index cdf9830..2cbfa8e 100644 --- a/src/empyre/vis/plot2d.py +++ b/src/empyre/vis/plot2d.py @@ -13,6 +13,7 @@ from matplotlib.colors import LinearSegmentedColormap from PIL import Image from . import colors +from .tools import use_style from ..fields.field import Field @@ -68,7 +69,7 @@ def imshow(field, axis=None, cmap=None, **kwargs): cmap = cmocean.cm.balance except ImportError: _log.info('cmocean.balance not found, fallback to rRdBu!') - cmap = 'RdBu_r' # '_r' for reverse! + cmap = plt.get_cmap('RdBu_r') # '_r' for reverse! elif isinstance(cmap, str): # make sure we have a Colormap object (and not a string): cmap = plt.get_cmap(cmap) if cmap.name.replace('_r', '') in DIVERGING_CMAPS: # 'replace' also matches reverted cmaps! @@ -89,7 +90,7 @@ def imshow(field, axis=None, cmap=None, **kwargs): dim_v, dim_u, s_v, s_u = *squeezed_field.dim, *squeezed_field.scale kwargs.setdefault('extent', (0, dim_u * s_u, 0, dim_v * s_v)) # Plot with the empyre style context: - with plt.style.context('empyre-image'): # Only works on axes created WITHIN context! + with use_style('empyre-image'): # Only works on axes created WITHIN context! if axis is None: # If no axis is set, find the current or create a new one: axis = plt.gca() return axis.imshow(squeezed_field, cmap=cmap, **kwargs) @@ -132,7 +133,7 @@ def contour(field, axis=None, **kwargs): kwargs.setdefault('linestyles', 'dotted') kwargs.setdefault('linewidths', 2) # Plot with the empyre style context: - with plt.style.context('empyre-image'): # Only works on axes created WITHIN context! + with use_style('empyre-image'): # Only works on axes created WITHIN context! if axis is None: # If no axis is set, find the current or create a new one: axis = plt.gca() axis.set_aspect('equal') @@ -197,7 +198,7 @@ def colorvec(field, axis=None, **kwargs): dim_v, dim_u, s_v, s_u = *squeezed_field.dim, *squeezed_field.scale kwargs.setdefault('extent', (0, dim_u * s_u, 0, dim_v * s_v)) # Plot with the empyre style context: - with plt.style.context('empyre-image'): # Only works on axes created WITHIN context! + with use_style('empyre-image'): # Only works on axes created WITHIN context! if axis is None: # If no axis is set, find the current or create a new one: axis = plt.gca() return axis.imshow(Image.fromarray(rgb), **kwargs) @@ -255,7 +256,7 @@ def cosine_contours(field, axis=None, gain='auto', cmap=None, **kwargs): dim_v, dim_u, s_v, s_u = *squeezed_field.dim, *squeezed_field.scale kwargs.setdefault('extent', (0, dim_u * s_u, 0, dim_v * s_v)) # Plot with the empyre style context: - with plt.style.context('empyre-image'): # Only works on axes created WITHIN context! + with use_style('empyre-image'): # Only works on axes created WITHIN context! if axis is None: # If no axis is set, find the current or create a new one: axis = plt.gca() return axis.imshow(contours, cmap=cmap, **kwargs) @@ -376,7 +377,7 @@ def quiver(field, axis=None, color_angles=False, cmap=None, n_bin='auto', bin_wi kwargs.setdefault('minshaft', 2) kwargs.setdefault('linewidths', 1) # Plot with the empyre style context: - with plt.style.context('empyre-image'): # Only works on axes created WITHIN context! + with use_style('empyre-image'): # Only works on axes created WITHIN context! if axis is None: # If no axis is set, find the current or create a new one: axis = plt.gca() axis.set_xlim(0, u_lim) diff --git a/src/empyre/vis/tools.py b/src/empyre/vis/tools.py index 2d5b3a4..28cdde8 100644 --- a/src/empyre/vis/tools.py +++ b/src/empyre/vis/tools.py @@ -5,16 +5,21 @@ """This module provides helper functions to the vis module.""" +import os +import glob +import shutil import logging from numbers import Number +from contextlib import contextmanager import numpy as np +import matplotlib as mpl import matplotlib.pyplot as plt from ..fields.field import Field -__all__ = ['new', 'savefig', 'calc_figsize'] +__all__ = ['new', 'savefig', 'calc_figsize', 'use_style', 'copy_mpl_stylesheets'] _log = logging.getLogger(__name__) @@ -79,10 +84,10 @@ def new(nrows=1, ncols=1, mode='image', figsize=None, textwidth=None, width_scal assert isinstance(aspect, Number), 'aspect has to be None, a number or a field instance squeezable to 2D!' figsize = calc_figsize(width_scale=width_scale, aspect=aspect, textwidth=textwidth) if mode == 'image': - with plt.style.context('empyre-image'): + with use_style('empyre-image'): return plt.subplots(nrows=nrows, ncols=ncols, figsize=figsize, **kwargs) else: # mode == 'plot': - with plt.style.context('empyre-plot'): + with use_style('empyre-plot'): return plt.subplots(nrows=nrows, ncols=ncols, figsize=figsize, **kwargs) @@ -101,7 +106,7 @@ def savefig(fname, **kwargs): """ _log.debug('Calling savefig') - with plt.style.context('empyre-save'): + with use_style('empyre-save'): plt.savefig(fname, **kwargs) @@ -143,3 +148,48 @@ def calc_figsize(textwidth, width_scale=1, aspect=1): raise ValueError(f"aspect has to be either a number, 'same' or 'golden'! Was {aspect}!") fig_size = [fig_width, fig_height] # Both in inches return fig_size + + +@contextmanager +def use_style(stylename): + """Context that uses a matplotlib stylesheet. Can fall back to local mpl stylesheets if necessary! + + Parameters + ---------- + stylename : str + A style specification. + + Yields + ------- + context + Context manager for using style settings temporarily. + + """ + try: # Try to load the style directly (works if it is installed somewhere mpl looks for it): + with plt.style.context(stylename) as context: + yield context + except OSError: # Stylesheet not found, use local ones: + mplstyle_path = os.path.join(os.path.dirname(__file__), 'mplstyles', f'{stylename}.mplstyle') + with plt.style.context(mplstyle_path) as context: + yield context + + +def copy_mpl_stylesheets(): + """Copy matplotlib styles to the users matplotlib config directory. Useful if you want to utilize them elsewhere. + + Notes + ----- + You might need to restart your Python session for the stylesheets to be recognized/found! + + """ + # Find matplotlib styles: + user_stylelib_path = os.path.join(mpl.get_configdir(), 'stylelib') + vis_dir = os.path.dirname(__file__) + style_files = glob.glob(os.path.join(vis_dir, 'mplstyles', '*.mplstyle')) + # Copy them to the local matplotlib styles folder: + if not os.path.exists(user_stylelib_path): + os.makedirs(user_stylelib_path) + for style_path in style_files: + _, fname = os.path.split(style_path) + dest = os.path.join(user_stylelib_path, fname) + shutil.copy(style_path, dest) -- GitLab