qmlmathplot 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.
- qmlmathplot/__init__.py +84 -0
- qmlmathplot/app.py +79 -0
- qmlmathplot/camera.py +366 -0
- qmlmathplot/curve.py +296 -0
- qmlmathplot/model.py +541 -0
- qmlmathplot/plot.py +213 -0
- qmlmathplot/qml/PlotView.qml +247 -0
- qmlmathplot/qsb.py +96 -0
- qmlmathplot/themes/LICENSE.matplotlib +99 -0
- qmlmathplot/themes/README.md +15 -0
- qmlmathplot/themes/Solarize_Light2.mplstyle +53 -0
- qmlmathplot/themes/bmh.mplstyle +27 -0
- qmlmathplot/themes/classic.mplstyle +491 -0
- qmlmathplot/themes/dark_background.mplstyle +26 -0
- qmlmathplot/themes/fast.mplstyle +11 -0
- qmlmathplot/themes/fivethirtyeight.mplstyle +37 -0
- qmlmathplot/themes/ggplot.mplstyle +39 -0
- qmlmathplot/themes/grayscale.mplstyle +29 -0
- qmlmathplot/themes/petroff10.mplstyle +5 -0
- qmlmathplot/themes/seaborn-v0_8-bright.mplstyle +3 -0
- qmlmathplot/themes/seaborn-v0_8-colorblind.mplstyle +3 -0
- qmlmathplot/themes/seaborn-v0_8-dark-palette.mplstyle +3 -0
- qmlmathplot/themes/seaborn-v0_8-dark.mplstyle +30 -0
- qmlmathplot/themes/seaborn-v0_8-darkgrid.mplstyle +30 -0
- qmlmathplot/themes/seaborn-v0_8-deep.mplstyle +3 -0
- qmlmathplot/themes/seaborn-v0_8-muted.mplstyle +3 -0
- qmlmathplot/themes/seaborn-v0_8-notebook.mplstyle +21 -0
- qmlmathplot/themes/seaborn-v0_8-paper.mplstyle +21 -0
- qmlmathplot/themes/seaborn-v0_8-pastel.mplstyle +3 -0
- qmlmathplot/themes/seaborn-v0_8-poster.mplstyle +21 -0
- qmlmathplot/themes/seaborn-v0_8-talk.mplstyle +21 -0
- qmlmathplot/themes/seaborn-v0_8-ticks.mplstyle +30 -0
- qmlmathplot/themes/seaborn-v0_8-white.mplstyle +30 -0
- qmlmathplot/themes/seaborn-v0_8-whitegrid.mplstyle +30 -0
- qmlmathplot/themes/seaborn-v0_8.mplstyle +57 -0
- qmlmathplot/themes/tableau-colorblind10.mplstyle +3 -0
- qmlmathplot/themes.py +236 -0
- qmlmathplot/view.py +55 -0
- qmlmathplot/widget.py +183 -0
- qmlmathplot-0.1.0.dist-info/METADATA +383 -0
- qmlmathplot-0.1.0.dist-info/RECORD +43 -0
- qmlmathplot-0.1.0.dist-info/WHEEL +4 -0
- qmlmathplot-0.1.0.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# from https://everyhue.me/posts/sane-color-scheme-for-matplotlib/
|
|
2
|
+
|
|
3
|
+
patch.linewidth: 0.5
|
|
4
|
+
patch.facecolor: 348ABD # blue
|
|
5
|
+
patch.edgecolor: EEEEEE
|
|
6
|
+
patch.antialiased: True
|
|
7
|
+
|
|
8
|
+
font.size: 10.0
|
|
9
|
+
|
|
10
|
+
axes.facecolor: E5E5E5
|
|
11
|
+
axes.edgecolor: white
|
|
12
|
+
axes.linewidth: 1
|
|
13
|
+
axes.grid: True
|
|
14
|
+
axes.titlesize: x-large
|
|
15
|
+
axes.labelsize: large
|
|
16
|
+
axes.labelcolor: 555555
|
|
17
|
+
axes.axisbelow: True # grid/ticks are below elements (e.g., lines, text)
|
|
18
|
+
|
|
19
|
+
axes.prop_cycle: cycler('color', ['E24A33', '348ABD', '988ED5', '777777', 'FBC15E', '8EBA42', 'FFB5B8'])
|
|
20
|
+
# E24A33 : red
|
|
21
|
+
# 348ABD : blue
|
|
22
|
+
# 988ED5 : purple
|
|
23
|
+
# 777777 : gray
|
|
24
|
+
# FBC15E : yellow
|
|
25
|
+
# 8EBA42 : green
|
|
26
|
+
# FFB5B8 : pink
|
|
27
|
+
|
|
28
|
+
xtick.color: 555555
|
|
29
|
+
xtick.direction: out
|
|
30
|
+
|
|
31
|
+
ytick.color: 555555
|
|
32
|
+
ytick.direction: out
|
|
33
|
+
|
|
34
|
+
grid.color: white
|
|
35
|
+
grid.linestyle: - # solid line
|
|
36
|
+
|
|
37
|
+
figure.facecolor: white
|
|
38
|
+
figure.edgecolor: 0.50
|
|
39
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Set all colors to grayscale
|
|
2
|
+
# Note: strings of float values are interpreted by matplotlib as gray values.
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
lines.color: black
|
|
6
|
+
patch.facecolor: gray
|
|
7
|
+
patch.edgecolor: black
|
|
8
|
+
|
|
9
|
+
text.color: black
|
|
10
|
+
|
|
11
|
+
axes.facecolor: white
|
|
12
|
+
axes.edgecolor: black
|
|
13
|
+
axes.labelcolor: black
|
|
14
|
+
# black to light gray
|
|
15
|
+
axes.prop_cycle: cycler('color', ['0.00', '0.40', '0.60', '0.70'])
|
|
16
|
+
|
|
17
|
+
xtick.color: black
|
|
18
|
+
ytick.color: black
|
|
19
|
+
|
|
20
|
+
grid.color: black
|
|
21
|
+
|
|
22
|
+
figure.facecolor: 0.75
|
|
23
|
+
figure.edgecolor: white
|
|
24
|
+
|
|
25
|
+
image.cmap: gray
|
|
26
|
+
|
|
27
|
+
savefig.facecolor: white
|
|
28
|
+
savefig.edgecolor: white
|
|
29
|
+
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Color cycle survey palette from Petroff (2021):
|
|
2
|
+
# https://arxiv.org/abs/2107.02270
|
|
3
|
+
# https://github.com/mpetroff/accessible-color-cycles
|
|
4
|
+
axes.prop_cycle: cycler('color', ['3f90da', 'ffa90e', 'bd1f01', '94a4a2', '832db6', 'a96b59', 'e76300', 'b9ac70', '717581', '92dadd'])
|
|
5
|
+
patch.facecolor: 3f90da
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Seaborn common parameters
|
|
2
|
+
# .15 = dark_gray
|
|
3
|
+
# .8 = light_gray
|
|
4
|
+
figure.facecolor: white
|
|
5
|
+
text.color: .15
|
|
6
|
+
axes.labelcolor: .15
|
|
7
|
+
legend.frameon: False
|
|
8
|
+
legend.numpoints: 1
|
|
9
|
+
legend.scatterpoints: 1
|
|
10
|
+
xtick.direction: out
|
|
11
|
+
ytick.direction: out
|
|
12
|
+
xtick.color: .15
|
|
13
|
+
ytick.color: .15
|
|
14
|
+
axes.axisbelow: True
|
|
15
|
+
image.cmap: Greys
|
|
16
|
+
font.family: sans-serif
|
|
17
|
+
font.sans-serif: Arial, Liberation Sans, DejaVu Sans, Bitstream Vera Sans, sans-serif
|
|
18
|
+
grid.linestyle: -
|
|
19
|
+
lines.solid_capstyle: round
|
|
20
|
+
|
|
21
|
+
# Seaborn dark parameters
|
|
22
|
+
axes.grid: False
|
|
23
|
+
axes.facecolor: EAEAF2
|
|
24
|
+
axes.edgecolor: white
|
|
25
|
+
axes.linewidth: 0
|
|
26
|
+
grid.color: white
|
|
27
|
+
xtick.major.size: 0
|
|
28
|
+
ytick.major.size: 0
|
|
29
|
+
xtick.minor.size: 0
|
|
30
|
+
ytick.minor.size: 0
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Seaborn common parameters
|
|
2
|
+
# .15 = dark_gray
|
|
3
|
+
# .8 = light_gray
|
|
4
|
+
figure.facecolor: white
|
|
5
|
+
text.color: .15
|
|
6
|
+
axes.labelcolor: .15
|
|
7
|
+
legend.frameon: False
|
|
8
|
+
legend.numpoints: 1
|
|
9
|
+
legend.scatterpoints: 1
|
|
10
|
+
xtick.direction: out
|
|
11
|
+
ytick.direction: out
|
|
12
|
+
xtick.color: .15
|
|
13
|
+
ytick.color: .15
|
|
14
|
+
axes.axisbelow: True
|
|
15
|
+
image.cmap: Greys
|
|
16
|
+
font.family: sans-serif
|
|
17
|
+
font.sans-serif: Arial, Liberation Sans, DejaVu Sans, Bitstream Vera Sans, sans-serif
|
|
18
|
+
grid.linestyle: -
|
|
19
|
+
lines.solid_capstyle: round
|
|
20
|
+
|
|
21
|
+
# Seaborn darkgrid parameters
|
|
22
|
+
axes.grid: True
|
|
23
|
+
axes.facecolor: EAEAF2
|
|
24
|
+
axes.edgecolor: white
|
|
25
|
+
axes.linewidth: 0
|
|
26
|
+
grid.color: white
|
|
27
|
+
xtick.major.size: 0
|
|
28
|
+
ytick.major.size: 0
|
|
29
|
+
xtick.minor.size: 0
|
|
30
|
+
ytick.minor.size: 0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Seaborn notebook context
|
|
2
|
+
figure.figsize: 8.0, 5.5
|
|
3
|
+
axes.labelsize: 11
|
|
4
|
+
axes.titlesize: 12
|
|
5
|
+
xtick.labelsize: 10
|
|
6
|
+
ytick.labelsize: 10
|
|
7
|
+
legend.fontsize: 10
|
|
8
|
+
|
|
9
|
+
grid.linewidth: 1
|
|
10
|
+
lines.linewidth: 1.75
|
|
11
|
+
patch.linewidth: .3
|
|
12
|
+
lines.markersize: 7
|
|
13
|
+
lines.markeredgewidth: 0
|
|
14
|
+
|
|
15
|
+
xtick.major.width: 1
|
|
16
|
+
ytick.major.width: 1
|
|
17
|
+
xtick.minor.width: .5
|
|
18
|
+
ytick.minor.width: .5
|
|
19
|
+
|
|
20
|
+
xtick.major.pad: 7
|
|
21
|
+
ytick.major.pad: 7
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Seaborn paper context
|
|
2
|
+
figure.figsize: 6.4, 4.4
|
|
3
|
+
axes.labelsize: 8.8
|
|
4
|
+
axes.titlesize: 9.6
|
|
5
|
+
xtick.labelsize: 8
|
|
6
|
+
ytick.labelsize: 8
|
|
7
|
+
legend.fontsize: 8
|
|
8
|
+
|
|
9
|
+
grid.linewidth: 0.8
|
|
10
|
+
lines.linewidth: 1.4
|
|
11
|
+
patch.linewidth: 0.24
|
|
12
|
+
lines.markersize: 5.6
|
|
13
|
+
lines.markeredgewidth: 0
|
|
14
|
+
|
|
15
|
+
xtick.major.width: 0.8
|
|
16
|
+
ytick.major.width: 0.8
|
|
17
|
+
xtick.minor.width: 0.4
|
|
18
|
+
ytick.minor.width: 0.4
|
|
19
|
+
|
|
20
|
+
xtick.major.pad: 5.6
|
|
21
|
+
ytick.major.pad: 5.6
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Seaborn poster context
|
|
2
|
+
figure.figsize: 12.8, 8.8
|
|
3
|
+
axes.labelsize: 17.6
|
|
4
|
+
axes.titlesize: 19.2
|
|
5
|
+
xtick.labelsize: 16
|
|
6
|
+
ytick.labelsize: 16
|
|
7
|
+
legend.fontsize: 16
|
|
8
|
+
|
|
9
|
+
grid.linewidth: 1.6
|
|
10
|
+
lines.linewidth: 2.8
|
|
11
|
+
patch.linewidth: 0.48
|
|
12
|
+
lines.markersize: 11.2
|
|
13
|
+
lines.markeredgewidth: 0
|
|
14
|
+
|
|
15
|
+
xtick.major.width: 1.6
|
|
16
|
+
ytick.major.width: 1.6
|
|
17
|
+
xtick.minor.width: 0.8
|
|
18
|
+
ytick.minor.width: 0.8
|
|
19
|
+
|
|
20
|
+
xtick.major.pad: 11.2
|
|
21
|
+
ytick.major.pad: 11.2
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Seaborn talk context
|
|
2
|
+
figure.figsize: 10.4, 7.15
|
|
3
|
+
axes.labelsize: 14.3
|
|
4
|
+
axes.titlesize: 15.6
|
|
5
|
+
xtick.labelsize: 13
|
|
6
|
+
ytick.labelsize: 13
|
|
7
|
+
legend.fontsize: 13
|
|
8
|
+
|
|
9
|
+
grid.linewidth: 1.3
|
|
10
|
+
lines.linewidth: 2.275
|
|
11
|
+
patch.linewidth: 0.39
|
|
12
|
+
lines.markersize: 9.1
|
|
13
|
+
lines.markeredgewidth: 0
|
|
14
|
+
|
|
15
|
+
xtick.major.width: 1.3
|
|
16
|
+
ytick.major.width: 1.3
|
|
17
|
+
xtick.minor.width: 0.65
|
|
18
|
+
ytick.minor.width: 0.65
|
|
19
|
+
|
|
20
|
+
xtick.major.pad: 9.1
|
|
21
|
+
ytick.major.pad: 9.1
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Seaborn common parameters
|
|
2
|
+
# .15 = dark_gray
|
|
3
|
+
# .8 = light_gray
|
|
4
|
+
figure.facecolor: white
|
|
5
|
+
text.color: .15
|
|
6
|
+
axes.labelcolor: .15
|
|
7
|
+
legend.frameon: False
|
|
8
|
+
legend.numpoints: 1
|
|
9
|
+
legend.scatterpoints: 1
|
|
10
|
+
xtick.direction: out
|
|
11
|
+
ytick.direction: out
|
|
12
|
+
xtick.color: .15
|
|
13
|
+
ytick.color: .15
|
|
14
|
+
axes.axisbelow: True
|
|
15
|
+
image.cmap: Greys
|
|
16
|
+
font.family: sans-serif
|
|
17
|
+
font.sans-serif: Arial, Liberation Sans, DejaVu Sans, Bitstream Vera Sans, sans-serif
|
|
18
|
+
grid.linestyle: -
|
|
19
|
+
lines.solid_capstyle: round
|
|
20
|
+
|
|
21
|
+
# Seaborn white parameters
|
|
22
|
+
axes.grid: False
|
|
23
|
+
axes.facecolor: white
|
|
24
|
+
axes.edgecolor: .15
|
|
25
|
+
axes.linewidth: 1.25
|
|
26
|
+
grid.color: .8
|
|
27
|
+
xtick.major.size: 6
|
|
28
|
+
ytick.major.size: 6
|
|
29
|
+
xtick.minor.size: 3
|
|
30
|
+
ytick.minor.size: 3
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Seaborn common parameters
|
|
2
|
+
# .15 = dark_gray
|
|
3
|
+
# .8 = light_gray
|
|
4
|
+
figure.facecolor: white
|
|
5
|
+
text.color: .15
|
|
6
|
+
axes.labelcolor: .15
|
|
7
|
+
legend.frameon: False
|
|
8
|
+
legend.numpoints: 1
|
|
9
|
+
legend.scatterpoints: 1
|
|
10
|
+
xtick.direction: out
|
|
11
|
+
ytick.direction: out
|
|
12
|
+
xtick.color: .15
|
|
13
|
+
ytick.color: .15
|
|
14
|
+
axes.axisbelow: True
|
|
15
|
+
image.cmap: Greys
|
|
16
|
+
font.family: sans-serif
|
|
17
|
+
font.sans-serif: Arial, Liberation Sans, DejaVu Sans, Bitstream Vera Sans, sans-serif
|
|
18
|
+
grid.linestyle: -
|
|
19
|
+
lines.solid_capstyle: round
|
|
20
|
+
|
|
21
|
+
# Seaborn white parameters
|
|
22
|
+
axes.grid: False
|
|
23
|
+
axes.facecolor: white
|
|
24
|
+
axes.edgecolor: .15
|
|
25
|
+
axes.linewidth: 1.25
|
|
26
|
+
grid.color: .8
|
|
27
|
+
xtick.major.size: 0
|
|
28
|
+
ytick.major.size: 0
|
|
29
|
+
xtick.minor.size: 0
|
|
30
|
+
ytick.minor.size: 0
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Seaborn common parameters
|
|
2
|
+
# .15 = dark_gray
|
|
3
|
+
# .8 = light_gray
|
|
4
|
+
figure.facecolor: white
|
|
5
|
+
text.color: .15
|
|
6
|
+
axes.labelcolor: .15
|
|
7
|
+
legend.frameon: False
|
|
8
|
+
legend.numpoints: 1
|
|
9
|
+
legend.scatterpoints: 1
|
|
10
|
+
xtick.direction: out
|
|
11
|
+
ytick.direction: out
|
|
12
|
+
xtick.color: .15
|
|
13
|
+
ytick.color: .15
|
|
14
|
+
axes.axisbelow: True
|
|
15
|
+
image.cmap: Greys
|
|
16
|
+
font.family: sans-serif
|
|
17
|
+
font.sans-serif: Arial, Liberation Sans, DejaVu Sans, Bitstream Vera Sans, sans-serif
|
|
18
|
+
grid.linestyle: -
|
|
19
|
+
lines.solid_capstyle: round
|
|
20
|
+
|
|
21
|
+
# Seaborn whitegrid parameters
|
|
22
|
+
axes.grid: True
|
|
23
|
+
axes.facecolor: white
|
|
24
|
+
axes.edgecolor: .8
|
|
25
|
+
axes.linewidth: 1
|
|
26
|
+
grid.color: .8
|
|
27
|
+
xtick.major.size: 0
|
|
28
|
+
ytick.major.size: 0
|
|
29
|
+
xtick.minor.size: 0
|
|
30
|
+
ytick.minor.size: 0
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# default seaborn aesthetic
|
|
2
|
+
# darkgrid + deep palette + notebook context
|
|
3
|
+
|
|
4
|
+
axes.axisbelow: True
|
|
5
|
+
axes.edgecolor: white
|
|
6
|
+
axes.facecolor: EAEAF2
|
|
7
|
+
axes.grid: True
|
|
8
|
+
axes.labelcolor: .15
|
|
9
|
+
axes.labelsize: 11
|
|
10
|
+
axes.linewidth: 0
|
|
11
|
+
axes.prop_cycle: cycler('color', ['4C72B0', '55A868', 'C44E52', '8172B2', 'CCB974', '64B5CD'])
|
|
12
|
+
axes.titlesize: 12
|
|
13
|
+
|
|
14
|
+
figure.facecolor: white
|
|
15
|
+
figure.figsize: 8.0, 5.5
|
|
16
|
+
|
|
17
|
+
font.family: sans-serif
|
|
18
|
+
font.sans-serif: Arial, Liberation Sans, DejaVu Sans, Bitstream Vera Sans, sans-serif
|
|
19
|
+
|
|
20
|
+
grid.color: white
|
|
21
|
+
grid.linestyle: -
|
|
22
|
+
grid.linewidth: 1
|
|
23
|
+
|
|
24
|
+
image.cmap: Greys
|
|
25
|
+
|
|
26
|
+
legend.fontsize: 10
|
|
27
|
+
legend.frameon: False
|
|
28
|
+
legend.numpoints: 1
|
|
29
|
+
legend.scatterpoints: 1
|
|
30
|
+
|
|
31
|
+
lines.linewidth: 1.75
|
|
32
|
+
lines.markeredgewidth: 0
|
|
33
|
+
lines.markersize: 7
|
|
34
|
+
lines.solid_capstyle: round
|
|
35
|
+
|
|
36
|
+
patch.facecolor: 4C72B0
|
|
37
|
+
patch.linewidth: .3
|
|
38
|
+
|
|
39
|
+
text.color: .15
|
|
40
|
+
|
|
41
|
+
xtick.color: .15
|
|
42
|
+
xtick.direction: out
|
|
43
|
+
xtick.labelsize: 10
|
|
44
|
+
xtick.major.pad: 7
|
|
45
|
+
xtick.major.size: 0
|
|
46
|
+
xtick.major.width: 1
|
|
47
|
+
xtick.minor.size: 0
|
|
48
|
+
xtick.minor.width: .5
|
|
49
|
+
|
|
50
|
+
ytick.color: .15
|
|
51
|
+
ytick.direction: out
|
|
52
|
+
ytick.labelsize: 10
|
|
53
|
+
ytick.major.pad: 7
|
|
54
|
+
ytick.major.size: 0
|
|
55
|
+
ytick.major.width: 1
|
|
56
|
+
ytick.minor.size: 0
|
|
57
|
+
ytick.minor.width: .5
|
qmlmathplot/themes.py
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""Matplotlib-flavoured themes.
|
|
2
|
+
|
|
3
|
+
A theme is a set of overrides for a :class:`~qmlmathplot.plot.Plot`'s style properties.
|
|
4
|
+
The bundled ones are Matplotlib's own style sheets, vendored verbatim into
|
|
5
|
+
``qmlmathplot/themes/*.mplstyle`` and redistributed under Matplotlib's BSD license (see
|
|
6
|
+
``themes/LICENSE.matplotlib``). They are converted at load time: Matplotlib's ``rcParams``
|
|
7
|
+
keys are mapped onto this library's property names, and keys that describe things this
|
|
8
|
+
library does not have (spines, figure size, dpi, legend, marker styles, …) are ignored.
|
|
9
|
+
|
|
10
|
+
plot.theme = "ggplot"
|
|
11
|
+
plot.theme = "dark_background"
|
|
12
|
+
plot.theme = "seaborn-v0_8-darkgrid"
|
|
13
|
+
plot.theme = "my-style" # themes/my-style.mplstyle
|
|
14
|
+
plot.theme = "path/to/any.mplstyle" # any file with the same format
|
|
15
|
+
|
|
16
|
+
The file format is Matplotlib's: ``key: value`` lines, ``#`` starts a comment. Only the
|
|
17
|
+
keys listed in ``_KEY_MAP`` are converted; the rest are dropped silently, so a style sheet
|
|
18
|
+
can be dropped in without editing.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import re
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
__all__ = ["names", "raw", "resolve"]
|
|
27
|
+
|
|
28
|
+
THEME_DIR = Path(__file__).with_name("themes")
|
|
29
|
+
|
|
30
|
+
#: Matplotlib sizes are in points; this library's properties are logical pixels.
|
|
31
|
+
PT_TO_PX = 4.0 / 3.0
|
|
32
|
+
|
|
33
|
+
#: Matplotlib's named font sizes, in points.
|
|
34
|
+
_NAMED_SIZES = {
|
|
35
|
+
"xx-small": 5.79, "x-small": 6.94, "small": 8.33, "medium": 10.0,
|
|
36
|
+
"large": 12.0, "x-large": 14.4, "xx-large": 17.28,
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
_LINE_STYLES = {
|
|
40
|
+
"solid": "-", "dashed": "--", "dotted": ":", "dashdot": "-.", "none": "", "": "",
|
|
41
|
+
"-": "-", "--": "--", ":": ":", "-.": "-.",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#: matplotlib's single-letter colour names.
|
|
45
|
+
_SHORT_COLORS = {
|
|
46
|
+
"b": "blue", "g": "green", "r": "red", "c": "cyan",
|
|
47
|
+
"m": "magenta", "y": "yellow", "k": "black", "w": "white",
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#: Font weights that mean "bold" (matplotlib's ``axes.titleweight``).
|
|
51
|
+
_BOLD_WEIGHTS = frozenset({"bold", "heavy", "semibold", "black"})
|
|
52
|
+
|
|
53
|
+
#: For every Plot property, the style-sheet keys that can feed it, most specific first.
|
|
54
|
+
#: A key that is absent is skipped; unknown keys are dropped entirely.
|
|
55
|
+
_SOURCES: dict[str, tuple[str, ...]] = {
|
|
56
|
+
"background": ("axes.facecolor", "figure.facecolor"),
|
|
57
|
+
"grid": ("axes.grid",),
|
|
58
|
+
"grid_color": ("grid.color",),
|
|
59
|
+
"grid_width": ("grid.linewidth",),
|
|
60
|
+
"grid_alpha": ("grid.alpha",),
|
|
61
|
+
"grid_style": ("grid.linestyle",),
|
|
62
|
+
"color_cycle": ("axes.prop_cycle",),
|
|
63
|
+
"line_width": ("lines.linewidth",),
|
|
64
|
+
"text_color": ("text.color", "axes.labelcolor"),
|
|
65
|
+
"tick_color": ("xtick.color", "ytick.color"),
|
|
66
|
+
"tick_length": ("xtick.major.size", "ytick.major.size"),
|
|
67
|
+
"font_size": ("font.size",),
|
|
68
|
+
"tick_font_size": ("xtick.labelsize", "ytick.labelsize"),
|
|
69
|
+
"title_color": ("axes.titlecolor",),
|
|
70
|
+
"title_font_size": ("axes.titlesize",),
|
|
71
|
+
"title_bold": ("axes.titleweight",),
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
#: Keys whose value is a boolean rather than text.
|
|
75
|
+
_BOOL_KEYS = frozenset({"axes.grid", "axes.titleweight"})
|
|
76
|
+
#: Keys whose value is a size in points (converted to logical pixels).
|
|
77
|
+
_PT_KEYS = frozenset({
|
|
78
|
+
"grid.linewidth", "lines.linewidth", "xtick.major.size", "ytick.major.size",
|
|
79
|
+
"font.size", "xtick.labelsize", "ytick.labelsize", "axes.titlesize",
|
|
80
|
+
})
|
|
81
|
+
#: Keys whose value is a colour.
|
|
82
|
+
_COLOR_KEYS = frozenset({
|
|
83
|
+
"xtick.color", "ytick.color", "text.color", "axes.labelcolor", "axes.titlecolor",
|
|
84
|
+
"grid.color", "axes.facecolor", "figure.facecolor",
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def names() -> list[str]:
|
|
89
|
+
"""Names of the bundled themes (file stems), plus ``"default"`` (no overrides)."""
|
|
90
|
+
stems = sorted(p.stem for p in THEME_DIR.glob("*.mplstyle")) if THEME_DIR.is_dir() else []
|
|
91
|
+
return ["default", *stems]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def raw(name: str) -> dict[str, str]:
|
|
95
|
+
"""The theme's raw ``rcParams`` (key -> text value), before conversion."""
|
|
96
|
+
if name == "default":
|
|
97
|
+
return {}
|
|
98
|
+
path = Path(name)
|
|
99
|
+
if not path.is_file():
|
|
100
|
+
path = THEME_DIR / f"{name}.mplstyle"
|
|
101
|
+
if not path.is_file():
|
|
102
|
+
raise KeyError(f"unknown theme {name!r}; available: {', '.join(names())}")
|
|
103
|
+
return _parse(path.read_text(encoding="utf-8"))
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def resolve(name: str) -> dict[str, object]:
|
|
107
|
+
"""Convert a theme into ``{plot property: value}`` overrides.
|
|
108
|
+
|
|
109
|
+
Only keys this library understands are returned; everything else in the style sheet
|
|
110
|
+
(spines, figure size, dpi, legend, marker styles, …) is dropped, because there is no
|
|
111
|
+
figure, no axes box and no legend here.
|
|
112
|
+
"""
|
|
113
|
+
values = raw(name)
|
|
114
|
+
cycle = _parse_cycle(values.get("axes.prop_cycle", ""))
|
|
115
|
+
out: dict[str, object] = {}
|
|
116
|
+
for prop, keys in _SOURCES.items():
|
|
117
|
+
for key in keys:
|
|
118
|
+
if key not in values:
|
|
119
|
+
continue
|
|
120
|
+
value = _convert(key, values[key], cycle)
|
|
121
|
+
if value is not None:
|
|
122
|
+
out[prop] = value
|
|
123
|
+
break
|
|
124
|
+
return out
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _convert(key: str, text: str, cycle: list[str]) -> object | None:
|
|
128
|
+
"""One style-sheet value -> the value a Plot property expects (``None`` = skip)."""
|
|
129
|
+
if key == "axes.prop_cycle":
|
|
130
|
+
return cycle or None
|
|
131
|
+
if key == "axes.grid":
|
|
132
|
+
return text.strip().lower() in ("true", "1", "yes", "on")
|
|
133
|
+
if key == "axes.titleweight":
|
|
134
|
+
return text.strip().lower() in _BOLD_WEIGHTS
|
|
135
|
+
if key == "grid.linestyle":
|
|
136
|
+
return _LINE_STYLES.get(text.strip().lower())
|
|
137
|
+
if key in _PT_KEYS:
|
|
138
|
+
return _size(text) * PT_TO_PX if key.endswith("size") else _number(text) * PT_TO_PX
|
|
139
|
+
if key in _COLOR_KEYS:
|
|
140
|
+
return _color(text, cycle)
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# --------------------------------------------------------------------------- parsing
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _parse(text: str) -> dict[str, str]:
|
|
148
|
+
"""Parse ``key: value`` lines; later keys win (Matplotlib semantics)."""
|
|
149
|
+
out: dict[str, str] = {}
|
|
150
|
+
for line in text.splitlines():
|
|
151
|
+
line = line.split("#", 1)[0].strip() if not line.lstrip().startswith("#") else ""
|
|
152
|
+
if not line or ":" not in line:
|
|
153
|
+
continue
|
|
154
|
+
key, _, value = line.partition(":")
|
|
155
|
+
out[key.strip()] = value.strip()
|
|
156
|
+
return out
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _number(text: str) -> float:
|
|
160
|
+
try:
|
|
161
|
+
return float(text.strip())
|
|
162
|
+
except ValueError:
|
|
163
|
+
return 0.0
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _size(text: str) -> float:
|
|
167
|
+
"""A font size: a number in points, a named size, or a relative one."""
|
|
168
|
+
text = text.strip().lower()
|
|
169
|
+
if text in _NAMED_SIZES:
|
|
170
|
+
return _NAMED_SIZES[text]
|
|
171
|
+
try:
|
|
172
|
+
return float(text)
|
|
173
|
+
except ValueError:
|
|
174
|
+
return _NAMED_SIZES["medium"]
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
_CYCLE_LIST = re.compile(r"\[([^\]]*)\]")
|
|
178
|
+
_QUOTED = re.compile(r"['\"]([^'\"]+)['\"]")
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _parse_cycle(text: str) -> list[str]:
|
|
182
|
+
"""``cycler('color', ['#1f77b4', …])`` -> the colour list.
|
|
183
|
+
|
|
184
|
+
Only the quoted strings *inside the list* count: the first argument of ``cycler`` is the
|
|
185
|
+
property name (``'color'``) and must not be mistaken for a colour.
|
|
186
|
+
"""
|
|
187
|
+
if "cycler" not in text:
|
|
188
|
+
return []
|
|
189
|
+
inside = _CYCLE_LIST.search(text)
|
|
190
|
+
if not inside:
|
|
191
|
+
return []
|
|
192
|
+
out: list[str] = []
|
|
193
|
+
for item in _QUOTED.findall(inside.group(1)):
|
|
194
|
+
value = _color(item, [])
|
|
195
|
+
if isinstance(value, str):
|
|
196
|
+
out.append(value)
|
|
197
|
+
return out
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _gray(value: float) -> str:
|
|
201
|
+
"""A 0..1 grayscale value as ``#rrggbb`` (matplotlib's float colour shorthand)."""
|
|
202
|
+
level = int(round(max(0.0, min(1.0, value)) * 255))
|
|
203
|
+
return f"#{level:02x}{level:02x}{level:02x}"
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _normalise_hex(text: str) -> str:
|
|
207
|
+
"""Matplotlib accepts hex colours without the leading ``#`` (``E24A33``)."""
|
|
208
|
+
text = text.strip()
|
|
209
|
+
if re.fullmatch(r"[0-9a-fA-F]{6}", text) or re.fullmatch(r"[0-9a-fA-F]{3}", text):
|
|
210
|
+
return f"#{text}"
|
|
211
|
+
return text
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _color(text: str, cycle: list[str]) -> str | float | None:
|
|
215
|
+
"""A colour value: ``#rrggbb``, a name, a grayscale float, or ``C0``… from the cycle.
|
|
216
|
+
|
|
217
|
+
Returns ``None`` for values that mean "nothing" (``none``), which have no equivalent
|
|
218
|
+
here because there is no frame or spine to hide.
|
|
219
|
+
"""
|
|
220
|
+
text = text.strip()
|
|
221
|
+
low = text.lower()
|
|
222
|
+
if low in ("none", ""):
|
|
223
|
+
return None
|
|
224
|
+
match = re.fullmatch(r"c(\d+)", low)
|
|
225
|
+
if match:
|
|
226
|
+
index = int(match.group(1))
|
|
227
|
+
return cycle[index % len(cycle)] if cycle else None
|
|
228
|
+
if text in _SHORT_COLORS: # matplotlib's single-letter names ("w", "k")
|
|
229
|
+
return _SHORT_COLORS[text]
|
|
230
|
+
if re.fullmatch(r"[0-9a-fA-F]{6}", text) or re.fullmatch(r"[0-9a-fA-F]{3}", text):
|
|
231
|
+
return f"#{text}" # matplotlib accepts hex without the leading '#'
|
|
232
|
+
try:
|
|
233
|
+
value = float(text)
|
|
234
|
+
except ValueError:
|
|
235
|
+
return text # a colour name ("white", "steelblue", …)
|
|
236
|
+
return _gray(value) if 0.0 <= value <= 1.0 else None # grayscale shorthand ("0.8")
|