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.
@@ -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
+ ![Python](https://img.shields.io/badge/python-3.8%2B-blue)
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
+ ![Python](https://img.shields.io/badge/python-3.8%2B-blue)
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
+ ![Python](https://img.shields.io/badge/python-3.8%2B-blue)
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,4 @@
1
+ matplotlib>=3.3.0
2
+
3
+ [test]
4
+ pytest>=7.0.0
@@ -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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -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