Skip to content

Add xkcd style file. #14943

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
5 changes: 5 additions & 0 deletions doc/users/next_whats_new/2019-07-31-AL.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
xkcd style
``````````

The xkcd style previously available as `.pyplot.xkcd` is now also available as
a regular style file, which can be used as ``matplotlib.style.use("xkcd")``.
21 changes: 21 additions & 0 deletions lib/matplotlib/mpl-data/stylelib/_seaborn-v0_8-common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"figure.facecolor": "white",
"text.color": "0.15",
"axes.labelcolor": "0.15",
"legend.frameon": False,
"legend.numpoints": 1,
"legend.scatterpoints": 1,
"xtick.direction": "out",
"ytick.direction": "out",
"xtick.color": "0.15",
"ytick.color": "0.15",
"axes.axisbelow": True,
"image.cmap": "Greys",
"font.family": "sans-serif",
"font.sans-serif": [
"Arial", "Liberation Sans", "DejaVu Sans", "Bitstream Vera Sans",
"sans-serif",
],
"grid.linestyle": "-",
"lines.solid_capstyle": "round"
}
30 changes: 0 additions & 30 deletions lib/matplotlib/mpl-data/stylelib/seaborn-v0_8-dark.mplstyle

This file was deleted.

16 changes: 16 additions & 0 deletions lib/matplotlib/mpl-data/stylelib/seaborn-v0_8-dark.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import ast
from pathlib import Path


__mpl_style__ = {
**ast.literal_eval(Path(__file__).with_name("_seaborn-v0_8-common.py").read_text()),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what I'm dreading about python style files: Hard-to-follow dynamically defined contents.

IMHO we should use algorithmic code structures very sparingly in style files. There's great value in a declarative definition.

Furthermore, I assume that that this is done to prevent repetition and enforce consistency (DRY). However, style files are static (in fact the only time we have changed these ever was renaming them once). So, getting accidentally inconsistent is not a practical problem.

Let's just keep the pattern from the .mplstyle file of writing everything out and marking the common and individual parts by comments.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not just make the common file normal Python and import it, instead of replicating all that with our own evaluation stuff?

Copy link
Member

@timhoffm timhoffm Jun 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not quite clear what you mean.

  • We obviously have to continue to support *.mplstyle files
  • We additionally want a (for now internal) .py based style file. We can freely decide on the structure, but __mpl_style__ = {...} seems a reasonable choice - and since it is internal we can still make adjustments.

My concrete suggestion here is to not touch the seaborn .mplstyle files in this PR. It should only introduce stylelib\xkcd.py and the machinery to handle that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, if we want to change seaborn's styles, then instead of this ast.literal_eval work, it'd be simpler to give it an importable name, and do something like:

from . import _seaborn08_common

__mpl_style__ = {
    **_seaborn08_common.__mpl_style__,
    ...,
}

But again, that's only if we want to change these files with inheritance in this manner.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using imports is tricky because style files currently live in mpl-data and thus not directly importable without playing tricks with the import machinery, which seems to be too complex to be worth it.
If the general agreement is to not change the seaborn styles at all I can just revert that part of the change.

"axes.grid": False,
"axes.facecolor": "#EAEAF2",
"axes.edgecolor": "white",
"axes.linewidth": 0,
"grid.color": "white",
"xtick.major.size": 0,
"ytick.major.size": 0,
"xtick.minor.size": 0,
"ytick.minor.size": 0,
}
30 changes: 0 additions & 30 deletions lib/matplotlib/mpl-data/stylelib/seaborn-v0_8-darkgrid.mplstyle

This file was deleted.

16 changes: 16 additions & 0 deletions lib/matplotlib/mpl-data/stylelib/seaborn-v0_8-darkgrid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import ast
from pathlib import Path


__mpl_style__ = {
**ast.literal_eval(Path(__file__).with_name("_seaborn-v0_8-common.py").read_text()),
"axes.grid": True,
"axes.facecolor": "#EAEAF2",
"axes.edgecolor": "white",
"axes.linewidth": 0,
"grid.color": "white",
"xtick.major.size": 0,
"ytick.major.size": 0,
"xtick.minor.size": 0,
"ytick.minor.size": 0,
}
30 changes: 0 additions & 30 deletions lib/matplotlib/mpl-data/stylelib/seaborn-v0_8-ticks.mplstyle

This file was deleted.

16 changes: 16 additions & 0 deletions lib/matplotlib/mpl-data/stylelib/seaborn-v0_8-ticks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import ast
from pathlib import Path


__mpl_style__ = {
**ast.literal_eval(Path(__file__).with_name("_seaborn-v0_8-common.py").read_text()),
"axes.grid": False,
"axes.facecolor": "white",
"axes.edgecolor": ".15",
"axes.linewidth": 1.25,
"grid.color": ".8",
"xtick.major.size": 6,
"ytick.major.size": 6,
"xtick.minor.size": 3,
"ytick.minor.size": 3,
}
30 changes: 0 additions & 30 deletions lib/matplotlib/mpl-data/stylelib/seaborn-v0_8-white.mplstyle

