diff --git a/pyproject.toml b/pyproject.toml index 52795c4..0404237 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,10 +14,9 @@ maintainers = [ authors = [ { name = "Jane Doe" }, ] -requires-python = ">=3.11" +requires-python = ">=3.12" classifiers = [ "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", @@ -54,7 +53,7 @@ envs.default.dependency-groups = [ "dev" ] envs.hatch-test.dependency-groups = [ "dev", "test" ] envs.hatch-test.matrix = [ # Test the lowest and highest supported Python versions with normal deps - { deps = [ "stable" ], python = [ "3.11", "3.14" ] }, + { deps = [ "stable" ], python = [ "3.12", "3.14" ] }, # Test the newest supported Python version also with pre-release deps { deps = [ "pre" ], python = [ "3.14" ] }, ] diff --git a/src/fancypackage/plotting/__init__.py b/src/fancypackage/plotting/__init__.py index 537f882..b2b4853 100644 --- a/src/fancypackage/plotting/__init__.py +++ b/src/fancypackage/plotting/__init__.py @@ -1,3 +1,4 @@ from ._embedding import plot_embedding +from ._styles import style_context -__all__ = ["plot_embedding"] +__all__ = ["plot_embedding", "style_context"] diff --git a/src/fancypackage/plotting/_styles.py b/src/fancypackage/plotting/_styles.py new file mode 100644 index 0000000..d24899a --- /dev/null +++ b/src/fancypackage/plotting/_styles.py @@ -0,0 +1,81 @@ +# Adapted from https://github.com/adamgayoso/mplscience/tree/main/mplscience/_styledata +from collections.abc import Iterable + +import matplotlib + +_default = { + "svg.fonttype": "none", + "pdf.fonttype": 42, + "savefig.transparent": True, + "figure.figsize": (4, 4), + "axes.titlesize": 12, + "axes.titlepad": 8.0, + "axes.labelsize": 12, + "axes.linewidth": 1.2, + "axes.labelpad": 6.0, + "font.size": 12, + "font.family": "sans-serif", + "font.sans-serif": ["Arial", "Helvetica", "Computer Modern Sans Serif", "DejaVU Sans"], + "xtick.labelsize": 12, + "xtick.minor.size": 1.375, + "xtick.major.size": 2.75, + "xtick.major.pad": 2, + "xtick.minor.pad": 2, + "ytick.labelsize": 12, + "ytick.minor.size": 1.375, + "ytick.major.size": 2.75, + "ytick.major.pad": 2, + "ytick.minor.pad": 2, + "legend.fontsize": 12, + "legend.handlelength": 1.4, + "legend.numpoints": 1, + "legend.scatterpoints": 1, + "legend.frameon": False, + "lines.linewidth": 1.7, +} + +_despine = { + "axes.spines.top": False, + "axes.spines.right": False, +} + +STYLES = {"default": _default, "despine": _despine} + + +def style_context( + context: str | dict | Iterable[str | dict] = ("default", "despine"), reset_current: bool = False, **kwargs +): + """Create a style context for plotting. + + Parameters + ---------- + context + Style name(s) or settings to apply. Available: "default", "despine". + reset_current + Reset any custom styling before applying the context. + kwargs + Optional keyword arguments. To set specific rcparams, pass them as a dictionary via the `rcparams` argument. + + Returns + ------- + A matplotlib rc_context with the selected style(s). + """ + rcparams = {} + context = [context] if isinstance(context, (str, dict)) else context + + if reset_current: + from seaborn import reset_orig + + reset_orig() + + for spec in context: + if isinstance(spec, str) and (spec not in STYLES): + raise ValueError(f"Style '{spec}' not found. Available: {', '.join(f"'{style}'" for style in STYLES)}") + elif isinstance(spec, str): + rcparams.update(STYLES[spec]) + else: + rcparams.update(spec) + if "rcparams" in kwargs: + rcparams.update(kwargs["rcparams"]) + + return matplotlib.rc_context(rcparams)