Skip to content

Commit

Permalink
Merge pull request #4343 from neutrinoceros/reusable_stylesheet
Browse files Browse the repository at this point in the history
ENH: add reusable matplotlib stylesheet
  • Loading branch information
matthewturk authored Mar 22, 2023
2 parents 11cb4be + 25b1d7b commit def170f
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 33 deletions.
2 changes: 2 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ recursive-include yt/visualization/volume_rendering/shaders *.fragmentshader *.v
include yt/sample_data_registry.json
include conftest.py
include yt/py.typed
include yt/default.mplstyle

prune yt/frontends/_skeleton
recursive-include yt/frontends/amrvac *.par
exclude .codecov.yml .coveragerc .git-blame-ignore-revs .gitmodules .hgchurn .mailmap
Expand Down
2 changes: 1 addition & 1 deletion conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ def pytest_configure(config):
r"Use Palette\.ADAPTIVE instead\.:DeprecationWarning",
)

if NUMPY_VERSION < Version("1.19") and MPL_VERSION < Version("3.3"):
if NUMPY_VERSION < Version("1.19") and MPL_VERSION < Version("3.4"):
# This warning is triggered from matplotlib in exactly one test at the time of writing
# and exclusively on the minimal test env. Upgrading numpy or matplotlib resolves
# the issue, so we can afford to ignore it.
Expand Down
77 changes: 77 additions & 0 deletions doc/source/visualizing/plots.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2419,3 +2419,80 @@ an example that includes slices and phase plots:
)
mp.save_fig("multi_slice_phase")
Using yt's style with matplotlib
--------------------------------

It is possible to use yt's plot style in outside of yt itself, with the
:func:`~yt.funcs.matplotlib_style_context` context manager

.. code-block:: python
import matplotlib.pyplot as plt
import numpy as np
import yt
plt.rcParams["font.size"] = 14
x = np.linspace(-np.pi, np.pi, 100)
y = np.sin(x)
with yt.funcs.matplotlib_style_context():
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set(
xlabel=r"$x$",
ylabel=r"$y$",
title="A yt-styled matplotlib figure",
)
Note that :func:`~yt.funcs.matplotlib_style_context` doesn't control the font
size, so we adjust it manually in the preamble.

With matplotlib 3.7 and newer, you can avoid importing yt altogether

.. code-block:: python
# requires matplotlib>=3.7
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams["font.size"] = 14
x = np.linspace(-np.pi, np.pi, 100)
y = np.sin(x)
with plt.style.context("yt.default"):
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set(
xlabel=r"$x$",
ylabel=r"$y$",
title="A yt-styled matplotlib figure",
)
and you can also enable yt's style without a context manager as

.. code-block:: python
# requires matplotlib>=3.7
import matplotlib.pyplot as plt
import numpy as np
plt.style.use("yt.default")
plt.rcParams["font.size"] = 14
x = np.linspace(-np.pi, np.pi, 100)
y = np.sin(x)
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set(
xlabel=r"$x$",
ylabel=r"$y$",
title="A yt-styled matplotlib figure",
)
For more details, see `matplotlib's documentation
<https://matplotlib.org/stable/tutorials/introductory/customizing.html#customizing-with-style-sheets>_`
14 changes: 14 additions & 0 deletions yt/default.mplstyle
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# basic usage (requires matplotlib 3.7+)
# >>> import matplotlib as mpl
# >>> mpl.style.use("yt.default")

xtick.top: True
ytick.right: True
xtick.minor.visible: True
ytick.minor.visible: True
xtick.direction: in
ytick.direction: in

font.family: stixgeneral

mathtext.fontset: cm
33 changes: 10 additions & 23 deletions yt/funcs.py
Original file line number Diff line number Diff line change
Expand Up @@ -994,35 +994,22 @@ def get_brewer_cmap(cmap):
return bmap.get_mpl_colormap(N=cmap[2])


@contextlib.contextmanager
def dummy_context_manager(*args, **kwargs):
yield