This file was deleted.

17 changes: 17 additions & 0 deletions lib/matplotlib/mpl-data/stylelib/seaborn-v0_8-white.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

import ast
from pathlib import Path


__mpl_style__ = {
**ast.literal_eval(Path(__file__).with_name("_seaborn-v0_8-common.py").read_text()),
"axes.grid": False,
"axes.facecolor": "white",
"axes.edgecolor": ".15",
"axes.linewidth": 1.25,
"grid.color": ".8",
"xtick.major.size": 0,
"ytick.major.size": 0,
"xtick.minor.size": 0,
"ytick.minor.size": 0,
}
30 changes: 0 additions & 30 deletions lib/matplotlib/mpl-data/stylelib/seaborn-v0_8-whitegrid.mplstyle

This file was deleted.

16 changes: 16 additions & 0 deletions lib/matplotlib/mpl-data/stylelib/seaborn-v0_8-whitegrid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import ast
from pathlib import Path


__mpl_style__ = {
**ast.literal_eval(Path(__file__).with_name("_seaborn-v0_8-common.py").read_text()),
"axes.grid": True,
"axes.facecolor": "white",
"axes.edgecolor": ".8",
"axes.linewidth": 1,
"grid.color": ".8",
"xtick.major.size": 0,
"ytick.major.size": 0,
"xtick.minor.size": 0,
"ytick.minor.size": 0,
}
21 changes: 21 additions & 0 deletions lib/matplotlib/mpl-data/stylelib/xkcd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from matplotlib import patheffects


__mpl_style__ = {
"axes.edgecolor": "black",
"axes.grid": False,
"axes.linewidth": 1.5,
"axes.unicode_minus": False,
"figure.facecolor": "white",
"font.family": ["xkcd", "Humor Sans", "Comic Sans MS"],
"font.size": 14.0,
"grid.linewidth": 0.0,
"lines.linewidth": 2.0,
"path.effects": [patheffects.withStroke(linewidth=4, foreground="w")],
"path.sketch": (1, 100, 2),
"text.usetex": False,
"xtick.major.size": 8,
"xtick.major.width": 3,
"ytick.major.size": 8,
"ytick.major.width": 3,
}
18 changes: 1 addition & 17 deletions lib/matplotlib/pyplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -712,25 +712,9 @@ def xkcd(
stack = ExitStack()
stack.callback(dict.update, rcParams, rcParams.copy()) # type: ignore

from matplotlib import patheffects
rcParams.update({
'font.family': ['xkcd', 'xkcd Script', 'Humor Sans', 'Comic Neue',
'Comic Sans MS'],
'font.size': 14.0,
**style.library["xkcd"],
'path.sketch': (scale, length, randomness),
'path.effects': [
patheffects.withStroke(linewidth=4, foreground="w")],
'axes.linewidth': 1.5,
'lines.linewidth': 2.0,
'figure.facecolor': 'white',
'grid.linewidth': 0.0,
'axes.grid': False,
'axes.unicode_minus': False,
'axes.edgecolor': 'black',
'xtick.major.size': 8,
'xtick.major.width': 3,
'ytick.major.size': 8,
'ytick.major.width': 3,
})

return stack
Expand Down
29 changes: 28 additions & 1 deletion lib/matplotlib/style/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"""

import contextlib
import importlib
import logging
import os
from pathlib import Path
Expand Down Expand Up @@ -214,6 +215,29 @@ def read_style_directory(style_dir):
return styles


def _read_pystyle_base_directory():
"""
Return directory of styles defined in *style_dir* as Python files.

Because Python style files can execute arbitrary code, this feature is only
used internally to load files provided by Matplotlib itself, not
user-provided style files.

A Python style is a non-private Python module that exports a dict named
``__mpl_style__``, from which a `RcParams` is constructed. This convention
may be revisited if this feature ever becomes public.
"""
styles = {}
for path in Path(BASE_LIBRARY_PATH).glob("*.py"):
if path.name.startswith("_"):
continue
spec = importlib.util.spec_from_file_location(path.stem, path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
styles[path.stem] = mpl.RcParams(module.__mpl_style__)
return styles


def update_nested_dict(main_dict, new_dict):
"""
Update nested dict (only level of nesting) with new values.
Expand All @@ -230,7 +254,10 @@ def update_nested_dict(main_dict, new_dict):

# Load style library
# ==================
_base_library = read_style_directory(BASE_LIBRARY_PATH)
_base_library = {
**read_style_directory(BASE_LIBRARY_PATH),
**_read_pystyle_base_directory(),
}
library = {}
available = []

Expand Down