matplotlib-dark 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.
- matplotlib_dark-0.1.0/LICENSE +21 -0
- matplotlib_dark-0.1.0/PKG-INFO +130 -0
- matplotlib_dark-0.1.0/README.md +97 -0
- matplotlib_dark-0.1.0/pyproject.toml +57 -0
- matplotlib_dark-0.1.0/setup.cfg +4 -0
- matplotlib_dark-0.1.0/src/matplotlib_dark/__init__.py +19 -0
- matplotlib_dark-0.1.0/src/matplotlib_dark/core.py +148 -0
- matplotlib_dark-0.1.0/src/matplotlib_dark/py.typed +1 -0
- matplotlib_dark-0.1.0/src/matplotlib_dark/themes.py +104 -0
- matplotlib_dark-0.1.0/src/matplotlib_dark.egg-info/PKG-INFO +130 -0
- matplotlib_dark-0.1.0/src/matplotlib_dark.egg-info/SOURCES.txt +13 -0
- matplotlib_dark-0.1.0/src/matplotlib_dark.egg-info/dependency_links.txt +1 -0
- matplotlib_dark-0.1.0/src/matplotlib_dark.egg-info/requires.txt +9 -0
- matplotlib_dark-0.1.0/src/matplotlib_dark.egg-info/top_level.txt +1 -0
- matplotlib_dark-0.1.0/tests/test_core.py +97 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Eduardo J. Barrios
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: matplotlib-dark
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Automatic dark themes for Matplotlib, with one-line activation
|
|
5
|
+
Author-email: "Eduardo J. Barrios" <edujbarrios@outlook.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/edujbarrios/matplotlib-dark
|
|
8
|
+
Project-URL: Documentation, https://github.com/edujbarrios/matplotlib-dark#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/edujbarrios/matplotlib-dark
|
|
10
|
+
Project-URL: Issues, https://github.com/edujbarrios/matplotlib-dark/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/edujbarrios/matplotlib-dark/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: matplotlib,dark-mode,visualization,plotting,theme
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: matplotlib>=3.5.0
|
|
26
|
+
Provides-Extra: test
|
|
27
|
+
Requires-Dist: pytest>=7; extra == "test"
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: build>=1; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
31
|
+
Requires-Dist: twine>=5; extra == "dev"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
<div align="center">
|
|
35
|
+
|
|
36
|
+
# matplotlib-dark
|
|
37
|
+
|
|
38
|
+
**Automatic dark mode for Matplotlib — polished charts without designing a theme.**
|
|
39
|
+
|
|
40
|
+
[](https://github.com/edujbarrios/matplotlib-dark/actions/workflows/ci.yml)
|
|
41
|
+
[](https://pypi.org/project/matplotlib-dark/)
|
|
42
|
+
[](https://pypi.org/project/matplotlib-dark/)
|
|
43
|
+
[](LICENSE)
|
|
44
|
+
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
`matplotlib-dark` is the automatic alternative to hand-picking backgrounds,
|
|
48
|
+
grid colors, text contrast, and line palettes for every chart. Activate it in
|
|
49
|
+
one line, then keep using the Matplotlib API you already know.
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
python -m pip install matplotlib-dark
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Python 3.9+ and Matplotlib 3.5+ are supported.
|
|
60
|
+
|
|
61
|
+
## Quick start
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
import matplotlib.pyplot as plt
|
|
65
|
+
import matplotlib_dark as mdk
|
|
66
|
+
|
|
67
|
+
mdk.dark_mode() # That's it: every following chart uses dark mode.
|
|
68
|
+
|
|
69
|
+
plt.plot([1, 2, 3, 4], [1, 4, 2, 3])
|
|
70
|
+
plt.title("Ready for the dark")
|
|
71
|
+
plt.show()
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
No custom CSS, manual color selection, or complex design code is required.
|
|
75
|
+
|
|
76
|
+
## Choose a theme
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
mdk.dark_mode("nord")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Six ready-to-use themes are included: `default`, `nord`, `monokai`,
|
|
83
|
+
`dracula`, `neon`, and `material`.
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
print(mdk.get_available_themes())
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Apply dark mode temporarily
|
|
90
|
+
|
|
91
|
+
Use the context manager when only some charts should be dark. Your previous
|
|
92
|
+
Matplotlib configuration is restored automatically, even if plotting fails.
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
with mdk.dark_theme("dracula"):
|
|
96
|
+
plt.plot([1, 2, 3], [3, 1, 4])
|
|
97
|
+
plt.savefig("dark-chart.png")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
For global mode, restore your original configuration explicitly:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
mdk.dark_mode("material")
|
|
104
|
+
# Create charts...
|
|
105
|
+
mdk.light_mode()
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Why matplotlib-dark?
|
|
109
|
+
|
|
110
|
+
- One-line automatic dark mode
|
|
111
|
+
- Six coordinated color palettes
|
|
112
|
+
- Works with the complete Matplotlib API
|
|
113
|
+
- Global mode or safe, temporary context manager
|
|
114
|
+
- Dark backgrounds are preserved when saving figures
|
|
115
|
+
- Zero dependencies beyond Matplotlib
|
|
116
|
+
|
|
117
|
+
## Development
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
git clone https://github.com/edujbarrios/matplotlib-dark.git
|
|
121
|
+
cd matplotlib-dark
|
|
122
|
+
python -m pip install -e ".[dev]"
|
|
123
|
+
python -m pytest
|
|
124
|
+
python -m build
|
|
125
|
+
python -m twine check dist/*
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
MIT © Eduardo J. Barrios
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# matplotlib-dark
|
|
4
|
+
|
|
5
|
+
**Automatic dark mode for Matplotlib — polished charts without designing a theme.**
|
|
6
|
+
|
|
7
|
+
[](https://github.com/edujbarrios/matplotlib-dark/actions/workflows/ci.yml)
|
|
8
|
+
[](https://pypi.org/project/matplotlib-dark/)
|
|
9
|
+
[](https://pypi.org/project/matplotlib-dark/)
|
|
10
|
+
[](LICENSE)
|
|
11
|
+
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
`matplotlib-dark` is the automatic alternative to hand-picking backgrounds,
|
|
15
|
+
grid colors, text contrast, and line palettes for every chart. Activate it in
|
|
16
|
+
one line, then keep using the Matplotlib API you already know.
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
python -m pip install matplotlib-dark
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Python 3.9+ and Matplotlib 3.5+ are supported.
|
|
27
|
+
|
|
28
|
+
## Quick start
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
import matplotlib.pyplot as plt
|
|
32
|
+
import matplotlib_dark as mdk
|
|
33
|
+
|
|
34
|
+
mdk.dark_mode() # That's it: every following chart uses dark mode.
|
|
35
|
+
|
|
36
|
+
plt.plot([1, 2, 3, 4], [1, 4, 2, 3])
|
|
37
|
+
plt.title("Ready for the dark")
|
|
38
|
+
plt.show()
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
No custom CSS, manual color selection, or complex design code is required.
|
|
42
|
+
|
|
43
|
+
## Choose a theme
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
mdk.dark_mode("nord")
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Six ready-to-use themes are included: `default`, `nord`, `monokai`,
|
|
50
|
+
`dracula`, `neon`, and `material`.
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
print(mdk.get_available_themes())
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Apply dark mode temporarily
|
|
57
|
+
|
|
58
|
+
Use the context manager when only some charts should be dark. Your previous
|
|
59
|
+
Matplotlib configuration is restored automatically, even if plotting fails.
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
with mdk.dark_theme("dracula"):
|
|
63
|
+
plt.plot([1, 2, 3], [3, 1, 4])
|
|
64
|
+
plt.savefig("dark-chart.png")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
For global mode, restore your original configuration explicitly:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
mdk.dark_mode("material")
|
|
71
|
+
# Create charts...
|
|
72
|
+
mdk.light_mode()
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Why matplotlib-dark?
|
|
76
|
+
|
|
77
|
+
- One-line automatic dark mode
|
|
78
|
+
- Six coordinated color palettes
|
|
79
|
+
- Works with the complete Matplotlib API
|
|
80
|
+
- Global mode or safe, temporary context manager
|
|
81
|
+
- Dark backgrounds are preserved when saving figures
|
|
82
|
+
- Zero dependencies beyond Matplotlib
|
|
83
|
+
|
|
84
|
+
## Development
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
git clone https://github.com/edujbarrios/matplotlib-dark.git
|
|
88
|
+
cd matplotlib-dark
|
|
89
|
+
python -m pip install -e ".[dev]"
|
|
90
|
+
python -m pytest
|
|
91
|
+
python -m build
|
|
92
|
+
python -m twine check dist/*
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
MIT © Eduardo J. Barrios
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "matplotlib-dark"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Automatic dark themes for Matplotlib, with one-line activation"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = [
|
|
11
|
+
{name = "Eduardo J. Barrios", email = "edujbarrios@outlook.com"}
|
|
12
|
+
]
|
|
13
|
+
license = "MIT"
|
|
14
|
+
license-files = ["LICENSE"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Intended Audience :: Science/Research",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.9",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Scientific/Engineering :: Visualization",
|
|
25
|
+
]
|
|
26
|
+
keywords = ["matplotlib", "dark-mode", "visualization", "plotting", "theme"]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"matplotlib>=3.5.0",
|
|
29
|
+
]
|
|
30
|
+
requires-python = ">=3.9"
|
|
31
|
+
|
|
32
|
+
[project.optional-dependencies]
|
|
33
|
+
test = [
|
|
34
|
+
"pytest>=7",
|
|
35
|
+
]
|
|
36
|
+
dev = [
|
|
37
|
+
"build>=1",
|
|
38
|
+
"pytest>=7",
|
|
39
|
+
"twine>=5",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[project.urls]
|
|
43
|
+
Homepage = "https://github.com/edujbarrios/matplotlib-dark"
|
|
44
|
+
Documentation = "https://github.com/edujbarrios/matplotlib-dark#readme"
|
|
45
|
+
Repository = "https://github.com/edujbarrios/matplotlib-dark"
|
|
46
|
+
Issues = "https://github.com/edujbarrios/matplotlib-dark/issues"
|
|
47
|
+
Changelog = "https://github.com/edujbarrios/matplotlib-dark/blob/main/CHANGELOG.md"
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.packages.find]
|
|
50
|
+
where = ["src"]
|
|
51
|
+
|
|
52
|
+
[tool.setuptools.package-data]
|
|
53
|
+
matplotlib_dark = ["py.typed"]
|
|
54
|
+
|
|
55
|
+
[tool.pytest.ini_options]
|
|
56
|
+
addopts = "-ra"
|
|
57
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""
|
|
2
|
+
matplotlib-dark: matplotlib, but in dark mode
|
|
3
|
+
A lightweight package to apply dark themes to matplotlib plots.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
__version__ = "0.1.0"
|
|
7
|
+
__author__ = "Eduardo J. Barrios"
|
|
8
|
+
|
|
9
|
+
from .core import dark_mode, dark_theme, get_available_themes, light_mode, set_theme
|
|
10
|
+
from .themes import THEMES
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"dark_mode",
|
|
14
|
+
"dark_theme",
|
|
15
|
+
"light_mode",
|
|
16
|
+
"set_theme",
|
|
17
|
+
"get_available_themes",
|
|
18
|
+
"THEMES",
|
|
19
|
+
]
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""Core functionality for matplotlib-dark."""
|
|
2
|
+
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from typing import Iterator, List, Optional
|
|
5
|
+
|
|
6
|
+
import matplotlib as mpl
|
|
7
|
+
from matplotlib import cycler
|
|
8
|
+
|
|
9
|
+
from .themes import THEMES
|
|
10
|
+
|
|
11
|
+
# Store original rcParams
|
|
12
|
+
_original_params: Optional[dict] = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def dark_mode(theme: str = "default") -> None:
|
|
16
|
+
"""
|
|
17
|
+
Apply a dark theme to matplotlib plots.
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
----------
|
|
21
|
+
theme : str, optional
|
|
22
|
+
The name of the theme to apply. Available themes:
|
|
23
|
+
'default', 'nord', 'monokai', 'dracula'
|
|
24
|
+
Default is 'default'.
|
|
25
|
+
|
|
26
|
+
Examples
|
|
27
|
+
--------
|
|
28
|
+
>>> import matplotlib_dark as mdk
|
|
29
|
+
>>> mdk.dark_mode()
|
|
30
|
+
>>> mdk.dark_mode(theme='nord')
|
|
31
|
+
"""
|
|
32
|
+
set_theme(theme)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def light_mode() -> None:
|
|
36
|
+
"""
|
|
37
|
+
Restore matplotlib to its default light theme.
|
|
38
|
+
|
|
39
|
+
Examples
|
|
40
|
+
--------
|
|
41
|
+
>>> import matplotlib_dark as mdk
|
|
42
|
+
>>> mdk.dark_mode()
|
|
43
|
+
>>> # ... create plots ...
|
|
44
|
+
>>> mdk.light_mode() # Restore defaults
|
|
45
|
+
"""
|
|
46
|
+
global _original_params
|
|
47
|
+
|
|
48
|
+
if _original_params is None:
|
|
49
|
+
mpl.rcdefaults()
|
|
50
|
+
else:
|
|
51
|
+
mpl.rcParams.update(_original_params)
|
|
52
|
+
_original_params = None
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def set_theme(theme_name: str) -> None:
|
|
56
|
+
"""
|
|
57
|
+
Set a specific dark theme.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
theme_name : str
|
|
62
|
+
The name of the theme to apply.
|
|
63
|
+
|
|
64
|
+
Raises
|
|
65
|
+
------
|
|
66
|
+
ValueError
|
|
67
|
+
If the theme name is not recognized.
|
|
68
|
+
"""
|
|
69
|
+
global _original_params
|
|
70
|
+
|
|
71
|
+
if theme_name not in THEMES:
|
|
72
|
+
available = ', '.join(THEMES.keys())
|
|
73
|
+
raise ValueError(
|
|
74
|
+
f"Unknown theme '{theme_name}'. "
|
|
75
|
+
f"Available themes: {available}"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Save original params on first call
|
|
79
|
+
if _original_params is None:
|
|
80
|
+
_original_params = dict(mpl.rcParams)
|
|
81
|
+
|
|
82
|
+
theme = THEMES[theme_name]
|
|
83
|
+
|
|
84
|
+
# Apply theme colors
|
|
85
|
+
mpl.rcParams.update({
|
|
86
|
+
'figure.facecolor': theme['bg_color'],
|
|
87
|
+
'axes.facecolor': theme['axes_bg'],
|
|
88
|
+
'axes.edgecolor': theme['text_color'],
|
|
89
|
+
'axes.labelcolor': theme['text_color'],
|
|
90
|
+
'axes.grid': True,
|
|
91
|
+
'grid.color': theme['grid_color'],
|
|
92
|
+
'grid.alpha': 0.3,
|
|
93
|
+
'text.color': theme['text_color'],
|
|
94
|
+
'xtick.color': theme['text_color'],
|
|
95
|
+
'ytick.color': theme['text_color'],
|
|
96
|
+
'legend.facecolor': theme['axes_bg'],
|
|
97
|
+
'legend.edgecolor': theme['grid_color'],
|
|
98
|
+
'savefig.facecolor': theme['bg_color'],
|
|
99
|
+
'savefig.edgecolor': theme['bg_color'],
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
# Set color cycle
|
|
103
|
+
colors = theme.get('colors', [
|
|
104
|
+
'#8FBCBB', '#88C0D0', '#81A1C1', '#5E81AC',
|
|
105
|
+
'#BF616A', '#D08770', '#EBCB8B', '#A3BE8C', '#B48EAD'
|
|
106
|
+
])
|
|
107
|
+
mpl.rcParams['axes.prop_cycle'] = cycler(color=colors)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def get_available_themes() -> List[str]:
|
|
111
|
+
"""
|
|
112
|
+
Get a list of available theme names.
|
|
113
|
+
|
|
114
|
+
Returns
|
|
115
|
+
-------
|
|
116
|
+
list
|
|
117
|
+
List of available theme names.
|
|
118
|
+
|
|
119
|
+
Examples
|
|
120
|
+
--------
|
|
121
|
+
>>> import matplotlib_dark as mdk
|
|
122
|
+
>>> themes = mdk.get_available_themes()
|
|
123
|
+
>>> print(themes)
|
|
124
|
+
['default', 'nord', 'monokai', 'dracula']
|
|
125
|
+
"""
|
|
126
|
+
return list(THEMES.keys())
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@contextmanager
|
|
130
|
+
def dark_theme(theme: str = "default") -> Iterator[None]:
|
|
131
|
+
"""Temporarily apply a dark theme and restore the current style afterwards.
|
|
132
|
+
|
|
133
|
+
This is safe to nest and is useful when only one plot should use the theme.
|
|
134
|
+
|
|
135
|
+
Examples
|
|
136
|
+
--------
|
|
137
|
+
>>> import matplotlib_dark as mdk
|
|
138
|
+
>>> with mdk.dark_theme("nord"):
|
|
139
|
+
... pass # Create and save a plot here.
|
|
140
|
+
"""
|
|
141
|
+
global _original_params
|
|
142
|
+
previous_original = _original_params
|
|
143
|
+
with mpl.rc_context():
|
|
144
|
+
set_theme(theme)
|
|
145
|
+
try:
|
|
146
|
+
yield
|
|
147
|
+
finally:
|
|
148
|
+
_original_params = previous_original
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Dark theme definitions for matplotlib-dark"""
|
|
2
|
+
|
|
3
|
+
THEMES = {
|
|
4
|
+
'default': {
|
|
5
|
+
'bg_color': '#1e1e1e',
|
|
6
|
+
'axes_bg': '#2d2d2d',
|
|
7
|
+
'text_color': '#e4e4e4',
|
|
8
|
+
'grid_color': '#505050',
|
|
9
|
+
'colors': [
|
|
10
|
+
'#61AFEF', # Blue
|
|
11
|
+
'#98C379', # Green
|
|
12
|
+
'#E06C75', # Red
|
|
13
|
+
'#C678DD', # Purple
|
|
14
|
+
'#56B6C2', # Cyan
|
|
15
|
+
'#E5C07B', # Yellow
|
|
16
|
+
'#D19A66', # Orange
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
'nord': {
|
|
21
|
+
'bg_color': '#2E3440',
|
|
22
|
+
'axes_bg': '#3B4252',
|
|
23
|
+
'text_color': '#ECEFF4',
|
|
24
|
+
'grid_color': '#4C566A',
|
|
25
|
+
'colors': [
|
|
26
|
+
'#88C0D0', # Frost Blue
|
|
27
|
+
'#A3BE8C', # Aurora Green
|
|
28
|
+
'#EBCB8B', # Aurora Yellow
|
|
29
|
+
'#D08770', # Aurora Orange
|
|
30
|
+
'#BF616A', # Aurora Red
|
|
31
|
+
'#B48EAD', # Aurora Purple
|
|
32
|
+
'#5E81AC', # Frost Dark Blue
|
|
33
|
+
'#8FBCBB', # Frost Teal
|
|
34
|
+
'#81A1C1', # Frost Light Blue
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
'monokai': {
|
|
39
|
+
'bg_color': '#272822',
|
|
40
|
+
'axes_bg': '#3E3D32',
|
|
41
|
+
'text_color': '#F8F8F2',
|
|
42
|
+
'grid_color': '#75715E',
|
|
43
|
+
'colors': [
|
|
44
|
+
'#66D9EF', # Cyan
|
|
45
|
+
'#A6E22E', # Green
|
|
46
|
+
'#F92672', # Pink
|
|
47
|
+
'#FD971F', # Orange
|
|
48
|
+
'#AE81FF', # Purple
|
|
49
|
+
'#E6DB74', # Yellow
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
'dracula': {
|
|
54
|
+
'bg_color': '#282A36',
|
|
55
|
+
'axes_bg': '#44475A',
|
|
56
|
+
'text_color': '#F8F8F2',
|
|
57
|
+
'grid_color': '#6272A4',
|
|
58
|
+
'colors': [
|
|
59
|
+
'#8BE9FD', # Cyan
|
|
60
|
+
'#50FA7B', # Green
|
|
61
|
+
'#FFB86C', # Orange
|
|
62
|
+
'#FF79C6', # Pink
|
|
63
|
+
'#BD93F9', # Purple
|
|
64
|
+
'#FF5555', # Red
|
|
65
|
+
'#F1FA8C', # Yellow
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
'neon': {
|
|
70
|
+
'bg_color': '#0a0a0a',
|
|
71
|
+
'axes_bg': '#1a1a1a',
|
|
72
|
+
'text_color': '#ffffff',
|
|
73
|
+
'grid_color': '#ffffff40',
|
|
74
|
+
'colors': [
|
|
75
|
+
'#00ffff', # Cyan neón
|
|
76
|
+
'#00ff00', # Verde neón
|
|
77
|
+
'#ff00ff', # Magenta neón
|
|
78
|
+
'#ffff00', # Amarillo neón
|
|
79
|
+
'#ff0080', # Rosa neón
|
|
80
|
+
'#00ff80', # Verde-cyan neón
|
|
81
|
+
'#ff00ff', # Púrpura neón
|
|
82
|
+
'#ff8000', # Naranja neón
|
|
83
|
+
'#0080ff', # Azul neón
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
'material': {
|
|
88
|
+
'bg_color': '#212121',
|
|
89
|
+
'axes_bg': '#303030',
|
|
90
|
+
'text_color': '#FFFFFF',
|
|
91
|
+
'grid_color': '#424242',
|
|
92
|
+
'colors': [
|
|
93
|
+
'#2196F3', # Material Blue
|
|
94
|
+
'#4CAF50', # Material Green
|
|
95
|
+
'#FF5722', # Material Deep Orange
|
|
96
|
+
'#9C27B0', # Material Purple
|
|
97
|
+
'#00BCD4', # Material Cyan
|
|
98
|
+
'#FFEB3B', # Material Yellow
|
|
99
|
+
'#E91E63', # Material Pink
|
|
100
|
+
'#FF9800', # Material Orange
|
|
101
|
+
'#009688', # Material Teal
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: matplotlib-dark
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Automatic dark themes for Matplotlib, with one-line activation
|
|
5
|
+
Author-email: "Eduardo J. Barrios" <edujbarrios@outlook.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/edujbarrios/matplotlib-dark
|
|
8
|
+
Project-URL: Documentation, https://github.com/edujbarrios/matplotlib-dark#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/edujbarrios/matplotlib-dark
|
|
10
|
+
Project-URL: Issues, https://github.com/edujbarrios/matplotlib-dark/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/edujbarrios/matplotlib-dark/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: matplotlib,dark-mode,visualization,plotting,theme
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: matplotlib>=3.5.0
|
|
26
|
+
Provides-Extra: test
|
|
27
|
+
Requires-Dist: pytest>=7; extra == "test"
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: build>=1; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
31
|
+
Requires-Dist: twine>=5; extra == "dev"
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
<div align="center">
|
|
35
|
+
|
|
36
|
+
# matplotlib-dark
|
|
37
|
+
|
|
38
|
+
**Automatic dark mode for Matplotlib — polished charts without designing a theme.**
|
|
39
|
+
|
|
40
|
+
[](https://github.com/edujbarrios/matplotlib-dark/actions/workflows/ci.yml)
|
|
41
|
+
[](https://pypi.org/project/matplotlib-dark/)
|
|
42
|
+
[](https://pypi.org/project/matplotlib-dark/)
|
|
43
|
+
[](LICENSE)
|
|
44
|
+
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
`matplotlib-dark` is the automatic alternative to hand-picking backgrounds,
|
|
48
|
+
grid colors, text contrast, and line palettes for every chart. Activate it in
|
|
49
|
+
one line, then keep using the Matplotlib API you already know.
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
## Installation
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
python -m pip install matplotlib-dark
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Python 3.9+ and Matplotlib 3.5+ are supported.
|
|
60
|
+
|
|
61
|
+
## Quick start
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
import matplotlib.pyplot as plt
|
|
65
|
+
import matplotlib_dark as mdk
|
|
66
|
+
|
|
67
|
+
mdk.dark_mode() # That's it: every following chart uses dark mode.
|
|
68
|
+
|
|
69
|
+
plt.plot([1, 2, 3, 4], [1, 4, 2, 3])
|
|
70
|
+
plt.title("Ready for the dark")
|
|
71
|
+
plt.show()
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
No custom CSS, manual color selection, or complex design code is required.
|
|
75
|
+
|
|
76
|
+
## Choose a theme
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
mdk.dark_mode("nord")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Six ready-to-use themes are included: `default`, `nord`, `monokai`,
|
|
83
|
+
`dracula`, `neon`, and `material`.
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
print(mdk.get_available_themes())
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Apply dark mode temporarily
|
|
90
|
+
|
|
91
|
+
Use the context manager when only some charts should be dark. Your previous
|
|
92
|
+
Matplotlib configuration is restored automatically, even if plotting fails.
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
with mdk.dark_theme("dracula"):
|
|
96
|
+
plt.plot([1, 2, 3], [3, 1, 4])
|
|
97
|
+
plt.savefig("dark-chart.png")
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
For global mode, restore your original configuration explicitly:
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
mdk.dark_mode("material")
|
|
104
|
+
# Create charts...
|
|
105
|
+
mdk.light_mode()
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Why matplotlib-dark?
|
|
109
|
+
|
|
110
|
+
- One-line automatic dark mode
|
|
111
|
+
- Six coordinated color palettes
|
|
112
|
+
- Works with the complete Matplotlib API
|
|
113
|
+
- Global mode or safe, temporary context manager
|
|
114
|
+
- Dark backgrounds are preserved when saving figures
|
|
115
|
+
- Zero dependencies beyond Matplotlib
|
|
116
|
+
|
|
117
|
+
## Development
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
git clone https://github.com/edujbarrios/matplotlib-dark.git
|
|
121
|
+
cd matplotlib-dark
|
|
122
|
+
python -m pip install -e ".[dev]"
|
|
123
|
+
python -m pytest
|
|
124
|
+
python -m build
|
|
125
|
+
python -m twine check dist/*
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
MIT © Eduardo J. Barrios
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/matplotlib_dark/__init__.py
|
|
5
|
+
src/matplotlib_dark/core.py
|
|
6
|
+
src/matplotlib_dark/py.typed
|
|
7
|
+
src/matplotlib_dark/themes.py
|
|
8
|
+
src/matplotlib_dark.egg-info/PKG-INFO
|
|
9
|
+
src/matplotlib_dark.egg-info/SOURCES.txt
|
|
10
|
+
src/matplotlib_dark.egg-info/dependency_links.txt
|
|
11
|
+
src/matplotlib_dark.egg-info/requires.txt
|
|
12
|
+
src/matplotlib_dark.egg-info/top_level.txt
|
|
13
|
+
tests/test_core.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
matplotlib_dark
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""Tests for matplotlib-dark core functionality"""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
import matplotlib.pyplot as plt
|
|
5
|
+
import matplotlib_dark as mdk
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.fixture(autouse=True)
|
|
9
|
+
def restore_matplotlib_style():
|
|
10
|
+
"""Keep global Matplotlib state from leaking between tests."""
|
|
11
|
+
original = plt.rcParams.copy()
|
|
12
|
+
yield
|
|
13
|
+
plt.rcParams.update(original)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_dark_mode_default():
|
|
17
|
+
"""Test applying default dark mode"""
|
|
18
|
+
mdk.dark_mode()
|
|
19
|
+
assert plt.rcParams['figure.facecolor'] == '#1e1e1e'
|
|
20
|
+
mdk.light_mode()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_dark_mode_nord():
|
|
24
|
+
"""Test applying nord theme"""
|
|
25
|
+
mdk.dark_mode(theme='nord')
|
|
26
|
+
assert plt.rcParams['figure.facecolor'] == '#2E3440'
|
|
27
|
+
mdk.light_mode()
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_dark_mode_monokai():
|
|
31
|
+
"""Test applying monokai theme"""
|
|
32
|
+
mdk.dark_mode(theme='monokai')
|
|
33
|
+
assert plt.rcParams['figure.facecolor'] == '#272822'
|
|
34
|
+
mdk.light_mode()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_dark_mode_dracula():
|
|
38
|
+
"""Test applying dracula theme"""
|
|
39
|
+
mdk.dark_mode(theme='dracula')
|
|
40
|
+
assert plt.rcParams['figure.facecolor'] == '#282A36'
|
|
41
|
+
mdk.light_mode()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_invalid_theme():
|
|
45
|
+
"""Test that invalid theme raises ValueError"""
|
|
46
|
+
with pytest.raises(ValueError):
|
|
47
|
+
mdk.dark_mode(theme='nonexistent')
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_light_mode_restore():
|
|
51
|
+
"""Test that light mode restores defaults"""
|
|
52
|
+
original_bg = plt.rcParams['figure.facecolor']
|
|
53
|
+
mdk.dark_mode()
|
|
54
|
+
mdk.light_mode()
|
|
55
|
+
assert plt.rcParams['figure.facecolor'] == original_bg
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_get_available_themes():
|
|
59
|
+
"""Test getting available themes"""
|
|
60
|
+
themes = mdk.get_available_themes()
|
|
61
|
+
assert isinstance(themes, list)
|
|
62
|
+
assert 'default' in themes
|
|
63
|
+
assert 'nord' in themes
|
|
64
|
+
assert 'monokai' in themes
|
|
65
|
+
assert 'dracula' in themes
|
|
66
|
+
assert 'neon' in themes
|
|
67
|
+
assert 'material' in themes
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_set_theme():
|
|
71
|
+
"""Test set_theme function"""
|
|
72
|
+
mdk.set_theme('nord')
|
|
73
|
+
assert plt.rcParams['figure.facecolor'] == '#2E3440'
|
|
74
|
+
mdk.light_mode()
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_switching_theme_still_restores_original_style():
|
|
78
|
+
original_bg = plt.rcParams['figure.facecolor']
|
|
79
|
+
mdk.dark_mode('nord')
|
|
80
|
+
mdk.dark_mode('dracula')
|
|
81
|
+
mdk.light_mode()
|
|
82
|
+
assert plt.rcParams['figure.facecolor'] == original_bg
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def test_dark_theme_context_restores_style():
|
|
86
|
+
original_bg = plt.rcParams['figure.facecolor']
|
|
87
|
+
with mdk.dark_theme('material'):
|
|
88
|
+
assert plt.rcParams['figure.facecolor'] == '#212121'
|
|
89
|
+
assert plt.rcParams['figure.facecolor'] == original_bg
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_dark_theme_context_restores_style_after_error():
|
|
93
|
+
original_bg = plt.rcParams['figure.facecolor']
|
|
94
|
+
with pytest.raises(RuntimeError):
|
|
95
|
+
with mdk.dark_theme('neon'):
|
|
96
|
+
raise RuntimeError('plot failed')
|
|
97
|
+
assert plt.rcParams['figure.facecolor'] == original_bg
|