figrecipe 0.6.0__py3-none-any.whl → 0.7.4__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.
- figrecipe/__init__.py +106 -973
- figrecipe/_api/__init__.py +48 -0
- figrecipe/_api/_extract.py +108 -0
- figrecipe/_api/_notebook.py +61 -0
- figrecipe/_api/_panel.py +46 -0
- figrecipe/_api/_save.py +191 -0
- figrecipe/_api/_seaborn_proxy.py +34 -0
- figrecipe/_api/_style_manager.py +153 -0
- figrecipe/_api/_subplots.py +333 -0
- figrecipe/_api/_validate.py +82 -0
- figrecipe/_dev/__init__.py +2 -93
- figrecipe/_dev/_plotters.py +76 -0
- figrecipe/_dev/_run_demos.py +56 -0
- figrecipe/_dev/demo_plotters/__init__.py +35 -166
- figrecipe/_dev/demo_plotters/_categories.py +81 -0
- figrecipe/_dev/demo_plotters/_figure_creators.py +119 -0
- figrecipe/_dev/demo_plotters/_helpers.py +31 -0
- figrecipe/_dev/demo_plotters/_registry.py +50 -0
- figrecipe/_dev/demo_plotters/bar_categorical/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/contour_surface/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/distribution/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/image_matrix/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/line_curve/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/{plot_plot.py → line_curve/plot_plot.py} +3 -2
- figrecipe/_dev/demo_plotters/scatter_points/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/special/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/{plot_pie.py → special/plot_pie.py} +5 -1
- figrecipe/_dev/demo_plotters/spectral_signal/__init__.py +4 -0
- figrecipe/_dev/demo_plotters/vector_flow/__init__.py +4 -0
- figrecipe/_editor/__init__.py +57 -9
- figrecipe/_editor/_bbox/__init__.py +43 -0
- figrecipe/_editor/_bbox/_collections.py +177 -0
- figrecipe/_editor/_bbox/_elements.py +159 -0
- figrecipe/_editor/_bbox/_extract.py +256 -0
- figrecipe/_editor/_bbox/_extract_axes.py +370 -0
- figrecipe/_editor/_bbox/_extract_text.py +342 -0
- figrecipe/_editor/_bbox/_lines.py +173 -0
- figrecipe/_editor/_bbox/_transforms.py +146 -0
- figrecipe/_editor/_flask_app.py +68 -1039
- figrecipe/_editor/_helpers.py +242 -0
- figrecipe/_editor/_hitmap/__init__.py +76 -0
- figrecipe/_editor/_hitmap/_artists/__init__.py +21 -0
- figrecipe/_editor/_hitmap/_artists/_collections.py +345 -0
- figrecipe/_editor/_hitmap/_artists/_images.py +68 -0
- figrecipe/_editor/_hitmap/_artists/_lines.py +107 -0
- figrecipe/_editor/_hitmap/_artists/_patches.py +163 -0
- figrecipe/_editor/_hitmap/_artists/_text.py +190 -0
- figrecipe/_editor/_hitmap/_colors.py +181 -0
- figrecipe/_editor/_hitmap/_detect.py +137 -0
- figrecipe/_editor/_hitmap/_restore.py +154 -0
- figrecipe/_editor/_hitmap_main.py +182 -0
- figrecipe/_editor/_preferences.py +135 -0
- figrecipe/_editor/_render_overrides.py +480 -0
- figrecipe/_editor/_renderer.py +35 -185
- figrecipe/_editor/_routes_axis.py +453 -0
- figrecipe/_editor/_routes_core.py +284 -0
- figrecipe/_editor/_routes_element.py +317 -0
- figrecipe/_editor/_routes_style.py +223 -0
- figrecipe/_editor/_templates/__init__.py +78 -1
- figrecipe/_editor/_templates/_html.py +109 -13
- figrecipe/_editor/_templates/_scripts/__init__.py +120 -0
- figrecipe/_editor/_templates/_scripts/_api.py +228 -0
- figrecipe/_editor/_templates/_scripts/_colors.py +485 -0
- figrecipe/_editor/_templates/_scripts/_core.py +436 -0
- figrecipe/_editor/_templates/_scripts/_debug_snapshot.py +186 -0
- figrecipe/_editor/_templates/_scripts/_element_editor.py +310 -0
- figrecipe/_editor/_templates/_scripts/_files.py +195 -0
- figrecipe/_editor/_templates/_scripts/_hitmap.py +509 -0
- figrecipe/_editor/_templates/_scripts/_inspector.py +315 -0
- figrecipe/_editor/_templates/_scripts/_labels.py +464 -0
- figrecipe/_editor/_templates/_scripts/_legend_drag.py +265 -0
- figrecipe/_editor/_templates/_scripts/_modals.py +226 -0
- figrecipe/_editor/_templates/_scripts/_overlays.py +292 -0
- figrecipe/_editor/_templates/_scripts/_panel_drag.py +334 -0
- figrecipe/_editor/_templates/_scripts/_panel_position.py +279 -0
- figrecipe/_editor/_templates/_scripts/_selection.py +237 -0
- figrecipe/_editor/_templates/_scripts/_tabs.py +89 -0
- figrecipe/_editor/_templates/_scripts/_view_mode.py +107 -0
- figrecipe/_editor/_templates/_scripts/_zoom.py +179 -0
- figrecipe/_editor/_templates/_styles/__init__.py +69 -0
- figrecipe/_editor/_templates/_styles/_base.py +64 -0
- figrecipe/_editor/_templates/_styles/_buttons.py +206 -0
- figrecipe/_editor/_templates/_styles/_color_input.py +123 -0
- figrecipe/_editor/_templates/_styles/_controls.py +265 -0
- figrecipe/_editor/_templates/_styles/_dynamic_props.py +144 -0
- figrecipe/_editor/_templates/_styles/_forms.py +126 -0
- figrecipe/_editor/_templates/_styles/_hitmap.py +184 -0
- figrecipe/_editor/_templates/_styles/_inspector.py +90 -0
- figrecipe/_editor/_templates/_styles/_labels.py +118 -0
- figrecipe/_editor/_templates/_styles/_modals.py +98 -0
- figrecipe/_editor/_templates/_styles/_overlays.py +130 -0
- figrecipe/_editor/_templates/_styles/_preview.py +225 -0
- figrecipe/_editor/_templates/_styles/_selection.py +73 -0
- figrecipe/_params/_DECORATION_METHODS.py +6 -0
- figrecipe/_recorder.py +35 -106
- figrecipe/_recorder_utils.py +124 -0
- figrecipe/_reproducer/__init__.py +18 -0
- figrecipe/_reproducer/_core.py +498 -0
- figrecipe/_reproducer/_custom_plots.py +279 -0
- figrecipe/_reproducer/_seaborn.py +100 -0
- figrecipe/_reproducer/_violin.py +186 -0
- figrecipe/_signatures/_kwargs.py +273 -0
- figrecipe/_signatures/_loader.py +21 -423
- figrecipe/_signatures/_parsing.py +147 -0
- figrecipe/_wrappers/_axes.py +119 -910
- figrecipe/_wrappers/_axes_helpers.py +136 -0
- figrecipe/_wrappers/_axes_plots.py +418 -0
- figrecipe/_wrappers/_axes_seaborn.py +157 -0
- figrecipe/_wrappers/_figure.py +162 -0
- figrecipe/_wrappers/_panel_labels.py +127 -0
- figrecipe/_wrappers/_plot_helpers.py +143 -0
- figrecipe/_wrappers/_violin_helpers.py +180 -0
- figrecipe/styles/__init__.py +8 -6
- figrecipe/styles/_dotdict.py +72 -0
- figrecipe/styles/_finalize.py +134 -0
- figrecipe/styles/_fonts.py +77 -0
- figrecipe/styles/_kwargs_converter.py +178 -0
- figrecipe/styles/_plot_styles.py +209 -0
- figrecipe/styles/_style_applier.py +32 -478
- figrecipe/styles/_style_loader.py +16 -192
- figrecipe/styles/_themes.py +151 -0
- figrecipe/styles/presets/MATPLOTLIB.yaml +2 -1
- figrecipe/styles/presets/SCITEX.yaml +29 -24
- {figrecipe-0.6.0.dist-info → figrecipe-0.7.4.dist-info}/METADATA +37 -2
- figrecipe-0.7.4.dist-info/RECORD +188 -0
- figrecipe/_editor/_bbox.py +0 -978
- figrecipe/_editor/_hitmap.py +0 -937
- figrecipe/_editor/_templates/_scripts.py +0 -2778
- figrecipe/_editor/_templates/_styles.py +0 -1326
- figrecipe/_reproducer.py +0 -975
- figrecipe-0.6.0.dist-info/RECORD +0 -90
- /figrecipe/_dev/demo_plotters/{plot_bar.py → bar_categorical/plot_bar.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_barh.py → bar_categorical/plot_barh.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_contour.py → contour_surface/plot_contour.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_contourf.py → contour_surface/plot_contourf.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_tricontour.py → contour_surface/plot_tricontour.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_tricontourf.py → contour_surface/plot_tricontourf.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_tripcolor.py → contour_surface/plot_tripcolor.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_triplot.py → contour_surface/plot_triplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_boxplot.py → distribution/plot_boxplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_ecdf.py → distribution/plot_ecdf.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_hist.py → distribution/plot_hist.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_hist2d.py → distribution/plot_hist2d.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_violinplot.py → distribution/plot_violinplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_hexbin.py → image_matrix/plot_hexbin.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_imshow.py → image_matrix/plot_imshow.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_matshow.py → image_matrix/plot_matshow.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_pcolor.py → image_matrix/plot_pcolor.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_pcolormesh.py → image_matrix/plot_pcolormesh.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_spy.py → image_matrix/plot_spy.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_errorbar.py → line_curve/plot_errorbar.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_fill.py → line_curve/plot_fill.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_fill_between.py → line_curve/plot_fill_between.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_fill_betweenx.py → line_curve/plot_fill_betweenx.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_stackplot.py → line_curve/plot_stackplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_stairs.py → line_curve/plot_stairs.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_step.py → line_curve/plot_step.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_scatter.py → scatter_points/plot_scatter.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_eventplot.py → special/plot_eventplot.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_loglog.py → special/plot_loglog.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_semilogx.py → special/plot_semilogx.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_semilogy.py → special/plot_semilogy.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_stem.py → special/plot_stem.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_acorr.py → spectral_signal/plot_acorr.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_angle_spectrum.py → spectral_signal/plot_angle_spectrum.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_cohere.py → spectral_signal/plot_cohere.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_csd.py → spectral_signal/plot_csd.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_magnitude_spectrum.py → spectral_signal/plot_magnitude_spectrum.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_phase_spectrum.py → spectral_signal/plot_phase_spectrum.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_psd.py → spectral_signal/plot_psd.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_specgram.py → spectral_signal/plot_specgram.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_xcorr.py → spectral_signal/plot_xcorr.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_barbs.py → vector_flow/plot_barbs.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_quiver.py → vector_flow/plot_quiver.py} +0 -0
- /figrecipe/_dev/demo_plotters/{plot_streamplot.py → vector_flow/plot_streamplot.py} +0 -0
- {figrecipe-0.6.0.dist-info → figrecipe-0.7.4.dist-info}/WHEEL +0 -0
- {figrecipe-0.6.0.dist-info → figrecipe-0.7.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""DotDict class for nested dictionary access with dot notation."""
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DotDict(dict):
|
|
9
|
+
"""Dictionary with dot-notation access to nested keys.
|
|
10
|
+
|
|
11
|
+
Examples
|
|
12
|
+
--------
|
|
13
|
+
>>> d = DotDict({"axes": {"width_mm": 40}})
|
|
14
|
+
>>> d.axes.width_mm
|
|
15
|
+
40
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __getattr__(self, key: str) -> Any:
|
|
19
|
+
# Handle special methods first
|
|
20
|
+
if key == "to_subplots_kwargs":
|
|
21
|
+
from ._style_loader import to_subplots_kwargs
|
|
22
|
+
|
|
23
|
+
return lambda: to_subplots_kwargs(self)
|
|
24
|
+
try:
|
|
25
|
+
value = self[key]
|
|
26
|
+
if isinstance(value, dict) and not isinstance(value, DotDict):
|
|
27
|
+
value = DotDict(value)
|
|
28
|
+
self[key] = value
|
|
29
|
+
return value
|
|
30
|
+
except KeyError:
|
|
31
|
+
raise AttributeError(f"'{type(self).__name__}' has no attribute '{key}'")
|
|
32
|
+
|
|
33
|
+
def __setattr__(self, key: str, value: Any) -> None:
|
|
34
|
+
self[key] = value
|
|
35
|
+
|
|
36
|
+
def __delattr__(self, key: str) -> None:
|
|
37
|
+
try:
|
|
38
|
+
del self[key]
|
|
39
|
+
except KeyError:
|
|
40
|
+
raise AttributeError(key)
|
|
41
|
+
|
|
42
|
+
def __repr__(self) -> str:
|
|
43
|
+
return f"DotDict({super().__repr__()})"
|
|
44
|
+
|
|
45
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
46
|
+
"""Get value with default, supporting nested keys with dots."""
|
|
47
|
+
if "." in key:
|
|
48
|
+
parts = key.split(".")
|
|
49
|
+
value = self
|
|
50
|
+
for part in parts:
|
|
51
|
+
if isinstance(value, dict) and part in value:
|
|
52
|
+
value = value[part]
|
|
53
|
+
else:
|
|
54
|
+
return default
|
|
55
|
+
return value
|
|
56
|
+
return super().get(key, default)
|
|
57
|
+
|
|
58
|
+
def flatten(self, prefix: str = "") -> dict:
|
|
59
|
+
"""Flatten nested dict to single level with underscore-joined keys."""
|
|
60
|
+
result = {}
|
|
61
|
+
for k, v in self.items():
|
|
62
|
+
new_key = f"{prefix}_{k}" if prefix else k
|
|
63
|
+
if isinstance(v, dict):
|
|
64
|
+
result.update(DotDict(v).flatten(new_key))
|
|
65
|
+
else:
|
|
66
|
+
result[new_key] = v
|
|
67
|
+
return result
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
__all__ = ["DotDict"]
|
|
71
|
+
|
|
72
|
+
# EOF
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Finalization utilities for figrecipe styles."""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
|
|
7
|
+
from matplotlib.axes import Axes
|
|
8
|
+
|
|
9
|
+
from ._fonts import check_font
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def finalize_ticks(ax: Axes) -> None:
|
|
13
|
+
"""
|
|
14
|
+
Apply deferred tick configuration after all plotting is done.
|
|
15
|
+
|
|
16
|
+
This function applies the n_ticks setting stored by apply_style_mm(),
|
|
17
|
+
but only to numeric axes (not categorical). Skips pie charts and other
|
|
18
|
+
plot types that should have hidden axes.
|
|
19
|
+
|
|
20
|
+
Parameters
|
|
21
|
+
----------
|
|
22
|
+
ax : matplotlib.axes.Axes
|
|
23
|
+
The axes to finalize.
|
|
24
|
+
"""
|
|
25
|
+
from matplotlib.patches import Wedge
|
|
26
|
+
from matplotlib.ticker import MaxNLocator
|
|
27
|
+
|
|
28
|
+
# Skip pie charts - they should have no ticks
|
|
29
|
+
has_pie = any(isinstance(p, Wedge) for p in ax.patches)
|
|
30
|
+
if has_pie:
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
# Get tick count preferences (new format: min/max)
|
|
34
|
+
n_ticks_min = getattr(ax, "_figrecipe_n_ticks_min", None)
|
|
35
|
+
n_ticks_max = getattr(ax, "_figrecipe_n_ticks_max", None)
|
|
36
|
+
|
|
37
|
+
if n_ticks_min is None and n_ticks_max is None:
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
# Default values - minimum 3 ticks required
|
|
41
|
+
n_ticks_min = max(3, n_ticks_min or 3)
|
|
42
|
+
n_ticks_max = max(n_ticks_min, n_ticks_max or 4)
|
|
43
|
+
|
|
44
|
+
nbins = n_ticks_max
|
|
45
|
+
|
|
46
|
+
def _is_numeric_label(lbl: str) -> bool:
|
|
47
|
+
"""Check if a tick label represents a numeric value."""
|
|
48
|
+
if not lbl:
|
|
49
|
+
return True
|
|
50
|
+
stripped = lbl.replace(".", "").replace("-", "").replace("+", "")
|
|
51
|
+
stripped = stripped.replace("−", "") # Unicode minus sign
|
|
52
|
+
stripped = stripped.replace("e", "").replace("E", "")
|
|
53
|
+
return stripped.isdigit() or stripped == ""
|
|
54
|
+
|
|
55
|
+
# Check if x-axis is categorical
|
|
56
|
+
x_labels = [t.get_text() for t in ax.get_xticklabels()]
|
|
57
|
+
x_is_categorical = any(not _is_numeric_label(lbl) for lbl in x_labels)
|
|
58
|
+
if not x_is_categorical:
|
|
59
|
+
ax.xaxis.set_major_locator(MaxNLocator(nbins=nbins, min_n_ticks=n_ticks_min))
|
|
60
|
+
|
|
61
|
+
# Check if y-axis is categorical
|
|
62
|
+
y_labels = [t.get_text() for t in ax.get_yticklabels()]
|
|
63
|
+
y_is_categorical = any(not _is_numeric_label(lbl) for lbl in y_labels)
|
|
64
|
+
if not y_is_categorical:
|
|
65
|
+
ax.yaxis.set_major_locator(MaxNLocator(nbins=nbins, min_n_ticks=n_ticks_min))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def finalize_special_plots(ax: Axes, style: Dict[str, Any] = None) -> None:
|
|
69
|
+
"""
|
|
70
|
+
Finalize axes visibility for special plot types (pie, imshow, etc.).
|
|
71
|
+
|
|
72
|
+
This should be called after all plotting is done, before saving.
|
|
73
|
+
It handles plot types that need axes/ticks hidden.
|
|
74
|
+
|
|
75
|
+
Parameters
|
|
76
|
+
----------
|
|
77
|
+
ax : matplotlib.axes.Axes
|
|
78
|
+
The axes to finalize.
|
|
79
|
+
style : dict, optional
|
|
80
|
+
Style dictionary. If None, uses defaults.
|
|
81
|
+
"""
|
|
82
|
+
from matplotlib.image import AxesImage
|
|
83
|
+
from matplotlib.patches import Wedge
|
|
84
|
+
|
|
85
|
+
if style is None:
|
|
86
|
+
style = {}
|
|
87
|
+
|
|
88
|
+
# Check for pie chart
|
|
89
|
+
has_pie = any(isinstance(p, Wedge) for p in ax.patches)
|
|
90
|
+
if has_pie:
|
|
91
|
+
show_axes = style.get("pie_show_axes", False)
|
|
92
|
+
text_pt = style.get("pie_text_pt", 6)
|
|
93
|
+
font_family = check_font(style.get("font_family", "Arial"))
|
|
94
|
+
|
|
95
|
+
for text in ax.texts:
|
|
96
|
+
transform = text.get_transform()
|
|
97
|
+
if transform == ax.transAxes:
|
|
98
|
+
x, y = text.get_position()
|
|
99
|
+
if y > 1.0 or y < 0.0:
|
|
100
|
+
continue
|
|
101
|
+
text.set_fontsize(text_pt)
|
|
102
|
+
text.set_fontfamily(font_family)
|
|
103
|
+
|
|
104
|
+
if not show_axes:
|
|
105
|
+
ax.set_xticks([])
|
|
106
|
+
ax.set_yticks([])
|
|
107
|
+
ax.set_xticklabels([])
|
|
108
|
+
ax.set_yticklabels([])
|
|
109
|
+
ax.set_xlabel("")
|
|
110
|
+
ax.set_ylabel("")
|
|
111
|
+
for spine in ax.spines.values():
|
|
112
|
+
spine.set_visible(False)
|
|
113
|
+
|
|
114
|
+
# Check for imshow/matshow (has AxesImage)
|
|
115
|
+
has_image = any(isinstance(c, AxesImage) for c in ax.get_children())
|
|
116
|
+
if has_image:
|
|
117
|
+
xlabel = ax.get_xlabel()
|
|
118
|
+
ylabel = ax.get_ylabel()
|
|
119
|
+
is_specgram = xlabel or ylabel
|
|
120
|
+
|
|
121
|
+
if not is_specgram:
|
|
122
|
+
show_axes = style.get("imshow_show_axes", False)
|
|
123
|
+
if not show_axes:
|
|
124
|
+
ax.set_xticks([])
|
|
125
|
+
ax.set_yticks([])
|
|
126
|
+
ax.set_xticklabels([])
|
|
127
|
+
ax.set_yticklabels([])
|
|
128
|
+
for spine in ax.spines.values():
|
|
129
|
+
spine.set_visible(False)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
__all__ = ["finalize_ticks", "finalize_special_plots"]
|
|
133
|
+
|
|
134
|
+
# EOF
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Font utilities for figrecipe.
|
|
4
|
+
|
|
5
|
+
Provides font availability checking and listing for publication-quality figures.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"list_available_fonts",
|
|
10
|
+
"check_font",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
import warnings
|
|
14
|
+
from typing import List
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def list_available_fonts() -> List[str]:
|
|
18
|
+
"""List all available font families.
|
|
19
|
+
|
|
20
|
+
Returns
|
|
21
|
+
-------
|
|
22
|
+
list of str
|
|
23
|
+
Sorted list of available font family names.
|
|
24
|
+
|
|
25
|
+
Examples
|
|
26
|
+
--------
|
|
27
|
+
>>> fonts = ps.list_available_fonts()
|
|
28
|
+
>>> print(fonts[:5])
|
|
29
|
+
['Arial', 'Courier New', 'DejaVu Sans', ...]
|
|
30
|
+
"""
|
|
31
|
+
import matplotlib.font_manager as fm
|
|
32
|
+
|
|
33
|
+
fonts = set()
|
|
34
|
+
for font in fm.fontManager.ttflist:
|
|
35
|
+
fonts.add(font.name)
|
|
36
|
+
return sorted(fonts)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def check_font(font_family: str, fallback: str = "DejaVu Sans") -> str:
|
|
40
|
+
"""Check if font is available, with fallback and helpful error message.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
font_family : str
|
|
45
|
+
Requested font family name.
|
|
46
|
+
fallback : str
|
|
47
|
+
Fallback font if requested font is not available.
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
str
|
|
52
|
+
The font to use (original if available, fallback otherwise).
|
|
53
|
+
|
|
54
|
+
Examples
|
|
55
|
+
--------
|
|
56
|
+
>>> font = check_font("Arial") # Returns "Arial" if available
|
|
57
|
+
>>> font = check_font("NonExistentFont") # Returns fallback with warning
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
available = list_available_fonts()
|
|
61
|
+
|
|
62
|
+
if font_family in available:
|
|
63
|
+
return font_family
|
|
64
|
+
|
|
65
|
+
# Font not found - show helpful message
|
|
66
|
+
similar = [f for f in available if font_family.lower() in f.lower()]
|
|
67
|
+
|
|
68
|
+
msg = f"Font '{font_family}' not found.\n"
|
|
69
|
+
if similar:
|
|
70
|
+
msg += f" Similar fonts available: {similar[:5]}\n"
|
|
71
|
+
msg += f" Using fallback: '{fallback}'\n"
|
|
72
|
+
msg += " To see all available fonts: ps.list_available_fonts()\n"
|
|
73
|
+
msg += " To install Arial on Linux: sudo apt install ttf-mscorefonts-installer"
|
|
74
|
+
|
|
75
|
+
warnings.warn(msg, UserWarning)
|
|
76
|
+
|
|
77
|
+
return fallback if fallback in available else "DejaVu Sans"
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Convert style DotDict to subplots kwargs."""
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from ._dotdict import DotDict
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def to_subplots_kwargs(style: Optional["DotDict"] = None) -> Dict[str, Any]:
|
|
12
|
+
"""Convert style DotDict to kwargs for ps.subplots().
|
|
13
|
+
|
|
14
|
+
Uses YAML-compatible flattened key names as the single source of truth.
|
|
15
|
+
For example, YAML `fonts.axis_label_pt` becomes `fonts_axis_label_pt`.
|
|
16
|
+
|
|
17
|
+
Parameters
|
|
18
|
+
----------
|
|
19
|
+
style : DotDict, optional
|
|
20
|
+
Style configuration. If None, uses current loaded style.
|
|
21
|
+
|
|
22
|
+
Returns
|
|
23
|
+
-------
|
|
24
|
+
dict
|
|
25
|
+
Keyword arguments for ps.subplots() with YAML-compatible flattened keys.
|
|
26
|
+
|
|
27
|
+
Examples
|
|
28
|
+
--------
|
|
29
|
+
>>> style = load_style()
|
|
30
|
+
>>> kwargs = to_subplots_kwargs(style)
|
|
31
|
+
>>> fig, ax = ps.subplots(**kwargs)
|
|
32
|
+
"""
|
|
33
|
+
if style is None:
|
|
34
|
+
from ._style_loader import get_style
|
|
35
|
+
|
|
36
|
+
style = get_style()
|
|
37
|
+
|
|
38
|
+
# YAML-compatible flattened keys (single source of truth)
|
|
39
|
+
result = {
|
|
40
|
+
# Axes (axes.* in YAML)
|
|
41
|
+
"axes_width_mm": style.axes.width_mm,
|
|
42
|
+
"axes_height_mm": style.axes.height_mm,
|
|
43
|
+
"axes_thickness_mm": style.axes.thickness_mm,
|
|
44
|
+
# Margins (margins.* in YAML)
|
|
45
|
+
"margins_left_mm": style.margins.left_mm,
|
|
46
|
+
"margins_right_mm": style.margins.right_mm,
|
|
47
|
+
"margins_bottom_mm": style.margins.bottom_mm,
|
|
48
|
+
"margins_top_mm": style.margins.top_mm,
|
|
49
|
+
# Spacing (spacing.* in YAML)
|
|
50
|
+
"spacing_horizontal_mm": style.spacing.horizontal_mm,
|
|
51
|
+
"spacing_vertical_mm": style.spacing.vertical_mm,
|
|
52
|
+
# Ticks (ticks.* in YAML)
|
|
53
|
+
"ticks_length_mm": style.ticks.length_mm,
|
|
54
|
+
"ticks_thickness_mm": style.ticks.thickness_mm,
|
|
55
|
+
"ticks_direction": style.ticks.get("direction", "out"),
|
|
56
|
+
"ticks_n_ticks_min": style.ticks.get("n_ticks_min", 3),
|
|
57
|
+
"ticks_n_ticks_max": style.ticks.get("n_ticks_max", 4),
|
|
58
|
+
# Lines (lines.* in YAML)
|
|
59
|
+
"lines_trace_mm": style.lines.trace_mm,
|
|
60
|
+
"lines_errorbar_mm": style.lines.get("errorbar_mm", 0.2),
|
|
61
|
+
"lines_errorbar_cap_mm": style.lines.get("errorbar_cap_mm", 0.8),
|
|
62
|
+
# Markers (markers.* in YAML)
|
|
63
|
+
"markers_size_mm": style.markers.size_mm,
|
|
64
|
+
"markers_scatter_mm": style.markers.get("scatter_mm", style.markers.size_mm),
|
|
65
|
+
"markers_flier_mm": style.markers.get("flier_mm", style.markers.size_mm),
|
|
66
|
+
"markers_edge_width_mm": style.markers.get("edge_width_mm"),
|
|
67
|
+
# Boxplot (boxplot.* in YAML)
|
|
68
|
+
"boxplot_line_mm": style.get("boxplot", {}).get("line_mm", 0.2),
|
|
69
|
+
"boxplot_whisker_mm": style.get("boxplot", {}).get("whisker_mm", 0.2),
|
|
70
|
+
"boxplot_cap_mm": style.get("boxplot", {}).get("cap_mm", 0.2),
|
|
71
|
+
"boxplot_median_mm": style.get("boxplot", {}).get("median_mm", 0.2),
|
|
72
|
+
"boxplot_median_color": style.get("boxplot", {}).get("median_color", "black"),
|
|
73
|
+
"boxplot_flier_edge_mm": style.get("boxplot", {}).get("flier_edge_mm", 0.2),
|
|
74
|
+
# Violinplot (violinplot.* in YAML)
|
|
75
|
+
"violinplot_line_mm": style.get("violinplot", {}).get("line_mm", 0.2),
|
|
76
|
+
"violinplot_inner": style.get("violinplot", {}).get("inner", "box"),
|
|
77
|
+
"violinplot_box_width_mm": style.get("violinplot", {}).get("box_width_mm", 1.5),
|
|
78
|
+
"violinplot_whisker_mm": style.get("violinplot", {}).get("whisker_mm", 0.2),
|
|
79
|
+
"violinplot_median_mm": style.get("violinplot", {}).get("median_mm", 0.8),
|
|
80
|
+
# Barplot (barplot.* in YAML)
|
|
81
|
+
"barplot_edge_mm": style.get("barplot", {}).get("edge_mm", 0.2),
|
|
82
|
+
# Histogram (histogram.* in YAML)
|
|
83
|
+
"histogram_edge_mm": style.get("histogram", {}).get("edge_mm", 0.2),
|
|
84
|
+
# Pie chart (pie.* in YAML)
|
|
85
|
+
"pie_text_pt": style.get("pie", {}).get("text_pt", 6),
|
|
86
|
+
"pie_show_axes": style.get("pie", {}).get("show_axes", False),
|
|
87
|
+
# Imshow (imshow.* in YAML)
|
|
88
|
+
"imshow_show_axes": style.get("imshow", {}).get("show_axes", False),
|
|
89
|
+
"imshow_show_labels": style.get("imshow", {}).get("show_labels", False),
|
|
90
|
+
# Fonts (fonts.* in YAML)
|
|
91
|
+
"fonts_family": style.fonts.family,
|
|
92
|
+
"fonts_axis_label_pt": style.fonts.axis_label_pt,
|
|
93
|
+
"fonts_tick_label_pt": style.fonts.tick_label_pt,
|
|
94
|
+
"fonts_title_pt": style.fonts.title_pt,
|
|
95
|
+
"fonts_suptitle_pt": style.fonts.suptitle_pt,
|
|
96
|
+
"fonts_legend_pt": style.fonts.legend_pt,
|
|
97
|
+
"fonts_annotation_pt": style.fonts.get("annotation_pt", 6),
|
|
98
|
+
# Padding (padding.* in YAML)
|
|
99
|
+
"padding_label_pt": style.padding.label_pt,
|
|
100
|
+
"padding_tick_pt": style.padding.tick_pt,
|
|
101
|
+
"padding_title_pt": style.padding.title_pt,
|
|
102
|
+
# Output (output.* in YAML)
|
|
103
|
+
"output_dpi": style.output.dpi,
|
|
104
|
+
"output_transparent": style.output.get("transparent", True),
|
|
105
|
+
"output_format": style.output.get("format", "pdf"),
|
|
106
|
+
# Theme (theme.* in YAML)
|
|
107
|
+
"theme_mode": style.theme.mode,
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# Add theme colors from preset if available
|
|
111
|
+
theme_mode = style.theme.mode
|
|
112
|
+
if "theme" in style and theme_mode in style.theme:
|
|
113
|
+
result["theme_colors"] = dict(style.theme[theme_mode])
|
|
114
|
+
|
|
115
|
+
# Add color palette if available
|
|
116
|
+
if "colors" in style and "palette" in style.colors:
|
|
117
|
+
result["color_palette"] = list(style.colors.palette)
|
|
118
|
+
|
|
119
|
+
# Add behavior settings (behavior.* in YAML)
|
|
120
|
+
if "behavior" in style:
|
|
121
|
+
behavior = style.behavior
|
|
122
|
+
if hasattr(behavior, "hide_top_spine"):
|
|
123
|
+
result["behavior_hide_top_spine"] = behavior.hide_top_spine
|
|
124
|
+
if hasattr(behavior, "hide_right_spine"):
|
|
125
|
+
result["behavior_hide_right_spine"] = behavior.hide_right_spine
|
|
126
|
+
if hasattr(behavior, "grid"):
|
|
127
|
+
result["behavior_grid"] = behavior.grid
|
|
128
|
+
if hasattr(behavior, "auto_scale_axes"):
|
|
129
|
+
result["behavior_auto_scale_axes"] = behavior.auto_scale_axes
|
|
130
|
+
if hasattr(behavior, "constrained_layout"):
|
|
131
|
+
result["behavior_constrained_layout"] = behavior.constrained_layout
|
|
132
|
+
|
|
133
|
+
# Legacy key aliases for backwards compatibility
|
|
134
|
+
result.update(_get_legacy_aliases(result))
|
|
135
|
+
|
|
136
|
+
return result
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _get_legacy_aliases(result: Dict[str, Any]) -> Dict[str, Any]:
|
|
140
|
+
"""Get legacy key aliases for backwards compatibility.
|
|
141
|
+
|
|
142
|
+
These allow existing code using old keys to still work.
|
|
143
|
+
"""
|
|
144
|
+
return {
|
|
145
|
+
"margin_left_mm": result["margins_left_mm"],
|
|
146
|
+
"margin_right_mm": result["margins_right_mm"],
|
|
147
|
+
"margin_bottom_mm": result["margins_bottom_mm"],
|
|
148
|
+
"margin_top_mm": result["margins_top_mm"],
|
|
149
|
+
"space_w_mm": result["spacing_horizontal_mm"],
|
|
150
|
+
"space_h_mm": result["spacing_vertical_mm"],
|
|
151
|
+
"tick_length_mm": result["ticks_length_mm"],
|
|
152
|
+
"tick_thickness_mm": result["ticks_thickness_mm"],
|
|
153
|
+
"n_ticks_min": result["ticks_n_ticks_min"],
|
|
154
|
+
"n_ticks_max": result["ticks_n_ticks_max"],
|
|
155
|
+
"trace_thickness_mm": result["lines_trace_mm"],
|
|
156
|
+
"marker_size_mm": result["markers_size_mm"],
|
|
157
|
+
"font_family": result["fonts_family"],
|
|
158
|
+
"axis_font_size_pt": result["fonts_axis_label_pt"],
|
|
159
|
+
"tick_font_size_pt": result["fonts_tick_label_pt"],
|
|
160
|
+
"title_font_size_pt": result["fonts_title_pt"],
|
|
161
|
+
"suptitle_font_size_pt": result["fonts_suptitle_pt"],
|
|
162
|
+
"legend_font_size_pt": result["fonts_legend_pt"],
|
|
163
|
+
"label_pad_pt": result["padding_label_pt"],
|
|
164
|
+
"tick_pad_pt": result["padding_tick_pt"],
|
|
165
|
+
"title_pad_pt": result["padding_title_pt"],
|
|
166
|
+
"dpi": result["output_dpi"],
|
|
167
|
+
"theme": result["theme_mode"],
|
|
168
|
+
"hide_top_spine": result.get("behavior_hide_top_spine", True),
|
|
169
|
+
"hide_right_spine": result.get("behavior_hide_right_spine", True),
|
|
170
|
+
"grid": result.get("behavior_grid", False),
|
|
171
|
+
"auto_scale_axes": result.get("behavior_auto_scale_axes", True),
|
|
172
|
+
"constrained_layout": result.get("behavior_constrained_layout", False),
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
__all__ = ["to_subplots_kwargs"]
|
|
177
|
+
|
|
178
|
+
# EOF
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Plot-specific style application for figrecipe."""
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
|
|
7
|
+
from matplotlib.axes import Axes
|
|
8
|
+
|
|
9
|
+
from .._utils._units import mm_to_pt
|
|
10
|
+
from ._fonts import check_font
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def apply_boxplot_style(ax: Axes, style: Dict[str, Any]) -> None:
|
|
14
|
+
"""Apply boxplot line width styling to existing boxplot elements.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
ax : matplotlib.axes.Axes
|
|
19
|
+
Target axes containing boxplot elements.
|
|
20
|
+
style : dict
|
|
21
|
+
Style dictionary with boxplot_* keys.
|
|
22
|
+
"""
|
|
23
|
+
from matplotlib.lines import Line2D
|
|
24
|
+
from matplotlib.patches import PathPatch
|
|
25
|
+
|
|
26
|
+
box_lw = mm_to_pt(style.get("boxplot_line_mm", 0.2))
|
|
27
|
+
whisker_lw = mm_to_pt(style.get("boxplot_whisker_mm", 0.2))
|
|
28
|
+
cap_lw = mm_to_pt(style.get("boxplot_cap_mm", 0.2))
|
|
29
|
+
median_lw = mm_to_pt(style.get("boxplot_median_mm", 0.2))
|
|
30
|
+
median_color = style.get("boxplot_median_color", "black")
|
|
31
|
+
flier_edge_lw = mm_to_pt(style.get("boxplot_flier_edge_mm", 0.2))
|
|
32
|
+
|
|
33
|
+
for child in ax.get_children():
|
|
34
|
+
if isinstance(child, PathPatch):
|
|
35
|
+
if child.get_edgecolor() is not None:
|
|
36
|
+
child.set_linewidth(box_lw)
|
|
37
|
+
|
|
38
|
+
elif isinstance(child, Line2D):
|
|
39
|
+
xdata = child.get_xdata()
|
|
40
|
+
ydata = child.get_ydata()
|
|
41
|
+
|
|
42
|
+
marker = child.get_marker()
|
|
43
|
+
linestyle = child.get_linestyle()
|
|
44
|
+
if marker and marker != "None" and linestyle in ("None", "", " "):
|
|
45
|
+
child.set_markeredgewidth(flier_edge_lw)
|
|
46
|
+
elif len(xdata) == 2 and len(ydata) == 2:
|
|
47
|
+
if ydata[0] == ydata[1]:
|
|
48
|
+
if linestyle == "-":
|
|
49
|
+
child.set_linewidth(median_lw)
|
|
50
|
+
if median_color:
|
|
51
|
+
child.set_color(median_color)
|
|
52
|
+
else:
|
|
53
|
+
child.set_linewidth(cap_lw)
|
|
54
|
+
elif xdata[0] == xdata[1]:
|
|
55
|
+
child.set_linewidth(whisker_lw)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def apply_violinplot_style(ax: Axes, style: Dict[str, Any]) -> None:
|
|
59
|
+
"""Apply violinplot line width styling to existing violinplot elements.
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
ax : matplotlib.axes.Axes
|
|
64
|
+
Target axes containing violinplot elements.
|
|
65
|
+
style : dict
|
|
66
|
+
Style dictionary with violinplot_* keys.
|
|
67
|
+
"""
|
|
68
|
+
from matplotlib.collections import LineCollection, PolyCollection
|
|
69
|
+
|
|
70
|
+
body_lw = mm_to_pt(style.get("violinplot_line_mm", 0.2))
|
|
71
|
+
whisker_lw = mm_to_pt(style.get("violinplot_whisker_mm", 0.2))
|
|
72
|
+
|
|
73
|
+
for child in ax.get_children():
|
|
74
|
+
if isinstance(child, PolyCollection):
|
|
75
|
+
child.set_linewidth(body_lw)
|
|
76
|
+
elif isinstance(child, LineCollection):
|
|
77
|
+
child.set_linewidth(whisker_lw)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def apply_barplot_style(ax: Axes, style: Dict[str, Any]) -> None:
|
|
81
|
+
"""Apply barplot edge styling to existing bar elements.
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
ax : matplotlib.axes.Axes
|
|
86
|
+
Target axes containing bar elements.
|
|
87
|
+
style : dict
|
|
88
|
+
Style dictionary with barplot_* keys.
|
|
89
|
+
"""
|
|
90
|
+
from matplotlib.patches import Rectangle
|
|
91
|
+
|
|
92
|
+
edge_lw = mm_to_pt(style.get("barplot_edge_mm", 0.2))
|
|
93
|
+
|
|
94
|
+
for patch in ax.patches:
|
|
95
|
+
if isinstance(patch, Rectangle):
|
|
96
|
+
patch.set_linewidth(edge_lw)
|
|
97
|
+
patch.set_edgecolor("black")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def apply_histogram_style(ax: Axes, style: Dict[str, Any]) -> None:
|
|
101
|
+
"""Apply histogram edge styling to existing histogram elements.
|
|
102
|
+
|
|
103
|
+
Parameters
|
|
104
|
+
----------
|
|
105
|
+
ax : matplotlib.axes.Axes
|
|
106
|
+
Target axes containing histogram elements.
|
|
107
|
+
style : dict
|
|
108
|
+
Style dictionary with histogram_* keys.
|
|
109
|
+
"""
|
|
110
|
+
from matplotlib.patches import Rectangle
|
|
111
|
+
|
|
112
|
+
edge_lw = mm_to_pt(style.get("histogram_edge_mm", 0.2))
|
|
113
|
+
|
|
114
|
+
for patch in ax.patches:
|
|
115
|
+
if isinstance(patch, Rectangle):
|
|
116
|
+
patch.set_linewidth(edge_lw)
|
|
117
|
+
patch.set_edgecolor("black")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def apply_pie_style(ax: Axes, style: Dict[str, Any]) -> None:
|
|
121
|
+
"""Apply pie chart styling to existing pie elements.
|
|
122
|
+
|
|
123
|
+
Parameters
|
|
124
|
+
----------
|
|
125
|
+
ax : matplotlib.axes.Axes
|
|
126
|
+
Target axes containing pie chart elements.
|
|
127
|
+
style : dict
|
|
128
|
+
Style dictionary with pie_* keys.
|
|
129
|
+
"""
|
|
130
|
+
from matplotlib.patches import Wedge
|
|
131
|
+
|
|
132
|
+
has_pie = any(isinstance(p, Wedge) for p in ax.patches)
|
|
133
|
+
if not has_pie:
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
text_pt = style.get("pie_text_pt", 6)
|
|
137
|
+
show_axes = style.get("pie_show_axes", False)
|
|
138
|
+
font_family = check_font(style.get("font_family", "Arial"))
|
|
139
|
+
|
|
140
|
+
for text in ax.texts:
|
|
141
|
+
transform = text.get_transform()
|
|
142
|
+
if transform == ax.transAxes:
|
|
143
|
+
x, y = text.get_position()
|
|
144
|
+
if y > 1.0 or y < 0.0:
|
|
145
|
+
continue
|
|
146
|
+
text.set_fontsize(text_pt)
|
|
147
|
+
text.set_fontfamily(font_family)
|
|
148
|
+
|
|
149
|
+
if not show_axes:
|
|
150
|
+
ax.set_xticks([])
|
|
151
|
+
ax.set_yticks([])
|
|
152
|
+
ax.set_xticklabels([])
|
|
153
|
+
ax.set_yticklabels([])
|
|
154
|
+
for spine in ax.spines.values():
|
|
155
|
+
spine.set_visible(False)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def apply_matrix_style(ax: Axes, style: Dict[str, Any]) -> None:
|
|
159
|
+
"""Apply imshow/matshow/spy styling (hide axes if configured).
|
|
160
|
+
|
|
161
|
+
Parameters
|
|
162
|
+
----------
|
|
163
|
+
ax : matplotlib.axes.Axes
|
|
164
|
+
Target axes containing matrix plot elements.
|
|
165
|
+
style : dict
|
|
166
|
+
Style dictionary with imshow_*, matshow_*, spy_* keys.
|
|
167
|
+
"""
|
|
168
|
+
from matplotlib.image import AxesImage
|
|
169
|
+
|
|
170
|
+
has_image = any(isinstance(c, AxesImage) for c in ax.get_children())
|
|
171
|
+
if not has_image:
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
# Check if this is specgram (has xlabel or ylabel)
|
|
175
|
+
# Specgram typically has "Time" and "Frequency" labels
|
|
176
|
+
xlabel = ax.get_xlabel()
|
|
177
|
+
ylabel = ax.get_ylabel()
|
|
178
|
+
is_specgram = bool(xlabel or ylabel)
|
|
179
|
+
|
|
180
|
+
# Don't hide axes for specgram - it needs visible ticks
|
|
181
|
+
if is_specgram:
|
|
182
|
+
return
|
|
183
|
+
|
|
184
|
+
show_axes = style.get("imshow_show_axes", True)
|
|
185
|
+
show_labels = style.get("imshow_show_labels", True)
|
|
186
|
+
|
|
187
|
+
if not show_axes:
|
|
188
|
+
ax.set_xticks([])
|
|
189
|
+
ax.set_yticks([])
|
|
190
|
+
ax.set_xticklabels([])
|
|
191
|
+
ax.set_yticklabels([])
|
|
192
|
+
for spine in ax.spines.values():
|
|
193
|
+
spine.set_visible(False)
|
|
194
|
+
|
|
195
|
+
if not show_labels:
|
|
196
|
+
ax.set_xlabel("")
|
|
197
|
+
ax.set_ylabel("")
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
__all__ = [
|
|
201
|
+
"apply_boxplot_style",
|
|
202
|
+
"apply_violinplot_style",
|
|
203
|
+
"apply_barplot_style",
|
|
204
|
+
"apply_histogram_style",
|
|
205
|
+
"apply_pie_style",
|
|
206
|
+
"apply_matrix_style",
|
|
207
|
+
]
|
|
208
|
+
|
|
209
|
+
# EOF
|