oh-mpl-paper 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oh_mpl_paper-0.1.0/PKG-INFO +87 -0
- oh_mpl_paper-0.1.0/README.md +72 -0
- oh_mpl_paper-0.1.0/oh_mpl_paper/__init__.py +51 -0
- oh_mpl_paper-0.1.0/oh_mpl_paper/styles/base.mplstyle +63 -0
- oh_mpl_paper-0.1.0/oh_mpl_paper/styles/ieee.mplstyle +55 -0
- oh_mpl_paper-0.1.0/oh_mpl_paper/styles/k.wang.mplstyle +76 -0
- oh_mpl_paper-0.1.0/oh_mpl_paper/styles/nature.mplstyle +54 -0
- oh_mpl_paper-0.1.0/oh_mpl_paper/styles/poster.mplstyle +51 -0
- oh_mpl_paper-0.1.0/oh_mpl_paper/styles/presentation.mplstyle +52 -0
- oh_mpl_paper-0.1.0/oh_mpl_paper.egg-info/PKG-INFO +87 -0
- oh_mpl_paper-0.1.0/oh_mpl_paper.egg-info/SOURCES.txt +15 -0
- oh_mpl_paper-0.1.0/oh_mpl_paper.egg-info/dependency_links.txt +1 -0
- oh_mpl_paper-0.1.0/oh_mpl_paper.egg-info/requires.txt +4 -0
- oh_mpl_paper-0.1.0/oh_mpl_paper.egg-info/top_level.txt +1 -0
- oh_mpl_paper-0.1.0/pyproject.toml +33 -0
- oh_mpl_paper-0.1.0/setup.cfg +4 -0
- oh_mpl_paper-0.1.0/tests/test_styles.py +72 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: oh-mpl-paper
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Publication-ready Matplotlib styles for scientific papers, posters, and presentations.
|
|
5
|
+
Author: Oscar Hickman
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/OscarHickman/thesis-themes
|
|
8
|
+
Project-URL: Repository, https://github.com/OscarHickman/thesis-themes
|
|
9
|
+
Project-URL: Issues, https://github.com/OscarHickman/thesis-themes/issues
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: matplotlib>=3.3.0
|
|
13
|
+
Provides-Extra: test
|
|
14
|
+
Requires-Dist: pytest>=7.0.0; extra == "test"
|
|
15
|
+
|
|
16
|
+
# oh-mpl-paper
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
Publication-ready Matplotlib styles, one function call away. Every academic
|
|
21
|
+
project ends up rewriting the same 40 lines of `rcParams` boilerplate to get
|
|
22
|
+
figures that satisfy a journal's font, size, and line-weight requirements —
|
|
23
|
+
`oh-mpl-paper` bundles that boilerplate as a handful of named, tested
|
|
24
|
+
`.mplstyle` presets and a two-line API.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install git+https://github.com/OscarHickman/thesis-themes.git
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import oh_mpl_paper as p
|
|
36
|
+
import matplotlib.pyplot as plt
|
|
37
|
+
|
|
38
|
+
p.setconfig("nature") # sets all rcParams for the chosen style
|
|
39
|
+
|
|
40
|
+
plt.plot(x, y)
|
|
41
|
+
plt.savefig("figure.pdf") # sized and styled for the target venue
|
|
42
|
+
|
|
43
|
+
print(p.available()) # ['base', 'ieee', 'k.wang', 'nature', 'poster', 'presentation']
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Calling `setconfig` with an unrecognised name raises a `ValueError` listing
|
|
47
|
+
every style currently bundled with the package.
|
|
48
|
+
|
|
49
|
+
## Available styles
|
|
50
|
+
|
|
51
|
+
| Style | Use case |
|
|
52
|
+
|----------------|---------------------------------------------------------------------------|
|
|
53
|
+
| `base` | Generic scientific-paper defaults (Type 42 fonts, colorblind-safe cycle). |
|
|
54
|
+
| `nature` | Single-column Nature-family figures, 89mm wide, 8pt sans-serif fonts. |
|
|
55
|
+
| `ieee` | IEEE transactions single-column format, 3.5in wide, Times New Roman. |
|
|
56
|
+
| `k.wang` | SaUCE/`sauce_xi` MNRAS paper-figures house style (single-column, serif + Computer Modern math, muted inward ticks). |
|
|
57
|
+
| `presentation` | 16:9 slide decks — large fonts, thick lines, transparent background. |
|
|
58
|
+
| `poster` | A0 conference posters — very large fonts, thick lines for ~2m viewing. |
|
|
59
|
+
|
|
60
|
+
All styles embed Type 42 (TrueType) fonts in PDF/PS output so text stays
|
|
61
|
+
editable in Illustrator or Inkscape, and default to a colorblind-safe
|
|
62
|
+
categorical color cycle.
|
|
63
|
+
|
|
64
|
+
## Development
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install -e ".[test]"
|
|
68
|
+
pytest
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Releasing
|
|
72
|
+
|
|
73
|
+
Pushing a tag of the form `vX.Y.Z` triggers `.github/workflows/publish.yml`,
|
|
74
|
+
which builds the package and publishes it to PyPI via
|
|
75
|
+
[Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (no API
|
|
76
|
+
token stored in this repo). The tag's version must match the `version`
|
|
77
|
+
field in `pyproject.toml` or the workflow fails before publishing.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# bump version in pyproject.toml, commit, then:
|
|
81
|
+
git tag v0.1.1
|
|
82
|
+
git push origin v0.1.1
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
PyPI-side setup (one-time, done outside this repo): add a trusted publisher
|
|
86
|
+
on the `oh-mpl-paper` project pointing at this GitHub repo, workflow file
|
|
87
|
+
`publish.yml`, and environment name `pypi`.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# oh-mpl-paper
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Publication-ready Matplotlib styles, one function call away. Every academic
|
|
6
|
+
project ends up rewriting the same 40 lines of `rcParams` boilerplate to get
|
|
7
|
+
figures that satisfy a journal's font, size, and line-weight requirements —
|
|
8
|
+
`oh-mpl-paper` bundles that boilerplate as a handful of named, tested
|
|
9
|
+
`.mplstyle` presets and a two-line API.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install git+https://github.com/OscarHickman/thesis-themes.git
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
import oh_mpl_paper as p
|
|
21
|
+
import matplotlib.pyplot as plt
|
|
22
|
+
|
|
23
|
+
p.setconfig("nature") # sets all rcParams for the chosen style
|
|
24
|
+
|
|
25
|
+
plt.plot(x, y)
|
|
26
|
+
plt.savefig("figure.pdf") # sized and styled for the target venue
|
|
27
|
+
|
|
28
|
+
print(p.available()) # ['base', 'ieee', 'k.wang', 'nature', 'poster', 'presentation']
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Calling `setconfig` with an unrecognised name raises a `ValueError` listing
|
|
32
|
+
every style currently bundled with the package.
|
|
33
|
+
|
|
34
|
+
## Available styles
|
|
35
|
+
|
|
36
|
+
| Style | Use case |
|
|
37
|
+
|----------------|---------------------------------------------------------------------------|
|
|
38
|
+
| `base` | Generic scientific-paper defaults (Type 42 fonts, colorblind-safe cycle). |
|
|
39
|
+
| `nature` | Single-column Nature-family figures, 89mm wide, 8pt sans-serif fonts. |
|
|
40
|
+
| `ieee` | IEEE transactions single-column format, 3.5in wide, Times New Roman. |
|
|
41
|
+
| `k.wang` | SaUCE/`sauce_xi` MNRAS paper-figures house style (single-column, serif + Computer Modern math, muted inward ticks). |
|
|
42
|
+
| `presentation` | 16:9 slide decks — large fonts, thick lines, transparent background. |
|
|
43
|
+
| `poster` | A0 conference posters — very large fonts, thick lines for ~2m viewing. |
|
|
44
|
+
|
|
45
|
+
All styles embed Type 42 (TrueType) fonts in PDF/PS output so text stays
|
|
46
|
+
editable in Illustrator or Inkscape, and default to a colorblind-safe
|
|
47
|
+
categorical color cycle.
|
|
48
|
+
|
|
49
|
+
## Development
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
pip install -e ".[test]"
|
|
53
|
+
pytest
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Releasing
|
|
57
|
+
|
|
58
|
+
Pushing a tag of the form `vX.Y.Z` triggers `.github/workflows/publish.yml`,
|
|
59
|
+
which builds the package and publishes it to PyPI via
|
|
60
|
+
[Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (no API
|
|
61
|
+
token stored in this repo). The tag's version must match the `version`
|
|
62
|
+
field in `pyproject.toml` or the workflow fails before publishing.
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# bump version in pyproject.toml, commit, then:
|
|
66
|
+
git tag v0.1.1
|
|
67
|
+
git push origin v0.1.1
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
PyPI-side setup (one-time, done outside this repo): add a trusted publisher
|
|
71
|
+
on the `oh-mpl-paper` project pointing at this GitHub repo, workflow file
|
|
72
|
+
`publish.yml`, and environment name `pypi`.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""oh_mpl_paper: publication-ready Matplotlib styles for scientific papers.
|
|
2
|
+
|
|
3
|
+
Usage
|
|
4
|
+
-----
|
|
5
|
+
>>> import oh_mpl_paper as p
|
|
6
|
+
>>> import matplotlib.pyplot as plt
|
|
7
|
+
>>> p.setconfig("nature")
|
|
8
|
+
>>> plt.plot(x, y)
|
|
9
|
+
>>> plt.show()
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import List
|
|
15
|
+
|
|
16
|
+
import matplotlib.pyplot as plt
|
|
17
|
+
|
|
18
|
+
__version__ = "0.1.0"
|
|
19
|
+
|
|
20
|
+
_STYLES_DIR = Path(__file__).resolve().parent / "styles"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def available() -> List[str]:
|
|
24
|
+
"""Return the sorted list of valid style names for :func:`setconfig`."""
|
|
25
|
+
return sorted(f.stem for f in _STYLES_DIR.glob("*.mplstyle"))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def setconfig(config_name: str) -> None:
|
|
29
|
+
"""Apply the named publication style globally via ``plt.style.use``.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
config_name:
|
|
34
|
+
Name of a style bundled under ``oh_mpl_paper/styles/`` (without the
|
|
35
|
+
``.mplstyle`` extension), e.g. ``"nature"``, ``"ieee"``, ``"k.wang"``.
|
|
36
|
+
|
|
37
|
+
Raises
|
|
38
|
+
------
|
|
39
|
+
ValueError
|
|
40
|
+
If ``config_name`` does not match any bundled style. The error lists
|
|
41
|
+
every currently available style.
|
|
42
|
+
"""
|
|
43
|
+
style_path = _STYLES_DIR / f"{config_name}.mplstyle"
|
|
44
|
+
if not style_path.is_file():
|
|
45
|
+
raise ValueError(
|
|
46
|
+
f"Unknown style {config_name!r}. Available styles: {available()}"
|
|
47
|
+
)
|
|
48
|
+
plt.style.use(str(style_path))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
__all__ = ["setconfig", "available", "__version__"]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# base.mplstyle — shared foundation for all oh_mpl_paper styles.
|
|
2
|
+
# Editable, embedded (Type 42) fonts; colorblind-safe categorical cycle
|
|
3
|
+
# (Okabe-Ito); inward ticks; clean two-spine axes.
|
|
4
|
+
|
|
5
|
+
# --- Fonts (editable in Illustrator/Inkscape) -------------------------------
|
|
6
|
+
pdf.fonttype: 42
|
|
7
|
+
ps.fonttype: 42
|
|
8
|
+
font.family: serif
|
|
9
|
+
font.weight: normal
|
|
10
|
+
font.size: 9
|
|
11
|
+
mathtext.fontset: cm
|
|
12
|
+
mathtext.default: it
|
|
13
|
+
|
|
14
|
+
# --- Axes --------------------------------------------------------------------
|
|
15
|
+
axes.linewidth: 0.8
|
|
16
|
+
axes.labelsize: 9
|
|
17
|
+
axes.labelweight: normal
|
|
18
|
+
axes.titlesize: 9
|
|
19
|
+
axes.spines.top: False
|
|
20
|
+
axes.spines.right: False
|
|
21
|
+
axes.grid: False
|
|
22
|
+
axes.axisbelow: True
|
|
23
|
+
|
|
24
|
+
# Okabe-Ito colorblind-safe categorical palette
|
|
25
|
+
axes.prop_cycle: cycler('color', ['000000', 'E69F00', '56B4E9', '009E73', 'F0E442', '0072B2', 'D55E00', 'CC79A7'])
|
|
26
|
+
|
|
27
|
+
# --- Ticks ---------------------------------------------------------------
|
|
28
|
+
xtick.direction: in
|
|
29
|
+
xtick.labelsize: 8
|
|
30
|
+
xtick.major.size: 3.0
|
|
31
|
+
xtick.major.width: 0.6
|
|
32
|
+
xtick.minor.size: 1.6
|
|
33
|
+
xtick.minor.width: 0.5
|
|
34
|
+
xtick.minor.visible: True
|
|
35
|
+
xtick.top: False
|
|
36
|
+
|
|
37
|
+
ytick.direction: in
|
|
38
|
+
ytick.labelsize: 8
|
|
39
|
+
ytick.major.size: 3.0
|
|
40
|
+
ytick.major.width: 0.6
|
|
41
|
+
ytick.minor.size: 1.6
|
|
42
|
+
ytick.minor.width: 0.5
|
|
43
|
+
ytick.minor.visible: True
|
|
44
|
+
ytick.right: False
|
|
45
|
+
|
|
46
|
+
# --- Lines & markers -----------------------------------------------------
|
|
47
|
+
lines.linewidth: 1.2
|
|
48
|
+
lines.markersize: 4
|
|
49
|
+
patch.linewidth: 0.5
|
|
50
|
+
errorbar.capsize: 2
|
|
51
|
+
|
|
52
|
+
# --- Legend ----------------------------------------------------------------
|
|
53
|
+
legend.fontsize: 7
|
|
54
|
+
legend.title_fontsize: 7
|
|
55
|
+
legend.frameon: False
|
|
56
|
+
legend.handlelength: 1.6
|
|
57
|
+
legend.labelspacing: 0.3
|
|
58
|
+
|
|
59
|
+
# --- Figure / saving -------------------------------------------------------
|
|
60
|
+
figure.figsize: 3.5, 2.625
|
|
61
|
+
figure.dpi: 150
|
|
62
|
+
savefig.dpi: 300
|
|
63
|
+
savefig.bbox: tight
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# ieee.mplstyle — strict IEEE transactions single-column format.
|
|
2
|
+
# Column width 3.5in, Times New Roman, 8pt fonts.
|
|
3
|
+
|
|
4
|
+
pdf.fonttype: 42
|
|
5
|
+
ps.fonttype: 42
|
|
6
|
+
font.family: serif
|
|
7
|
+
font.serif: Times New Roman, Times, DejaVu Serif
|
|
8
|
+
font.size: 8
|
|
9
|
+
mathtext.fontset: stix
|
|
10
|
+
mathtext.rm: serif
|
|
11
|
+
|
|
12
|
+
axes.linewidth: 0.8
|
|
13
|
+
axes.labelsize: 8
|
|
14
|
+
axes.labelweight: normal
|
|
15
|
+
axes.titlesize: 8
|
|
16
|
+
axes.spines.top: True
|
|
17
|
+
axes.spines.right: True
|
|
18
|
+
axes.grid: False
|
|
19
|
+
axes.axisbelow: True
|
|
20
|
+
axes.prop_cycle: cycler('color', ['000000', 'E69F00', '56B4E9', '009E73', 'F0E442', '0072B2', 'D55E00', 'CC79A7'])
|
|
21
|
+
|
|
22
|
+
xtick.direction: in
|
|
23
|
+
xtick.labelsize: 7
|
|
24
|
+
xtick.major.size: 3.0
|
|
25
|
+
xtick.major.width: 0.6
|
|
26
|
+
xtick.minor.size: 1.5
|
|
27
|
+
xtick.minor.width: 0.4
|
|
28
|
+
xtick.minor.visible: True
|
|
29
|
+
xtick.top: True
|
|
30
|
+
|
|
31
|
+
ytick.direction: in
|
|
32
|
+
ytick.labelsize: 7
|
|
33
|
+
ytick.major.size: 3.0
|
|
34
|
+
ytick.major.width: 0.6
|
|
35
|
+
ytick.minor.size: 1.5
|
|
36
|
+
ytick.minor.width: 0.4
|
|
37
|
+
ytick.minor.visible: True
|
|
38
|
+
ytick.right: True
|
|
39
|
+
|
|
40
|
+
lines.linewidth: 1.0
|
|
41
|
+
lines.markersize: 4
|
|
42
|
+
patch.linewidth: 0.5
|
|
43
|
+
errorbar.capsize: 2
|
|
44
|
+
|
|
45
|
+
legend.fontsize: 7
|
|
46
|
+
legend.title_fontsize: 7
|
|
47
|
+
legend.frameon: True
|
|
48
|
+
legend.framealpha: 0.9
|
|
49
|
+
legend.handlelength: 1.6
|
|
50
|
+
legend.labelspacing: 0.3
|
|
51
|
+
|
|
52
|
+
figure.figsize: 3.5, 2.625
|
|
53
|
+
figure.dpi: 150
|
|
54
|
+
savefig.dpi: 600
|
|
55
|
+
savefig.bbox: tight
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# k.wang.mplstyle — faithful reproduction of the SaUCE/sauce_xi MNRAS
|
|
2
|
+
# paper-figures house style (galform_analysis.utils.matplotlib_config
|
|
3
|
+
# defaults, as overridden by paper_figures/_shared.py, 2026-07 revision).
|
|
4
|
+
# Single-column MNRAS width (3.31in / ~84mm); serif + Computer Modern math.
|
|
5
|
+
|
|
6
|
+
pdf.fonttype: 42
|
|
7
|
+
ps.fonttype: 42
|
|
8
|
+
font.family: serif
|
|
9
|
+
font.weight: normal
|
|
10
|
+
font.size: 8
|
|
11
|
+
mathtext.fontset: cm
|
|
12
|
+
mathtext.default: it
|
|
13
|
+
|
|
14
|
+
figure.figsize: 3.31, 2.648
|
|
15
|
+
figure.subplot.wspace: 0.25
|
|
16
|
+
figure.subplot.hspace: 0.25
|
|
17
|
+
figure.constrained_layout.use: True
|
|
18
|
+
|
|
19
|
+
axes.labelsize: 11
|
|
20
|
+
axes.labelweight: normal
|
|
21
|
+
axes.titlesize: 11
|
|
22
|
+
axes.linewidth: 0.735
|
|
23
|
+
axes.edgecolor: (0.0, 0.0, 0.0, 0.8)
|
|
24
|
+
axes.grid: False
|
|
25
|
+
axes.axisbelow: True
|
|
26
|
+
|
|
27
|
+
xtick.color: (0.0, 0.0, 0.0, 0.8)
|
|
28
|
+
xtick.labelsize: 9
|
|
29
|
+
xtick.direction: in
|
|
30
|
+
xtick.top: True
|
|
31
|
+
xtick.major.size: 3.5
|
|
32
|
+
xtick.major.width: 0.7
|
|
33
|
+
xtick.minor.size: 2.0
|
|
34
|
+
xtick.minor.width: 0.5
|
|
35
|
+
xtick.minor.visible: True
|
|
36
|
+
|
|
37
|
+
ytick.color: (0.0, 0.0, 0.0, 0.8)
|
|
38
|
+
ytick.labelsize: 9
|
|
39
|
+
ytick.direction: in
|
|
40
|
+
ytick.right: True
|
|
41
|
+
ytick.major.size: 3.5
|
|
42
|
+
ytick.major.width: 0.7
|
|
43
|
+
ytick.minor.size: 2.0
|
|
44
|
+
ytick.minor.width: 0.5
|
|
45
|
+
ytick.minor.visible: True
|
|
46
|
+
|
|
47
|
+
grid.color: gray
|
|
48
|
+
grid.alpha: 0.5
|
|
49
|
+
grid.linestyle: (0, (1.6, 1.6))
|
|
50
|
+
grid.linewidth: 0.8
|
|
51
|
+
|
|
52
|
+
patch.edgecolor: (0.0, 0.0, 0.0, 0.8)
|
|
53
|
+
patch.linewidth: 0.5
|
|
54
|
+
|
|
55
|
+
lines.linewidth: 1.2
|
|
56
|
+
errorbar.capsize: 2
|
|
57
|
+
|
|
58
|
+
legend.numpoints: 2
|
|
59
|
+
legend.scatterpoints: 3
|
|
60
|
+
legend.markerscale: 1.0
|
|
61
|
+
legend.fontsize: 7
|
|
62
|
+
legend.title_fontsize: 7
|
|
63
|
+
legend.loc: best
|
|
64
|
+
legend.handlelength: 2.0
|
|
65
|
+
legend.handleheight: 0.5
|
|
66
|
+
legend.labelspacing: 0.1
|
|
67
|
+
legend.handletextpad: 0.5
|
|
68
|
+
legend.borderpad: 0.25
|
|
69
|
+
legend.borderaxespad: 0.8
|
|
70
|
+
legend.columnspacing: 1.0
|
|
71
|
+
legend.shadow: False
|
|
72
|
+
legend.frameon: False
|
|
73
|
+
legend.framealpha: 0.8
|
|
74
|
+
|
|
75
|
+
savefig.dpi: 300
|
|
76
|
+
savefig.bbox: tight
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# nature.mplstyle — single-column Nature/Nature-family figure format.
|
|
2
|
+
# Column width 3.46in (~89mm), 8pt fonts, tight constrained layout.
|
|
3
|
+
|
|
4
|
+
pdf.fonttype: 42
|
|
5
|
+
ps.fonttype: 42
|
|
6
|
+
font.family: sans-serif
|
|
7
|
+
font.sans-serif: Arial, Helvetica, DejaVu Sans
|
|
8
|
+
font.size: 8
|
|
9
|
+
mathtext.fontset: dejavusans
|
|
10
|
+
|
|
11
|
+
axes.linewidth: 0.8
|
|
12
|
+
axes.labelsize: 8
|
|
13
|
+
axes.labelweight: normal
|
|
14
|
+
axes.titlesize: 8
|
|
15
|
+
axes.spines.top: False
|
|
16
|
+
axes.spines.right: False
|
|
17
|
+
axes.grid: False
|
|
18
|
+
axes.axisbelow: True
|
|
19
|
+
axes.prop_cycle: cycler('color', ['000000', 'E69F00', '56B4E9', '009E73', 'F0E442', '0072B2', 'D55E00', 'CC79A7'])
|
|
20
|
+
|
|
21
|
+
xtick.direction: in
|
|
22
|
+
xtick.labelsize: 7
|
|
23
|
+
xtick.major.size: 2.5
|
|
24
|
+
xtick.major.width: 0.6
|
|
25
|
+
xtick.minor.size: 1.5
|
|
26
|
+
xtick.minor.width: 0.4
|
|
27
|
+
xtick.minor.visible: True
|
|
28
|
+
xtick.top: False
|
|
29
|
+
|
|
30
|
+
ytick.direction: in
|
|
31
|
+
ytick.labelsize: 7
|
|
32
|
+
ytick.major.size: 2.5
|
|
33
|
+
ytick.major.width: 0.6
|
|
34
|
+
ytick.minor.size: 1.5
|
|
35
|
+
ytick.minor.width: 0.4
|
|
36
|
+
ytick.minor.visible: True
|
|
37
|
+
ytick.right: False
|
|
38
|
+
|
|
39
|
+
lines.linewidth: 1.0
|
|
40
|
+
lines.markersize: 4
|
|
41
|
+
patch.linewidth: 0.5
|
|
42
|
+
errorbar.capsize: 2
|
|
43
|
+
|
|
44
|
+
legend.fontsize: 6.5
|
|
45
|
+
legend.title_fontsize: 6.5
|
|
46
|
+
legend.frameon: False
|
|
47
|
+
legend.handlelength: 1.4
|
|
48
|
+
legend.labelspacing: 0.3
|
|
49
|
+
|
|
50
|
+
figure.figsize: 3.46, 2.6
|
|
51
|
+
figure.dpi: 150
|
|
52
|
+
figure.constrained_layout.use: True
|
|
53
|
+
savefig.dpi: 300
|
|
54
|
+
savefig.bbox: tight
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# poster.mplstyle — A0 conference poster format.
|
|
2
|
+
# Very large fonts, thick lines, readable from ~2m distance.
|
|
3
|
+
|
|
4
|
+
pdf.fonttype: 42
|
|
5
|
+
ps.fonttype: 42
|
|
6
|
+
font.family: sans-serif
|
|
7
|
+
font.sans-serif: Arial, Helvetica, DejaVu Sans
|
|
8
|
+
font.size: 24
|
|
9
|
+
mathtext.fontset: dejavusans
|
|
10
|
+
|
|
11
|
+
axes.linewidth: 2.4
|
|
12
|
+
axes.labelsize: 28
|
|
13
|
+
axes.labelweight: bold
|
|
14
|
+
axes.titlesize: 32
|
|
15
|
+
axes.titleweight: bold
|
|
16
|
+
axes.spines.top: False
|
|
17
|
+
axes.spines.right: False
|
|
18
|
+
axes.grid: False
|
|
19
|
+
axes.axisbelow: True
|
|
20
|
+
axes.prop_cycle: cycler('color', ['E69F00', '56B4E9', '009E73', 'D55E00', '0072B2', 'CC79A7', 'F0E442', '000000'])
|
|
21
|
+
|
|
22
|
+
xtick.direction: in
|
|
23
|
+
xtick.labelsize: 22
|
|
24
|
+
xtick.major.size: 8.0
|
|
25
|
+
xtick.major.width: 2.0
|
|
26
|
+
xtick.minor.visible: False
|
|
27
|
+
xtick.top: False
|
|
28
|
+
|
|
29
|
+
ytick.direction: in
|
|
30
|
+
ytick.labelsize: 22
|
|
31
|
+
ytick.major.size: 8.0
|
|
32
|
+
ytick.major.width: 2.0
|
|
33
|
+
ytick.minor.visible: False
|
|
34
|
+
ytick.right: False
|
|
35
|
+
|
|
36
|
+
lines.linewidth: 4.0
|
|
37
|
+
lines.markersize: 12
|
|
38
|
+
patch.linewidth: 1.5
|
|
39
|
+
errorbar.capsize: 5
|
|
40
|
+
|
|
41
|
+
legend.fontsize: 22
|
|
42
|
+
legend.title_fontsize: 22
|
|
43
|
+
legend.frameon: False
|
|
44
|
+
legend.handlelength: 2.0
|
|
45
|
+
legend.labelspacing: 0.5
|
|
46
|
+
|
|
47
|
+
figure.figsize: 16, 12
|
|
48
|
+
figure.dpi: 100
|
|
49
|
+
figure.constrained_layout.use: True
|
|
50
|
+
savefig.dpi: 150
|
|
51
|
+
savefig.bbox: tight
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# presentation.mplstyle — 16:9 slide deck format.
|
|
2
|
+
# Large fonts, thick lines, high contrast for projector viewing.
|
|
3
|
+
|
|
4
|
+
pdf.fonttype: 42
|
|
5
|
+
ps.fonttype: 42
|
|
6
|
+
font.family: sans-serif
|
|
7
|
+
font.sans-serif: Arial, Helvetica, DejaVu Sans
|
|
8
|
+
font.size: 18
|
|
9
|
+
mathtext.fontset: dejavusans
|
|
10
|
+
|
|
11
|
+
axes.linewidth: 1.8
|
|
12
|
+
axes.labelsize: 20
|
|
13
|
+
axes.labelweight: bold
|
|
14
|
+
axes.titlesize: 22
|
|
15
|
+
axes.titleweight: bold
|
|
16
|
+
axes.spines.top: False
|
|
17
|
+
axes.spines.right: False
|
|
18
|
+
axes.grid: False
|
|
19
|
+
axes.axisbelow: True
|
|
20
|
+
axes.prop_cycle: cycler('color', ['E69F00', '56B4E9', '009E73', 'D55E00', '0072B2', 'CC79A7', 'F0E442', '000000'])
|
|
21
|
+
|
|
22
|
+
xtick.direction: in
|
|
23
|
+
xtick.labelsize: 16
|
|
24
|
+
xtick.major.size: 6.0
|
|
25
|
+
xtick.major.width: 1.5
|
|
26
|
+
xtick.minor.visible: False
|
|
27
|
+
xtick.top: False
|
|
28
|
+
|
|
29
|
+
ytick.direction: in
|
|
30
|
+
ytick.labelsize: 16
|
|
31
|
+
ytick.major.size: 6.0
|
|
32
|
+
ytick.major.width: 1.5
|
|
33
|
+
ytick.minor.visible: False
|
|
34
|
+
ytick.right: False
|
|
35
|
+
|
|
36
|
+
lines.linewidth: 3.0
|
|
37
|
+
lines.markersize: 9
|
|
38
|
+
patch.linewidth: 1.0
|
|
39
|
+
errorbar.capsize: 4
|
|
40
|
+
|
|
41
|
+
legend.fontsize: 16
|
|
42
|
+
legend.title_fontsize: 16
|
|
43
|
+
legend.frameon: False
|
|
44
|
+
legend.handlelength: 1.8
|
|
45
|
+
legend.labelspacing: 0.4
|
|
46
|
+
|
|
47
|
+
figure.figsize: 12.8, 7.2
|
|
48
|
+
figure.dpi: 120
|
|
49
|
+
figure.constrained_layout.use: True
|
|
50
|
+
savefig.dpi: 200
|
|
51
|
+
savefig.bbox: tight
|
|
52
|
+
savefig.transparent: True
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: oh-mpl-paper
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Publication-ready Matplotlib styles for scientific papers, posters, and presentations.
|
|
5
|
+
Author: Oscar Hickman
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/OscarHickman/thesis-themes
|
|
8
|
+
Project-URL: Repository, https://github.com/OscarHickman/thesis-themes
|
|
9
|
+
Project-URL: Issues, https://github.com/OscarHickman/thesis-themes/issues
|
|
10
|
+
Requires-Python: >=3.8
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: matplotlib>=3.3.0
|
|
13
|
+
Provides-Extra: test
|
|
14
|
+
Requires-Dist: pytest>=7.0.0; extra == "test"
|
|
15
|
+
|
|
16
|
+
# oh-mpl-paper
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
Publication-ready Matplotlib styles, one function call away. Every academic
|
|
21
|
+
project ends up rewriting the same 40 lines of `rcParams` boilerplate to get
|
|
22
|
+
figures that satisfy a journal's font, size, and line-weight requirements —
|
|
23
|
+
`oh-mpl-paper` bundles that boilerplate as a handful of named, tested
|
|
24
|
+
`.mplstyle` presets and a two-line API.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install git+https://github.com/OscarHickman/thesis-themes.git
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
import oh_mpl_paper as p
|
|
36
|
+
import matplotlib.pyplot as plt
|
|
37
|
+
|
|
38
|
+
p.setconfig("nature") # sets all rcParams for the chosen style
|
|
39
|
+
|
|
40
|
+
plt.plot(x, y)
|
|
41
|
+
plt.savefig("figure.pdf") # sized and styled for the target venue
|
|
42
|
+
|
|
43
|
+
print(p.available()) # ['base', 'ieee', 'k.wang', 'nature', 'poster', 'presentation']
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Calling `setconfig` with an unrecognised name raises a `ValueError` listing
|
|
47
|
+
every style currently bundled with the package.
|
|
48
|
+
|
|
49
|
+
## Available styles
|
|
50
|
+
|
|
51
|
+
| Style | Use case |
|
|
52
|
+
|----------------|---------------------------------------------------------------------------|
|
|
53
|
+
| `base` | Generic scientific-paper defaults (Type 42 fonts, colorblind-safe cycle). |
|
|
54
|
+
| `nature` | Single-column Nature-family figures, 89mm wide, 8pt sans-serif fonts. |
|
|
55
|
+
| `ieee` | IEEE transactions single-column format, 3.5in wide, Times New Roman. |
|
|
56
|
+
| `k.wang` | SaUCE/`sauce_xi` MNRAS paper-figures house style (single-column, serif + Computer Modern math, muted inward ticks). |
|
|
57
|
+
| `presentation` | 16:9 slide decks — large fonts, thick lines, transparent background. |
|
|
58
|
+
| `poster` | A0 conference posters — very large fonts, thick lines for ~2m viewing. |
|
|
59
|
+
|
|
60
|
+
All styles embed Type 42 (TrueType) fonts in PDF/PS output so text stays
|
|
61
|
+
editable in Illustrator or Inkscape, and default to a colorblind-safe
|
|
62
|
+
categorical color cycle.
|
|
63
|
+
|
|
64
|
+
## Development
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install -e ".[test]"
|
|
68
|
+
pytest
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Releasing
|
|
72
|
+
|
|
73
|
+
Pushing a tag of the form `vX.Y.Z` triggers `.github/workflows/publish.yml`,
|
|
74
|
+
which builds the package and publishes it to PyPI via
|
|
75
|
+
[Trusted Publishing](https://docs.pypi.org/trusted-publishers/) (no API
|
|
76
|
+
token stored in this repo). The tag's version must match the `version`
|
|
77
|
+
field in `pyproject.toml` or the workflow fails before publishing.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# bump version in pyproject.toml, commit, then:
|
|
81
|
+
git tag v0.1.1
|
|
82
|
+
git push origin v0.1.1
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
PyPI-side setup (one-time, done outside this repo): add a trusted publisher
|
|
86
|
+
on the `oh-mpl-paper` project pointing at this GitHub repo, workflow file
|
|
87
|
+
`publish.yml`, and environment name `pypi`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
oh_mpl_paper/__init__.py
|
|
4
|
+
oh_mpl_paper.egg-info/PKG-INFO
|
|
5
|
+
oh_mpl_paper.egg-info/SOURCES.txt
|
|
6
|
+
oh_mpl_paper.egg-info/dependency_links.txt
|
|
7
|
+
oh_mpl_paper.egg-info/requires.txt
|
|
8
|
+
oh_mpl_paper.egg-info/top_level.txt
|
|
9
|
+
oh_mpl_paper/styles/base.mplstyle
|
|
10
|
+
oh_mpl_paper/styles/ieee.mplstyle
|
|
11
|
+
oh_mpl_paper/styles/k.wang.mplstyle
|
|
12
|
+
oh_mpl_paper/styles/nature.mplstyle
|
|
13
|
+
oh_mpl_paper/styles/poster.mplstyle
|
|
14
|
+
oh_mpl_paper/styles/presentation.mplstyle
|
|
15
|
+
tests/test_styles.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
oh_mpl_paper
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "oh-mpl-paper"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Publication-ready Matplotlib styles for scientific papers, posters, and presentations."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Oscar Hickman" },
|
|
14
|
+
]
|
|
15
|
+
dependencies = [
|
|
16
|
+
"matplotlib>=3.3.0",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.optional-dependencies]
|
|
20
|
+
test = [
|
|
21
|
+
"pytest>=7.0.0",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
[project.urls]
|
|
25
|
+
Homepage = "https://github.com/OscarHickman/thesis-themes"
|
|
26
|
+
Repository = "https://github.com/OscarHickman/thesis-themes"
|
|
27
|
+
Issues = "https://github.com/OscarHickman/thesis-themes/issues"
|
|
28
|
+
|
|
29
|
+
[tool.setuptools.packages.find]
|
|
30
|
+
include = ["oh_mpl_paper*"]
|
|
31
|
+
|
|
32
|
+
[tool.setuptools.package-data]
|
|
33
|
+
"oh_mpl_paper" = ["styles/*.mplstyle"]
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Tests for oh_mpl_paper style loading and rendering."""
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import matplotlib
|
|
5
|
+
|
|
6
|
+
matplotlib.use("Agg")
|
|
7
|
+
import matplotlib.pyplot as plt
|
|
8
|
+
import numpy as np
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
import oh_mpl_paper as p
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.fixture(autouse=True)
|
|
15
|
+
def _reset_style():
|
|
16
|
+
yield
|
|
17
|
+
plt.style.use("default")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_available_returns_bundled_styles():
|
|
21
|
+
names = p.available()
|
|
22
|
+
assert isinstance(names, list)
|
|
23
|
+
assert names == sorted(names)
|
|
24
|
+
for expected in ("base", "nature", "ieee", "presentation", "poster", "k.wang"):
|
|
25
|
+
assert expected in names
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@pytest.mark.parametrize("style_name", p.available())
|
|
29
|
+
def test_setconfig_loads_every_bundled_style(style_name):
|
|
30
|
+
p.setconfig(style_name)
|
|
31
|
+
fig, ax = plt.subplots()
|
|
32
|
+
ax.plot([0, 1, 2], [0, 1, 0])
|
|
33
|
+
plt.close(fig)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_setconfig_raises_valueerror_for_unknown_style():
|
|
37
|
+
with pytest.raises(ValueError) as exc_info:
|
|
38
|
+
p.setconfig("not-a-real-style")
|
|
39
|
+
message = str(exc_info.value)
|
|
40
|
+
assert "not-a-real-style" in message
|
|
41
|
+
for name in p.available():
|
|
42
|
+
assert name in message
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_sample_grid_pdf_per_style(tmp_path: Path):
|
|
46
|
+
"""Render a line/scatter/bar grid per style for visual inspection."""
|
|
47
|
+
rng = np.random.default_rng(0)
|
|
48
|
+
x = np.linspace(0, 10, 50)
|
|
49
|
+
y = np.sin(x)
|
|
50
|
+
scatter_y = y + rng.normal(scale=0.1, size=x.shape)
|
|
51
|
+
categories = ["A", "B", "C", "D"]
|
|
52
|
+
values = [3, 7, 4, 6]
|
|
53
|
+
|
|
54
|
+
for style_name in p.available():
|
|
55
|
+
p.setconfig(style_name)
|
|
56
|
+
fig, axes = plt.subplots(1, 3)
|
|
57
|
+
axes[0].plot(x, y, label="sin(x)")
|
|
58
|
+
axes[0].legend()
|
|
59
|
+
axes[0].set_title("Line")
|
|
60
|
+
|
|
61
|
+
axes[1].scatter(x, scatter_y, s=10)
|
|
62
|
+
axes[1].set_title("Scatter")
|
|
63
|
+
|
|
64
|
+
axes[2].bar(categories, values)
|
|
65
|
+
axes[2].set_title("Bar")
|
|
66
|
+
|
|
67
|
+
out_path = tmp_path / f"sample_{style_name}.pdf"
|
|
68
|
+
fig.savefig(out_path)
|
|
69
|
+
plt.close(fig)
|
|
70
|
+
|
|
71
|
+
assert out_path.is_file()
|
|
72
|
+
assert out_path.stat().st_size > 0
|