def matplotlib_style_context(style_name=None, after_reset=False):
def matplotlib_style_context(style="yt.default", after_reset=False):
"""Returns a context manager for controlling matplotlib style.
Arguments are passed to matplotlib.style.context() if specified. Defaults
to setting "classic" style, after resetting to the default config parameters.
On older matplotlib versions (<=1.5.0) where matplotlib.style isn't
available, returns a dummy context manager.
to setting yt's "yt.default" style, after resetting to the default config parameters.
"""
if style_name is None:
import matplotlib
# FUTURE: this function should be deprecated in favour of matplotlib.style.context
# after support for matplotlib 3.6 and older versions is dropped.
import matplotlib.style

style_name = {"mathtext.fontset": "cm"}
if Version(matplotlib.__version__) >= Version("3.3.0"):
style_name["mathtext.fallback"] = "cm"
else:
style_name["mathtext.fallback_to_cm"] = True
try:
import matplotlib.style
from yt.visualization._commons import MPL_VERSION

return matplotlib.style.context(style_name, after_reset=after_reset)
except ImportError:
pass
return dummy_context_manager()
if style == "yt.default" and MPL_VERSION < Version("3.7"):
style = importlib_resources.files("yt") / "default.mplstyle"

return matplotlib.style.context(style, after_reset=after_reset)


interactivity = False
Expand Down
19 changes: 13 additions & 6 deletions yt/visualization/_commons.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@
from importlib.metadata import version
from typing import TYPE_CHECKING, Optional, Type, TypeVar

import matplotlib as mpl
import numpy as np
from more_itertools import always_iterable
from packaging.version import Version

from yt.config import ytcfg

if sys.version_info >= (3, 9):
import importlib.resources as importlib_resources
else:
import importlib_resources

if sys.version_info >= (3, 10):
pass
else:
Expand All @@ -20,15 +26,16 @@
from ._mpl_imports import FigureCanvasBase


DEFAULT_FONT_PROPERTIES = {
"family": "stixgeneral",
"size": 18,
}

MPL_VERSION = Version(version("matplotlib"))

_yt_style = mpl.rc_params_from_file(
importlib_resources.files("yt") / "default.mplstyle", use_default_template=False
)
DEFAULT_FONT_PROPERTIES = {"family": _yt_style["font.family"][0]}

if MPL_VERSION >= Version("3.4"):
DEFAULT_FONT_PROPERTIES["math_fontfamily"] = "cm"
DEFAULT_FONT_PROPERTIES["math_fontfamily"] = _yt_style["mathtext.fontset"]
del _yt_style


def _get_supported_image_file_formats():
Expand Down
7 changes: 4 additions & 3 deletions yt/visualization/plot_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ def __init__(self, data_source, figure_size=None, fontsize: Optional[float] = No
if sys.version_info >= (3, 9):
font_dict = DEFAULT_FONT_PROPERTIES | {"size": fontsize}
else:
font_dict = {**DEFAULT_FONT_PROPERTIES, "size": fontsize} # type:ignore
font_dict = {**DEFAULT_FONT_PROPERTIES, "size": fontsize}

self._font_properties = FontProperties(**font_dict)
self._font_color = None
Expand Down Expand Up @@ -451,10 +451,11 @@ def set_font(self, font_dict=None):
self._font_color = font_dict.pop("color")
# Set default values if the user does not explicitly set them.
# this prevents reverting to the matplotlib defaults.
_default_size = {"size": self.__class__._default_font_size}
if sys.version_info >= (3, 9):
font_dict = DEFAULT_FONT_PROPERTIES | font_dict
font_dict = DEFAULT_FONT_PROPERTIES | _default_size | font_dict
else:
font_dict = {**DEFAULT_FONT_PROPERTIES, **font_dict}
font_dict = {**DEFAULT_FONT_PROPERTIES, **_default_size, **font_dict}
self._font_properties = FontProperties(**font_dict)
return self

Expand Down

0 comments on commit def170f

Please sign in to comment.