acadplot 0.1.0__py3-none-any.whl
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.
- acadplot/__init__.py +70 -0
- acadplot/bar.py +428 -0
- acadplot/charts.py +376 -0
- acadplot/draw.py +126 -0
- acadplot/line.py +184 -0
- acadplot/plot.py +70 -0
- acadplot/styles.py +911 -0
- acadplot/utils.py +440 -0
- acadplot-0.1.0.dist-info/METADATA +840 -0
- acadplot-0.1.0.dist-info/RECORD +12 -0
- acadplot-0.1.0.dist-info/WHEEL +4 -0
- acadplot-0.1.0.dist-info/licenses/LICENSE +21 -0
acadplot/styles.py
ADDED
|
@@ -0,0 +1,911 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from copy import deepcopy
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
import shutil
|
|
7
|
+
import subprocess
|
|
8
|
+
from typing import Iterator, Literal
|
|
9
|
+
|
|
10
|
+
import matplotlib.pyplot as plt
|
|
11
|
+
from matplotlib.colors import is_color_like, to_hex
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
GridPreset = Literal["major-y", "major", "major-minor", "none"]
|
|
15
|
+
LatexMode = bool | Literal["auto"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class LayoutProfile:
|
|
20
|
+
fig_size: tuple[float, float]
|
|
21
|
+
font_size: float
|
|
22
|
+
line_width: float
|
|
23
|
+
marker_scale: float
|
|
24
|
+
marker_edge_width: float
|
|
25
|
+
axes_linewidth: float
|
|
26
|
+
major_tick_width: float
|
|
27
|
+
minor_tick_width: float
|
|
28
|
+
grid_linewidth: float
|
|
29
|
+
minor_grid_linewidth: float
|
|
30
|
+
grid_alpha: float
|
|
31
|
+
minor_grid_alpha: float
|
|
32
|
+
bar_alpha: float
|
|
33
|
+
bar_edge_width: float
|
|
34
|
+
legend_frameon: bool
|
|
35
|
+
legend_framealpha: float
|
|
36
|
+
legend_frame_linewidth: float
|
|
37
|
+
line_grid: GridPreset
|
|
38
|
+
bar_grid: GridPreset
|
|
39
|
+
label_size: float | None = None
|
|
40
|
+
tick_size: float | None = None
|
|
41
|
+
legend_size: float | None = None
|
|
42
|
+
title_size: float | None = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True)
|
|
46
|
+
class Theme:
|
|
47
|
+
colors: dict[str, str]
|
|
48
|
+
palette: tuple[str, ...]
|
|
49
|
+
grid_color: str
|
|
50
|
+
axis_color: str
|
|
51
|
+
axis_label_color: str
|
|
52
|
+
tick_color: str
|
|
53
|
+
legend_text_color: str
|
|
54
|
+
legend_edge_color: str
|
|
55
|
+
legend_face_color: str
|
|
56
|
+
text_color: str
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass(frozen=True)
|
|
60
|
+
class FontPreset:
|
|
61
|
+
family: str
|
|
62
|
+
serif: tuple[str, ...]
|
|
63
|
+
sans_serif: tuple[str, ...]
|
|
64
|
+
monospace: tuple[str, ...]
|
|
65
|
+
latex_preamble: str
|
|
66
|
+
latex_packages: tuple[str, ...]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
BASE_COLORS = {
|
|
70
|
+
# Muted, high-contrast academic colors. Yellow is explicit-only, not cycled.
|
|
71
|
+
"blue": "#2F3A8F",
|
|
72
|
+
"orange": "#A95F32",
|
|
73
|
+
"green": "#28745A",
|
|
74
|
+
"purple": "#7B3F76",
|
|
75
|
+
"brown": "#937860",
|
|
76
|
+
"yellow": "#B8A642",
|
|
77
|
+
"sky_blue": "#4F8EA8",
|
|
78
|
+
"gray": "#6B6B6B",
|
|
79
|
+
"red": "#B04A5A",
|
|
80
|
+
"pink": "#B56A8A",
|
|
81
|
+
"teal": "#3F7F7D",
|
|
82
|
+
"olive": "#536A3B",
|
|
83
|
+
"navy": "#25356F",
|
|
84
|
+
"maroon": "#8A3B57",
|
|
85
|
+
"lime": "#4F8A5B",
|
|
86
|
+
"cyan": "#4F8EA8",
|
|
87
|
+
"magenta": "#8A4D8F",
|
|
88
|
+
"dark_gray": "#404040",
|
|
89
|
+
"light_gray": "#D3D3D3",
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
LAYOUTS = {
|
|
94
|
+
"paper-1col": LayoutProfile(
|
|
95
|
+
fig_size=(5.5, 3.2),
|
|
96
|
+
font_size=7.5,
|
|
97
|
+
line_width=0.8,
|
|
98
|
+
marker_scale=1.0,
|
|
99
|
+
marker_edge_width=0.5,
|
|
100
|
+
axes_linewidth=0.5,
|
|
101
|
+
major_tick_width=0.48,
|
|
102
|
+
minor_tick_width=0.3,
|
|
103
|
+
grid_linewidth=0.3,
|
|
104
|
+
minor_grid_linewidth=0.2,
|
|
105
|
+
grid_alpha=0.28,
|
|
106
|
+
minor_grid_alpha=0.14,
|
|
107
|
+
bar_alpha=0.86,
|
|
108
|
+
bar_edge_width=0.65,
|
|
109
|
+
legend_frameon=True,
|
|
110
|
+
legend_framealpha=0.6,
|
|
111
|
+
legend_frame_linewidth=0.2,
|
|
112
|
+
line_grid="major",
|
|
113
|
+
bar_grid="major-y",
|
|
114
|
+
),
|
|
115
|
+
"paper-2col": LayoutProfile(
|
|
116
|
+
fig_size=(3.35, 2.15),
|
|
117
|
+
font_size=9.2,
|
|
118
|
+
line_width=1.05,
|
|
119
|
+
marker_scale=1.22,
|
|
120
|
+
marker_edge_width=0.65,
|
|
121
|
+
axes_linewidth=0.65,
|
|
122
|
+
major_tick_width=0.6,
|
|
123
|
+
minor_tick_width=0.4,
|
|
124
|
+
grid_linewidth=0.36,
|
|
125
|
+
minor_grid_linewidth=0.24,
|
|
126
|
+
grid_alpha=0.24,
|
|
127
|
+
minor_grid_alpha=0.11,
|
|
128
|
+
bar_alpha=0.88,
|
|
129
|
+
bar_edge_width=0.75,
|
|
130
|
+
legend_frameon=True,
|
|
131
|
+
legend_framealpha=0.6,
|
|
132
|
+
legend_frame_linewidth=0.2,
|
|
133
|
+
line_grid="major",
|
|
134
|
+
bar_grid="major-y",
|
|
135
|
+
),
|
|
136
|
+
"paper-2col-subplot": LayoutProfile(
|
|
137
|
+
fig_size=(3.35, 1.45),
|
|
138
|
+
font_size=5.9,
|
|
139
|
+
line_width=0.65,
|
|
140
|
+
marker_scale=0.68,
|
|
141
|
+
marker_edge_width=0.36,
|
|
142
|
+
axes_linewidth=0.42,
|
|
143
|
+
major_tick_width=0.38,
|
|
144
|
+
minor_tick_width=0.28,
|
|
145
|
+
grid_linewidth=0.22,
|
|
146
|
+
minor_grid_linewidth=0.16,
|
|
147
|
+
grid_alpha=0.2,
|
|
148
|
+
minor_grid_alpha=0.1,
|
|
149
|
+
bar_alpha=0.86,
|
|
150
|
+
bar_edge_width=0.5,
|
|
151
|
+
legend_frameon=True,
|
|
152
|
+
legend_framealpha=0.6,
|
|
153
|
+
legend_frame_linewidth=0.16,
|
|
154
|
+
line_grid="major-y",
|
|
155
|
+
bar_grid="major-y",
|
|
156
|
+
label_size=5.8,
|
|
157
|
+
tick_size=4.8,
|
|
158
|
+
legend_size=5.0,
|
|
159
|
+
title_size=5.8,
|
|
160
|
+
),
|
|
161
|
+
"paper-2col-span": LayoutProfile(
|
|
162
|
+
fig_size=(6.8, 2.8),
|
|
163
|
+
font_size=8.0,
|
|
164
|
+
line_width=0.9,
|
|
165
|
+
marker_scale=1.05,
|
|
166
|
+
marker_edge_width=0.55,
|
|
167
|
+
axes_linewidth=0.55,
|
|
168
|
+
major_tick_width=0.5,
|
|
169
|
+
minor_tick_width=0.35,
|
|
170
|
+
grid_linewidth=0.35,
|
|
171
|
+
minor_grid_linewidth=0.22,
|
|
172
|
+
grid_alpha=0.26,
|
|
173
|
+
minor_grid_alpha=0.12,
|
|
174
|
+
bar_alpha=0.88,
|
|
175
|
+
bar_edge_width=0.7,
|
|
176
|
+
legend_frameon=True,
|
|
177
|
+
legend_framealpha=0.6,
|
|
178
|
+
legend_frame_linewidth=0.2,
|
|
179
|
+
line_grid="major",
|
|
180
|
+
bar_grid="major-y",
|
|
181
|
+
),
|
|
182
|
+
"presentation": LayoutProfile(
|
|
183
|
+
fig_size=(7.2, 4.2),
|
|
184
|
+
font_size=12.0,
|
|
185
|
+
line_width=1.4,
|
|
186
|
+
marker_scale=1.45,
|
|
187
|
+
marker_edge_width=0.8,
|
|
188
|
+
axes_linewidth=0.8,
|
|
189
|
+
major_tick_width=0.75,
|
|
190
|
+
minor_tick_width=0.45,
|
|
191
|
+
grid_linewidth=0.5,
|
|
192
|
+
minor_grid_linewidth=0.3,
|
|
193
|
+
grid_alpha=0.24,
|
|
194
|
+
minor_grid_alpha=0.12,
|
|
195
|
+
bar_alpha=0.9,
|
|
196
|
+
bar_edge_width=0.9,
|
|
197
|
+
legend_frameon=True,
|
|
198
|
+
legend_framealpha=0.65,
|
|
199
|
+
legend_frame_linewidth=0.25,
|
|
200
|
+
line_grid="major",
|
|
201
|
+
bar_grid="major-y",
|
|
202
|
+
),
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
THEMES = {
|
|
207
|
+
"classic": Theme(
|
|
208
|
+
colors=BASE_COLORS,
|
|
209
|
+
palette=(
|
|
210
|
+
"#2F3A8F",
|
|
211
|
+
"#28745A",
|
|
212
|
+
"#B04A5A",
|
|
213
|
+
"#4F8EA8",
|
|
214
|
+
"#7B3F76",
|
|
215
|
+
"#3F7F7D",
|
|
216
|
+
"#8A3B57",
|
|
217
|
+
"#A95F32",
|
|
218
|
+
"#536A3B",
|
|
219
|
+
"#6B6B6B",
|
|
220
|
+
),
|
|
221
|
+
grid_color="#A9A9A9",
|
|
222
|
+
axis_color="#3A3A3A",
|
|
223
|
+
axis_label_color="#3F3F3F",
|
|
224
|
+
tick_color="#3F3F3F",
|
|
225
|
+
legend_text_color="#3F3F3F",
|
|
226
|
+
legend_edge_color="#DADADA",
|
|
227
|
+
legend_face_color="#FFFFFF",
|
|
228
|
+
text_color="#3F3F3F",
|
|
229
|
+
),
|
|
230
|
+
"nature": Theme(
|
|
231
|
+
colors={
|
|
232
|
+
"blue": "#5A4A8A",
|
|
233
|
+
"orange": "#6FA9AA",
|
|
234
|
+
"green": "#737A73",
|
|
235
|
+
"purple": "#8E6BB0",
|
|
236
|
+
"brown": "#A67C52",
|
|
237
|
+
"yellow": "#DDAA33",
|
|
238
|
+
"sky_blue": "#8FC8C9",
|
|
239
|
+
"gray": "#7B7B7B",
|
|
240
|
+
"red": "#B35850",
|
|
241
|
+
"pink": "#C9A3D8",
|
|
242
|
+
"teal": "#2F7F86",
|
|
243
|
+
"olive": "#7B8D42",
|
|
244
|
+
"navy": "#4A3A70",
|
|
245
|
+
"maroon": "#7A3B46",
|
|
246
|
+
"lime": "#8DB67B",
|
|
247
|
+
"cyan": "#8FC8C9",
|
|
248
|
+
"magenta": "#9D74B6",
|
|
249
|
+
"dark_gray": "#4E4E4E",
|
|
250
|
+
"light_gray": "#D9D9D9",
|
|
251
|
+
},
|
|
252
|
+
palette=("#8E6BB0", "#2F7F86", "#737A73", "#5A4A8A", "#6FA9AA"),
|
|
253
|
+
grid_color="#A5A5A5",
|
|
254
|
+
axis_color="#3A3A3A",
|
|
255
|
+
axis_label_color="#3F3F3F",
|
|
256
|
+
tick_color="#3F3F3F",
|
|
257
|
+
legend_text_color="#3F3F3F",
|
|
258
|
+
legend_edge_color="#D8D8D8",
|
|
259
|
+
legend_face_color="#FFFFFF",
|
|
260
|
+
text_color="#3F3F3F",
|
|
261
|
+
),
|
|
262
|
+
"colorblind": Theme(
|
|
263
|
+
colors={
|
|
264
|
+
"blue": "#0072B2",
|
|
265
|
+
"orange": "#E69F00",
|
|
266
|
+
"green": "#009E73",
|
|
267
|
+
"purple": "#CC79A7",
|
|
268
|
+
"brown": "#A6761D",
|
|
269
|
+
"yellow": "#F0E442",
|
|
270
|
+
"sky_blue": "#56B4E9",
|
|
271
|
+
"gray": "#8F8F8F",
|
|
272
|
+
"red": "#D55E00",
|
|
273
|
+
"pink": "#CC79A7",
|
|
274
|
+
"teal": "#009E73",
|
|
275
|
+
"olive": "#7F8C3A",
|
|
276
|
+
"navy": "#0072B2",
|
|
277
|
+
"maroon": "#8C3A3A",
|
|
278
|
+
"lime": "#7FBF7B",
|
|
279
|
+
"cyan": "#56B4E9",
|
|
280
|
+
"magenta": "#CC79A7",
|
|
281
|
+
"dark_gray": "#3F3F3F",
|
|
282
|
+
"light_gray": "#D4D4D4",
|
|
283
|
+
},
|
|
284
|
+
palette=(
|
|
285
|
+
"#0072B2",
|
|
286
|
+
"#D55E00",
|
|
287
|
+
"#009E73",
|
|
288
|
+
"#CC79A7",
|
|
289
|
+
"#56B4E9",
|
|
290
|
+
"#8C3A3A",
|
|
291
|
+
"#000000",
|
|
292
|
+
"#E69F00",
|
|
293
|
+
),
|
|
294
|
+
grid_color="#A8A8A8",
|
|
295
|
+
axis_color="#3A3A3A",
|
|
296
|
+
axis_label_color="#3F3F3F",
|
|
297
|
+
tick_color="#3F3F3F",
|
|
298
|
+
legend_text_color="#3F3F3F",
|
|
299
|
+
legend_edge_color="#DADADA",
|
|
300
|
+
legend_face_color="#FFFFFF",
|
|
301
|
+
text_color="#3F3F3F",
|
|
302
|
+
),
|
|
303
|
+
"mono": Theme(
|
|
304
|
+
colors={
|
|
305
|
+
"blue": "#111111",
|
|
306
|
+
"orange": "#333333",
|
|
307
|
+
"green": "#555555",
|
|
308
|
+
"purple": "#777777",
|
|
309
|
+
"brown": "#999999",
|
|
310
|
+
"yellow": "#BBBBBB",
|
|
311
|
+
"sky_blue": "#666666",
|
|
312
|
+
"gray": "#8C8C8C",
|
|
313
|
+
"red": "#222222",
|
|
314
|
+
"pink": "#AAAAAA",
|
|
315
|
+
"teal": "#444444",
|
|
316
|
+
"olive": "#606060",
|
|
317
|
+
"navy": "#181818",
|
|
318
|
+
"maroon": "#383838",
|
|
319
|
+
"lime": "#B0B0B0",
|
|
320
|
+
"cyan": "#707070",
|
|
321
|
+
"magenta": "#909090",
|
|
322
|
+
"dark_gray": "#303030",
|
|
323
|
+
"light_gray": "#D0D0D0",
|
|
324
|
+
},
|
|
325
|
+
palette=("#111111", "#333333", "#555555", "#777777", "#999999"),
|
|
326
|
+
grid_color="#B0B0B0",
|
|
327
|
+
axis_color="#3A3A3A",
|
|
328
|
+
axis_label_color="#3F3F3F",
|
|
329
|
+
tick_color="#3F3F3F",
|
|
330
|
+
legend_text_color="#3F3F3F",
|
|
331
|
+
legend_edge_color="#D4D4D4",
|
|
332
|
+
legend_face_color="#FFFFFF",
|
|
333
|
+
text_color="#3F3F3F",
|
|
334
|
+
),
|
|
335
|
+
"warm": Theme(
|
|
336
|
+
colors={
|
|
337
|
+
"blue": "#3A6EA5",
|
|
338
|
+
"orange": "#C8792A",
|
|
339
|
+
"green": "#6E8B3D",
|
|
340
|
+
"purple": "#875A7B",
|
|
341
|
+
"brown": "#8B5E3C",
|
|
342
|
+
"yellow": "#D6A84F",
|
|
343
|
+
"sky_blue": "#6B9AC4",
|
|
344
|
+
"gray": "#8A837C",
|
|
345
|
+
"red": "#B55239",
|
|
346
|
+
"pink": "#B76E79",
|
|
347
|
+
"teal": "#5F8A8B",
|
|
348
|
+
"olive": "#827B3D",
|
|
349
|
+
"navy": "#304E6E",
|
|
350
|
+
"maroon": "#7B3F36",
|
|
351
|
+
"lime": "#A6B46A",
|
|
352
|
+
"cyan": "#7AA6A6",
|
|
353
|
+
"magenta": "#A05D83",
|
|
354
|
+
"dark_gray": "#4C4742",
|
|
355
|
+
"light_gray": "#D8D0C8",
|
|
356
|
+
},
|
|
357
|
+
palette=("#B55239", "#3A6EA5", "#6E8B3D", "#5F8A8B", "#875A7B"),
|
|
358
|
+
grid_color="#B5ACA3",
|
|
359
|
+
axis_color="#443E39",
|
|
360
|
+
axis_label_color="#4A4540",
|
|
361
|
+
tick_color="#4A4540",
|
|
362
|
+
legend_text_color="#4A4540",
|
|
363
|
+
legend_edge_color="#D9D0C8",
|
|
364
|
+
legend_face_color="#FFFFFF",
|
|
365
|
+
text_color="#4A4540",
|
|
366
|
+
),
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
FONTS = {
|
|
371
|
+
"libertine": FontPreset(
|
|
372
|
+
family="serif",
|
|
373
|
+
serif=("Linux Libertine O", "Libertinus Serif", "Libertine", "DejaVu Serif"),
|
|
374
|
+
sans_serif=("DejaVu Sans",),
|
|
375
|
+
monospace=("DejaVu Sans Mono", "Courier New", "Courier"),
|
|
376
|
+
latex_preamble=r"\usepackage{libertine}\usepackage[libertine]{newtxmath}",
|
|
377
|
+
latex_packages=("libertine.sty", "newtxmath.sty"),
|
|
378
|
+
),
|
|
379
|
+
"inconsolata": FontPreset(
|
|
380
|
+
family="monospace",
|
|
381
|
+
serif=("DejaVu Serif",),
|
|
382
|
+
sans_serif=("DejaVu Sans",),
|
|
383
|
+
monospace=("Inconsolata", "DejaVu Sans Mono", "Courier New", "Courier"),
|
|
384
|
+
latex_preamble=r"\usepackage[varqu]{zi4}",
|
|
385
|
+
latex_packages=("zi4.sty",),
|
|
386
|
+
),
|
|
387
|
+
"serif": FontPreset(
|
|
388
|
+
family="serif",
|
|
389
|
+
serif=("DejaVu Serif", "Times New Roman", "Times"),
|
|
390
|
+
sans_serif=("DejaVu Sans",),
|
|
391
|
+
monospace=("DejaVu Sans Mono", "Courier New", "Courier"),
|
|
392
|
+
latex_preamble="",
|
|
393
|
+
latex_packages=(),
|
|
394
|
+
),
|
|
395
|
+
"sans": FontPreset(
|
|
396
|
+
family="sans-serif",
|
|
397
|
+
serif=("DejaVu Serif",),
|
|
398
|
+
sans_serif=("DejaVu Sans", "Arial", "Helvetica"),
|
|
399
|
+
monospace=("DejaVu Sans Mono", "Courier New", "Courier"),
|
|
400
|
+
latex_preamble=r"\usepackage{sansmath}",
|
|
401
|
+
latex_packages=("sansmath.sty",),
|
|
402
|
+
),
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
_VALID_GRIDS = {"major-y", "major", "major-minor", "none"}
|
|
407
|
+
|
|
408
|
+
TEXT_COLORS = {
|
|
409
|
+
"theme": None,
|
|
410
|
+
"default": None,
|
|
411
|
+
"dark": "#202020",
|
|
412
|
+
"black": "#111111",
|
|
413
|
+
"charcoal": "#2B2B2B",
|
|
414
|
+
"slate": "#344054",
|
|
415
|
+
"gray": "#555555",
|
|
416
|
+
"dimgray": "#696969",
|
|
417
|
+
"muted": "#4A4A4A",
|
|
418
|
+
"soft": "#3F3F3F",
|
|
419
|
+
"light": "#F5F5F5",
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
def _build_style(
|
|
424
|
+
layout: str,
|
|
425
|
+
theme: str,
|
|
426
|
+
font: str,
|
|
427
|
+
latex: LatexMode,
|
|
428
|
+
font_size: float | None = None,
|
|
429
|
+
label_size: float | None = None,
|
|
430
|
+
tick_size: float | None = None,
|
|
431
|
+
legend_size: float | None = None,
|
|
432
|
+
title_size: float | None = None,
|
|
433
|
+
scale: float = 1.0,
|
|
434
|
+
text_color: str | None = None,
|
|
435
|
+
) -> dict[str, object]:
|
|
436
|
+
if layout not in LAYOUTS:
|
|
437
|
+
raise ValueError(
|
|
438
|
+
f"Unknown layout {layout!r}. Choose from: {', '.join(available_layouts())}."
|
|
439
|
+
)
|
|
440
|
+
if theme not in THEMES:
|
|
441
|
+
raise ValueError(
|
|
442
|
+
f"Unknown theme {theme!r}. Choose from: {', '.join(available_themes())}."
|
|
443
|
+
)
|
|
444
|
+
if font not in FONTS:
|
|
445
|
+
raise ValueError(f"Unknown font {font!r}. Choose from: {', '.join(FONTS)}.")
|
|
446
|
+
|
|
447
|
+
layout_profile = LAYOUTS[layout]
|
|
448
|
+
theme_profile = THEMES[theme]
|
|
449
|
+
font_profile = FONTS[font]
|
|
450
|
+
scale = float(scale)
|
|
451
|
+
if scale <= 0:
|
|
452
|
+
raise ValueError("scale must be a positive number.")
|
|
453
|
+
resolved_font_size = (
|
|
454
|
+
layout_profile.font_size if font_size is None else float(font_size)
|
|
455
|
+
) * scale
|
|
456
|
+
if resolved_font_size <= 0:
|
|
457
|
+
raise ValueError("font_size must be a positive number.")
|
|
458
|
+
default_label_size = _scaled_size(
|
|
459
|
+
layout_profile.label_size, resolved_font_size, scale
|
|
460
|
+
)
|
|
461
|
+
default_tick_size = _scaled_size(
|
|
462
|
+
layout_profile.tick_size, resolved_font_size, scale
|
|
463
|
+
)
|
|
464
|
+
default_legend_size = _scaled_size(
|
|
465
|
+
layout_profile.legend_size, resolved_font_size, scale
|
|
466
|
+
)
|
|
467
|
+
default_title_size = _scaled_size(
|
|
468
|
+
layout_profile.title_size, resolved_font_size, scale
|
|
469
|
+
)
|
|
470
|
+
resolved_label_size = _resolve_size("label_size", label_size, default_label_size)
|
|
471
|
+
resolved_tick_size = _resolve_size("tick_size", tick_size, default_tick_size)
|
|
472
|
+
resolved_legend_size = _resolve_size(
|
|
473
|
+
"legend_size", legend_size, default_legend_size
|
|
474
|
+
)
|
|
475
|
+
resolved_title_size = _resolve_size("title_size", title_size, default_title_size)
|
|
476
|
+
resolved_text_color = _resolve_color(
|
|
477
|
+
"text_color", text_color, theme_profile.text_color
|
|
478
|
+
)
|
|
479
|
+
if text_color is None:
|
|
480
|
+
resolved_axis_label_color = theme_profile.axis_label_color
|
|
481
|
+
resolved_tick_color = theme_profile.tick_color
|
|
482
|
+
resolved_legend_text_color = theme_profile.legend_text_color
|
|
483
|
+
resolved_title_color = theme_profile.text_color
|
|
484
|
+
else:
|
|
485
|
+
resolved_axis_label_color = resolved_text_color
|
|
486
|
+
resolved_tick_color = resolved_text_color
|
|
487
|
+
resolved_legend_text_color = resolved_text_color
|
|
488
|
+
resolved_title_color = resolved_text_color
|
|
489
|
+
|
|
490
|
+
return {
|
|
491
|
+
"layout": layout,
|
|
492
|
+
"theme": theme,
|
|
493
|
+
"font": font,
|
|
494
|
+
"latex": _resolve_latex(latex, font),
|
|
495
|
+
"latex_requested": latex,
|
|
496
|
+
"scale": scale,
|
|
497
|
+
"fig_size": layout_profile.fig_size,
|
|
498
|
+
"font_size": resolved_font_size,
|
|
499
|
+
"font_size_override": font_size,
|
|
500
|
+
"label_size": resolved_label_size,
|
|
501
|
+
"label_size_override": label_size,
|
|
502
|
+
"tick_size": resolved_tick_size,
|
|
503
|
+
"tick_size_override": tick_size,
|
|
504
|
+
"legend_size": resolved_legend_size,
|
|
505
|
+
"legend_size_override": legend_size,
|
|
506
|
+
"title_size": resolved_title_size,
|
|
507
|
+
"title_size_override": title_size,
|
|
508
|
+
"font_family": font_profile.family,
|
|
509
|
+
"line_width": layout_profile.line_width * scale,
|
|
510
|
+
"marker_scale": layout_profile.marker_scale * scale,
|
|
511
|
+
"marker_edge_width": layout_profile.marker_edge_width * scale,
|
|
512
|
+
"axes_linewidth": layout_profile.axes_linewidth * scale,
|
|
513
|
+
"major_tick_width": layout_profile.major_tick_width * scale,
|
|
514
|
+
"minor_tick_width": layout_profile.minor_tick_width * scale,
|
|
515
|
+
"grid_color": theme_profile.grid_color,
|
|
516
|
+
"grid_linewidth": layout_profile.grid_linewidth * scale,
|
|
517
|
+
"minor_grid_linewidth": layout_profile.minor_grid_linewidth * scale,
|
|
518
|
+
"grid_alpha": layout_profile.grid_alpha,
|
|
519
|
+
"minor_grid_alpha": layout_profile.minor_grid_alpha,
|
|
520
|
+
"bar_alpha": layout_profile.bar_alpha,
|
|
521
|
+
"bar_edge_width": layout_profile.bar_edge_width * scale,
|
|
522
|
+
"bar_edge_color": "#000000",
|
|
523
|
+
"legend_frameon": layout_profile.legend_frameon,
|
|
524
|
+
"legend_framealpha": layout_profile.legend_framealpha,
|
|
525
|
+
"legend_frame_linewidth": layout_profile.legend_frame_linewidth * scale,
|
|
526
|
+
"line_grid": layout_profile.line_grid,
|
|
527
|
+
"bar_grid": layout_profile.bar_grid,
|
|
528
|
+
"colors": deepcopy(theme_profile.colors),
|
|
529
|
+
"palette": theme_profile.palette,
|
|
530
|
+
"axis_color": theme_profile.axis_color,
|
|
531
|
+
"axis_label_color": resolved_axis_label_color,
|
|
532
|
+
"tick_color": resolved_tick_color,
|
|
533
|
+
"legend_text_color": resolved_legend_text_color,
|
|
534
|
+
"legend_edge_color": theme_profile.legend_edge_color,
|
|
535
|
+
"legend_face_color": theme_profile.legend_face_color,
|
|
536
|
+
"text_color": resolved_text_color,
|
|
537
|
+
"text_color_override": text_color,
|
|
538
|
+
"title_color": resolved_title_color,
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def _resolve_size(name: str, size: float | None, default: float) -> float:
|
|
543
|
+
if size is None:
|
|
544
|
+
return default
|
|
545
|
+
resolved = float(size)
|
|
546
|
+
if resolved <= 0:
|
|
547
|
+
raise ValueError(f"{name} must be a positive number.")
|
|
548
|
+
return resolved
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
def _scaled_size(size: float | None, fallback: float, scale: float) -> float:
|
|
552
|
+
if size is None:
|
|
553
|
+
return fallback
|
|
554
|
+
return float(size) * scale
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
def _resolve_color(name: str, color, default: str):
|
|
558
|
+
if isinstance(color, str) and color in TEXT_COLORS:
|
|
559
|
+
preset = TEXT_COLORS[color]
|
|
560
|
+
resolved = default if preset is None else preset
|
|
561
|
+
else:
|
|
562
|
+
resolved = default if color is None else color
|
|
563
|
+
if not is_color_like(resolved):
|
|
564
|
+
raise ValueError(f"{name} must be a valid Matplotlib color.")
|
|
565
|
+
return to_hex(resolved)
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
def _resolve_latex(latex: LatexMode, font: str) -> bool:
|
|
569
|
+
if latex == "auto":
|
|
570
|
+
return _latex_available(font)
|
|
571
|
+
if isinstance(latex, bool):
|
|
572
|
+
return latex
|
|
573
|
+
raise ValueError("latex must be True, False, or 'auto'.")
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
def _latex_available(font: str) -> bool:
|
|
577
|
+
if shutil.which("latex") is None:
|
|
578
|
+
return False
|
|
579
|
+
packages = FONTS[font].latex_packages
|
|
580
|
+
if not packages:
|
|
581
|
+
return True
|
|
582
|
+
if shutil.which("kpsewhich") is None:
|
|
583
|
+
return False
|
|
584
|
+
for package in packages:
|
|
585
|
+
result = subprocess.run(
|
|
586
|
+
["kpsewhich", package],
|
|
587
|
+
stdout=subprocess.DEVNULL,
|
|
588
|
+
stderr=subprocess.DEVNULL,
|
|
589
|
+
timeout=2,
|
|
590
|
+
check=False,
|
|
591
|
+
)
|
|
592
|
+
if result.returncode != 0:
|
|
593
|
+
return False
|
|
594
|
+
return True
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
_CURRENT_STYLE = _build_style("paper-1col", "classic", "inconsolata", True)
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
def available_layouts() -> tuple[str, ...]:
|
|
601
|
+
"""Return the names of available publication layout profiles."""
|
|
602
|
+
return tuple(LAYOUTS)
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
def available_themes() -> tuple[str, ...]:
|
|
606
|
+
"""Return the names of available professional visual themes."""
|
|
607
|
+
return tuple(THEMES)
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def available_fonts() -> tuple[str, ...]:
|
|
611
|
+
"""Return the names of available font presets."""
|
|
612
|
+
return tuple(FONTS)
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
def available_text_colors() -> tuple[str, ...]:
|
|
616
|
+
"""Return built-in text color preset names."""
|
|
617
|
+
return tuple(TEXT_COLORS)
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
def figure_size(layout: str | None = None) -> tuple[float, float]:
|
|
621
|
+
"""Return the figure size for a layout, or for the active style."""
|
|
622
|
+
if layout is None:
|
|
623
|
+
return tuple(get_current_style()["fig_size"])
|
|
624
|
+
if layout not in LAYOUTS:
|
|
625
|
+
raise ValueError(
|
|
626
|
+
f"Unknown layout {layout!r}. Choose from: {', '.join(available_layouts())}."
|
|
627
|
+
)
|
|
628
|
+
return LAYOUTS[layout].fig_size
|
|
629
|
+
|
|
630
|
+
|
|
631
|
+
def get_current_style() -> dict[str, object]:
|
|
632
|
+
"""Return a copy of the active AcadPlot style settings."""
|
|
633
|
+
return deepcopy(_CURRENT_STYLE)
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def _sync_public_colors(style: dict[str, object]) -> None:
|
|
637
|
+
from . import utils
|
|
638
|
+
|
|
639
|
+
utils.colors.clear()
|
|
640
|
+
utils.colors.update(style["colors"])
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def _apply_rcparams(style: dict[str, object]) -> None:
|
|
644
|
+
font = FONTS[str(style["font"])]
|
|
645
|
+
text_color = str(style["text_color"])
|
|
646
|
+
title_color = str(style["title_color"])
|
|
647
|
+
axis_color = str(style["axis_color"])
|
|
648
|
+
axis_label_color = str(style["axis_label_color"])
|
|
649
|
+
tick_color = str(style["tick_color"])
|
|
650
|
+
|
|
651
|
+
plt.rcParams.update(
|
|
652
|
+
{
|
|
653
|
+
"text.usetex": bool(style["latex"]),
|
|
654
|
+
"font.family": font.family,
|
|
655
|
+
"font.serif": list(font.serif),
|
|
656
|
+
"font.sans-serif": list(font.sans_serif),
|
|
657
|
+
"font.monospace": list(font.monospace),
|
|
658
|
+
"font.size": float(style["font_size"]),
|
|
659
|
+
"axes.linewidth": float(style["axes_linewidth"]),
|
|
660
|
+
"axes.labelsize": float(style["label_size"]),
|
|
661
|
+
"axes.titlesize": float(style["title_size"]),
|
|
662
|
+
"axes.labelcolor": axis_label_color,
|
|
663
|
+
"axes.titlecolor": title_color,
|
|
664
|
+
"axes.edgecolor": axis_color,
|
|
665
|
+
"axes.prop_cycle": plt.cycler(color=list(style["palette"])),
|
|
666
|
+
"text.color": text_color,
|
|
667
|
+
"xtick.labelsize": float(style["tick_size"]),
|
|
668
|
+
"ytick.labelsize": float(style["tick_size"]),
|
|
669
|
+
"xtick.color": tick_color,
|
|
670
|
+
"ytick.color": tick_color,
|
|
671
|
+
"xtick.major.width": float(style["major_tick_width"]),
|
|
672
|
+
"ytick.major.width": float(style["major_tick_width"]),
|
|
673
|
+
"xtick.minor.width": float(style["minor_tick_width"]),
|
|
674
|
+
"ytick.minor.width": float(style["minor_tick_width"]),
|
|
675
|
+
"legend.fontsize": float(style["legend_size"]),
|
|
676
|
+
"legend.labelcolor": str(style["legend_text_color"]),
|
|
677
|
+
"legend.facecolor": str(style["legend_face_color"]),
|
|
678
|
+
"legend.edgecolor": str(style["legend_edge_color"]),
|
|
679
|
+
"legend.frameon": bool(style["legend_frameon"]),
|
|
680
|
+
"legend.framealpha": float(style["legend_framealpha"]),
|
|
681
|
+
"figure.figsize": tuple(style["fig_size"]),
|
|
682
|
+
"figure.titlesize": float(style["title_size"]),
|
|
683
|
+
"figure.titleweight": "normal",
|
|
684
|
+
"grid.color": str(style["grid_color"]),
|
|
685
|
+
"grid.linewidth": float(style["grid_linewidth"]),
|
|
686
|
+
"grid.alpha": float(style["grid_alpha"]),
|
|
687
|
+
"pdf.fonttype": 42,
|
|
688
|
+
"ps.fonttype": 42,
|
|
689
|
+
"svg.fonttype": "none",
|
|
690
|
+
"savefig.dpi": 300,
|
|
691
|
+
}
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
if bool(style["latex"]):
|
|
695
|
+
plt.rcParams["text.latex.preamble"] = font.latex_preamble
|
|
696
|
+
else:
|
|
697
|
+
plt.rcParams["text.latex.preamble"] = ""
|
|
698
|
+
|
|
699
|
+
|
|
700
|
+
def configure_plot_style(
|
|
701
|
+
layout: str = "paper-1col",
|
|
702
|
+
theme: str = "classic",
|
|
703
|
+
font: str = "inconsolata",
|
|
704
|
+
latex: LatexMode = True,
|
|
705
|
+
font_size: float | None = None,
|
|
706
|
+
label_size: float | None = None,
|
|
707
|
+
tick_size: float | None = None,
|
|
708
|
+
legend_size: float | None = None,
|
|
709
|
+
title_size: float | None = None,
|
|
710
|
+
scale: float = 1.0,
|
|
711
|
+
text_color: str | None = None,
|
|
712
|
+
) -> dict[str, object]:
|
|
713
|
+
"""
|
|
714
|
+
Configure global AcadPlot style settings.
|
|
715
|
+
|
|
716
|
+
Declare this once near the top of a script to set publication layout,
|
|
717
|
+
theme colours, font family, and Matplotlib rendering defaults.
|
|
718
|
+
"""
|
|
719
|
+
global _CURRENT_STYLE
|
|
720
|
+
|
|
721
|
+
_CURRENT_STYLE = _build_style(
|
|
722
|
+
layout,
|
|
723
|
+
theme,
|
|
724
|
+
font,
|
|
725
|
+
latex,
|
|
726
|
+
font_size=font_size,
|
|
727
|
+
label_size=label_size,
|
|
728
|
+
tick_size=tick_size,
|
|
729
|
+
legend_size=legend_size,
|
|
730
|
+
title_size=title_size,
|
|
731
|
+
scale=scale,
|
|
732
|
+
text_color=text_color,
|
|
733
|
+
)
|
|
734
|
+
_apply_rcparams(_CURRENT_STYLE)
|
|
735
|
+
_sync_public_colors(_CURRENT_STYLE)
|
|
736
|
+
return get_current_style()
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
@contextmanager
|
|
740
|
+
def use_style(
|
|
741
|
+
layout: str | None = None,
|
|
742
|
+
theme: str | None = None,
|
|
743
|
+
font: str | None = None,
|
|
744
|
+
latex: LatexMode | None = None,
|
|
745
|
+
font_size: float | None = None,
|
|
746
|
+
label_size: float | None = None,
|
|
747
|
+
tick_size: float | None = None,
|
|
748
|
+
legend_size: float | None = None,
|
|
749
|
+
title_size: float | None = None,
|
|
750
|
+
scale: float | None = None,
|
|
751
|
+
text_color: str | None = None,
|
|
752
|
+
) -> Iterator[dict[str, object]]:
|
|
753
|
+
"""Temporarily apply an AcadPlot style inside a ``with`` block."""
|
|
754
|
+
global _CURRENT_STYLE
|
|
755
|
+
|
|
756
|
+
previous_style = get_current_style()
|
|
757
|
+
previous_rcparams = plt.rcParams.copy()
|
|
758
|
+
resolved_font_size = (
|
|
759
|
+
previous_style["font_size_override"] if font_size is None else font_size
|
|
760
|
+
)
|
|
761
|
+
resolved_label_size = (
|
|
762
|
+
previous_style["label_size_override"] if label_size is None else label_size
|
|
763
|
+
)
|
|
764
|
+
resolved_tick_size = (
|
|
765
|
+
previous_style["tick_size_override"] if tick_size is None else tick_size
|
|
766
|
+
)
|
|
767
|
+
resolved_legend_size = (
|
|
768
|
+
previous_style["legend_size_override"] if legend_size is None else legend_size
|
|
769
|
+
)
|
|
770
|
+
resolved_title_size = (
|
|
771
|
+
previous_style["title_size_override"] if title_size is None else title_size
|
|
772
|
+
)
|
|
773
|
+
resolved_text_color = (
|
|
774
|
+
previous_style["text_color_override"] if text_color is None else text_color
|
|
775
|
+
)
|
|
776
|
+
resolved_scale = float(previous_style["scale"]) if scale is None else scale
|
|
777
|
+
|
|
778
|
+
try:
|
|
779
|
+
yield configure_plot_style(
|
|
780
|
+
layout=layout or str(previous_style["layout"]),
|
|
781
|
+
theme=theme or str(previous_style["theme"]),
|
|
782
|
+
font=font or str(previous_style["font"]),
|
|
783
|
+
latex=previous_style["latex_requested"] if latex is None else latex,
|
|
784
|
+
font_size=resolved_font_size,
|
|
785
|
+
label_size=resolved_label_size,
|
|
786
|
+
tick_size=resolved_tick_size,
|
|
787
|
+
legend_size=resolved_legend_size,
|
|
788
|
+
title_size=resolved_title_size,
|
|
789
|
+
scale=resolved_scale,
|
|
790
|
+
text_color=resolved_text_color,
|
|
791
|
+
)
|
|
792
|
+
finally:
|
|
793
|
+
_CURRENT_STYLE = previous_style
|
|
794
|
+
plt.rcParams.update(previous_rcparams)
|
|
795
|
+
_sync_public_colors(_CURRENT_STYLE)
|
|
796
|
+
|
|
797
|
+
|
|
798
|
+
def apply_axis_style(ax) -> None:
|
|
799
|
+
"""Apply theme axis, tick, and label colors to an axes."""
|
|
800
|
+
style = get_current_style()
|
|
801
|
+
axis_color = str(style["axis_color"])
|
|
802
|
+
axis_label_color = str(style["axis_label_color"])
|
|
803
|
+
tick_color = str(style["tick_color"])
|
|
804
|
+
title_color = str(style["title_color"])
|
|
805
|
+
|
|
806
|
+
ax.xaxis.label.set_color(axis_label_color)
|
|
807
|
+
ax.yaxis.label.set_color(axis_label_color)
|
|
808
|
+
ax.title.set_color(title_color)
|
|
809
|
+
ax.tick_params(axis="both", colors=tick_color)
|
|
810
|
+
for spine in ax.spines.values():
|
|
811
|
+
spine.set_color(axis_color)
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
def despine(ax=None, sides: tuple[str, ...] = ("top", "right")):
|
|
815
|
+
"""Hide selected axes spines."""
|
|
816
|
+
if ax is None:
|
|
817
|
+
ax = plt.gca()
|
|
818
|
+
for side in sides:
|
|
819
|
+
ax.spines[side].set_visible(False)
|
|
820
|
+
return ax
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
def format_axes(
|
|
824
|
+
ax=None,
|
|
825
|
+
*,
|
|
826
|
+
grid: GridPreset | None = None,
|
|
827
|
+
despine: bool | tuple[str, ...] = False,
|
|
828
|
+
label_size: float | None = None,
|
|
829
|
+
tick_size: float | None = None,
|
|
830
|
+
title_size: float | None = None,
|
|
831
|
+
):
|
|
832
|
+
"""Apply AcadPlot styling to a manually created Matplotlib axes."""
|
|
833
|
+
if ax is None:
|
|
834
|
+
ax = plt.gca()
|
|
835
|
+
|
|
836
|
+
style = get_current_style()
|
|
837
|
+
resolved_label_size = (
|
|
838
|
+
float(style["label_size"]) if label_size is None else float(label_size)
|
|
839
|
+
)
|
|
840
|
+
resolved_tick_size = (
|
|
841
|
+
float(style["tick_size"]) if tick_size is None else float(tick_size)
|
|
842
|
+
)
|
|
843
|
+
resolved_title_size = (
|
|
844
|
+
float(style["title_size"]) if title_size is None else float(title_size)
|
|
845
|
+
)
|
|
846
|
+
|
|
847
|
+
ax.xaxis.label.set_size(resolved_label_size)
|
|
848
|
+
ax.yaxis.label.set_size(resolved_label_size)
|
|
849
|
+
ax.title.set_size(resolved_title_size)
|
|
850
|
+
ax.tick_params(axis="both", labelsize=resolved_tick_size)
|
|
851
|
+
apply_axis_style(ax)
|
|
852
|
+
|
|
853
|
+
if grid is not None:
|
|
854
|
+
apply_grid(ax, grid)
|
|
855
|
+
if despine:
|
|
856
|
+
sides = ("top", "right") if despine is True else tuple(despine)
|
|
857
|
+
globals()["despine"](ax, sides=sides)
|
|
858
|
+
return ax
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
def apply_legend_style(legend) -> None:
|
|
862
|
+
"""Apply theme legend text and frame colors."""
|
|
863
|
+
style = get_current_style()
|
|
864
|
+
for text in legend.get_texts():
|
|
865
|
+
text.set_color(str(style["legend_text_color"]))
|
|
866
|
+
|
|
867
|
+
frame = legend.get_frame()
|
|
868
|
+
frame.set_facecolor(str(style["legend_face_color"]))
|
|
869
|
+
frame.set_edgecolor(str(style["legend_edge_color"]))
|
|
870
|
+
frame.set_alpha(float(style["legend_framealpha"]))
|
|
871
|
+
frame.set_linewidth(float(style["legend_frame_linewidth"]))
|
|
872
|
+
|
|
873
|
+
|
|
874
|
+
def apply_grid(ax, grid: str) -> None:
|
|
875
|
+
"""Apply one of AcadPlot's supported grid presets to an axes."""
|
|
876
|
+
if grid not in _VALID_GRIDS:
|
|
877
|
+
raise ValueError(
|
|
878
|
+
f"Unknown grid {grid!r}. Choose from: {', '.join(_VALID_GRIDS)}."
|
|
879
|
+
)
|
|
880
|
+
|
|
881
|
+
style = get_current_style()
|
|
882
|
+
ax.grid(False, which="both", axis="both")
|
|
883
|
+
ax.set_axisbelow(True)
|
|
884
|
+
|
|
885
|
+
if grid == "none":
|
|
886
|
+
return
|
|
887
|
+
|
|
888
|
+
common = {
|
|
889
|
+
"color": str(style["grid_color"]),
|
|
890
|
+
"linestyle": "-",
|
|
891
|
+
"linewidth": float(style["grid_linewidth"]),
|
|
892
|
+
"alpha": float(style["grid_alpha"]),
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if grid == "major-y":
|
|
896
|
+
ax.grid(True, which="major", axis="y", **common)
|
|
897
|
+
return
|
|
898
|
+
|
|
899
|
+
ax.grid(True, which="major", axis="both", **common)
|
|
900
|
+
|
|
901
|
+
if grid == "major-minor":
|
|
902
|
+
ax.minorticks_on()
|
|
903
|
+
ax.grid(
|
|
904
|
+
True,
|
|
905
|
+
which="minor",
|
|
906
|
+
axis="both",
|
|
907
|
+
color=str(style["grid_color"]),
|
|
908
|
+
linestyle="-",
|
|
909
|
+
linewidth=float(style["minor_grid_linewidth"]),
|
|
910
|
+
alpha=float(style["minor_grid_alpha"]),
|
|
911
|
+
)
|