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
|
@@ -21,10 +21,12 @@ __all__ = [
|
|
|
21
21
|
"load_preset",
|
|
22
22
|
"unload_style",
|
|
23
23
|
"get_style",
|
|
24
|
+
"get_current_style_dict",
|
|
24
25
|
"reload_style",
|
|
25
26
|
"list_presets",
|
|
26
27
|
"STYLE",
|
|
27
28
|
"to_subplots_kwargs",
|
|
29
|
+
"DotDict",
|
|
28
30
|
]
|
|
29
31
|
|
|
30
32
|
from pathlib import Path
|
|
@@ -32,6 +34,9 @@ from typing import Any, Dict, List, Optional, Union
|
|
|
32
34
|
|
|
33
35
|
from ruamel.yaml import YAML
|
|
34
36
|
|
|
37
|
+
from ._dotdict import DotDict
|
|
38
|
+
from ._kwargs_converter import to_subplots_kwargs
|
|
39
|
+
|
|
35
40
|
# Path to presets directory
|
|
36
41
|
_PRESETS_DIR = Path(__file__).parent / "presets"
|
|
37
42
|
|
|
@@ -44,7 +49,7 @@ _PRESET_ALIASES = {
|
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
# Global style cache
|
|
47
|
-
_STYLE_CACHE: Optional[
|
|
52
|
+
_STYLE_CACHE: Optional[DotDict] = None
|
|
48
53
|
_CURRENT_STYLE_NAME: Optional[str] = None
|
|
49
54
|
|
|
50
55
|
|
|
@@ -70,52 +75,6 @@ def list_presets() -> List[str]:
|
|
|
70
75
|
return ["MATPLOTLIB", "SCITEX"]
|
|
71
76
|
|
|
72
77
|
|
|
73
|
-
class DotDict(dict):
|
|
74
|
-
"""Dictionary with dot-notation access to nested keys.
|
|
75
|
-
|
|
76
|
-
Examples
|
|
77
|
-
--------
|
|
78
|
-
>>> d = DotDict({"axes": {"width_mm": 40}})
|
|
79
|
-
>>> d.axes.width_mm
|
|
80
|
-
40
|
|
81
|
-
"""
|
|
82
|
-
|
|
83
|
-
def __getattr__(self, key: str) -> Any:
|
|
84
|
-
# Handle special methods first
|
|
85
|
-
if key == "to_subplots_kwargs":
|
|
86
|
-
return lambda: to_subplots_kwargs(self)
|
|
87
|
-
try:
|
|
88
|
-
value = self[key]
|
|
89
|
-
if isinstance(value, dict) and not isinstance(value, DotDict):
|
|
90
|
-
value = DotDict(value)
|
|
91
|
-
self[key] = value
|
|
92
|
-
return value
|
|
93
|
-
except KeyError:
|
|
94
|
-
raise AttributeError(f"'{type(self).__name__}' has no attribute '{key}'")
|
|
95
|
-
|
|
96
|
-
def __setattr__(self, key: str, value: Any) -> None:
|
|
97
|
-
self[key] = value
|
|
98
|
-
|
|
99
|
-
def __delattr__(self, key: str) -> None:
|
|
100
|
-
try:
|
|
101
|
-
del self[key]
|
|
102
|
-
except KeyError:
|
|
103
|
-
raise AttributeError(f"'{type(self).__name__}' has no attribute '{key}'")
|
|
104
|
-
|
|
105
|
-
def get(self, key: str, default: Any = None) -> Any:
|
|
106
|
-
"""Get value with default, supporting nested keys with dots."""
|
|
107
|
-
if "." in key:
|
|
108
|
-
parts = key.split(".")
|
|
109
|
-
value = self
|
|
110
|
-
for part in parts:
|
|
111
|
-
if isinstance(value, dict) and part in value:
|
|
112
|
-
value = value[part]
|
|
113
|
-
else:
|
|
114
|
-
return default
|
|
115
|
-
return value
|
|
116
|
-
return super().get(key, default)
|
|
117
|
-
|
|
118
|
-
|
|
119
78
|
def _deep_merge(base: Dict, override: Dict) -> Dict:
|
|
120
79
|
"""Deep merge two dictionaries, with override taking precedence."""
|
|
121
80
|
result = base.copy()
|
|
@@ -391,156 +350,19 @@ def get_style() -> DotDict:
|
|
|
391
350
|
return _STYLE_CACHE
|
|
392
351
|
|
|
393
352
|
|
|
394
|
-
def
|
|
395
|
-
"""
|
|
396
|
-
|
|
397
|
-
Uses YAML-compatible flattened key names as the single source of truth.
|
|
398
|
-
For example, YAML `fonts.axis_label_pt` becomes `fonts_axis_label_pt`.
|
|
399
|
-
|
|
400
|
-
Parameters
|
|
401
|
-
----------
|
|
402
|
-
style : DotDict, optional
|
|
403
|
-
Style configuration. If None, uses current loaded style.
|
|
353
|
+
def get_current_style_dict() -> Dict[str, Any]:
|
|
354
|
+
"""Get current style as a flat dictionary for style applier functions.
|
|
404
355
|
|
|
405
356
|
Returns
|
|
406
357
|
-------
|
|
407
358
|
dict
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
Examples
|
|
411
|
-
--------
|
|
412
|
-
>>> style = load_style()
|
|
413
|
-
>>> kwargs = to_subplots_kwargs(style)
|
|
414
|
-
>>> fig, ax = ps.subplots(**kwargs)
|
|
359
|
+
Flattened style dictionary with keys like 'pie_show_axes', 'imshow_show_axes'.
|
|
360
|
+
Returns empty dict if no style is loaded.
|
|
415
361
|
"""
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
result = {
|
|
421
|
-
# Axes (axes.* in YAML)
|
|
422
|
-
"axes_width_mm": style.axes.width_mm,
|
|
423
|
-
"axes_height_mm": style.axes.height_mm,
|
|
424
|
-
"axes_thickness_mm": style.axes.thickness_mm,
|
|
425
|
-
# Margins (margins.* in YAML)
|
|
426
|
-
"margins_left_mm": style.margins.left_mm,
|
|
427
|
-
"margins_right_mm": style.margins.right_mm,
|
|
428
|
-
"margins_bottom_mm": style.margins.bottom_mm,
|
|
429
|
-
"margins_top_mm": style.margins.top_mm,
|
|
430
|
-
# Spacing (spacing.* in YAML)
|
|
431
|
-
"spacing_horizontal_mm": style.spacing.horizontal_mm,
|
|
432
|
-
"spacing_vertical_mm": style.spacing.vertical_mm,
|
|
433
|
-
# Ticks (ticks.* in YAML)
|
|
434
|
-
"ticks_length_mm": style.ticks.length_mm,
|
|
435
|
-
"ticks_thickness_mm": style.ticks.thickness_mm,
|
|
436
|
-
"ticks_direction": style.ticks.get("direction", "out"),
|
|
437
|
-
"ticks_n_ticks": style.ticks.n_ticks,
|
|
438
|
-
# Lines (lines.* in YAML)
|
|
439
|
-
"lines_trace_mm": style.lines.trace_mm,
|
|
440
|
-
"lines_errorbar_mm": style.lines.get("errorbar_mm", 0.2),
|
|
441
|
-
"lines_errorbar_cap_mm": style.lines.get("errorbar_cap_mm", 0.8),
|
|
442
|
-
# Markers (markers.* in YAML)
|
|
443
|
-
"markers_size_mm": style.markers.size_mm,
|
|
444
|
-
"markers_scatter_mm": style.markers.get("scatter_mm", style.markers.size_mm),
|
|
445
|
-
"markers_flier_mm": style.markers.get("flier_mm", style.markers.size_mm),
|
|
446
|
-
"markers_edge_width_mm": style.markers.get("edge_width_mm"),
|
|
447
|
-
# Boxplot (boxplot.* in YAML)
|
|
448
|
-
"boxplot_line_mm": style.get("boxplot", {}).get("line_mm", 0.2),
|
|
449
|
-
"boxplot_whisker_mm": style.get("boxplot", {}).get("whisker_mm", 0.2),
|
|
450
|
-
"boxplot_cap_mm": style.get("boxplot", {}).get("cap_mm", 0.2),
|
|
451
|
-
"boxplot_median_mm": style.get("boxplot", {}).get("median_mm", 0.2),
|
|
452
|
-
"boxplot_median_color": style.get("boxplot", {}).get("median_color", "black"),
|
|
453
|
-
"boxplot_flier_edge_mm": style.get("boxplot", {}).get("flier_edge_mm", 0.2),
|
|
454
|
-
# Violinplot (violinplot.* in YAML)
|
|
455
|
-
"violinplot_line_mm": style.get("violinplot", {}).get("line_mm", 0.2),
|
|
456
|
-
"violinplot_inner": style.get("violinplot", {}).get("inner", "box"),
|
|
457
|
-
"violinplot_box_width_mm": style.get("violinplot", {}).get("box_width_mm", 1.5),
|
|
458
|
-
"violinplot_whisker_mm": style.get("violinplot", {}).get("whisker_mm", 0.2),
|
|
459
|
-
"violinplot_median_mm": style.get("violinplot", {}).get("median_mm", 0.8),
|
|
460
|
-
# Barplot (barplot.* in YAML)
|
|
461
|
-
"barplot_edge_mm": style.get("barplot", {}).get("edge_mm", 0.2),
|
|
462
|
-
# Histogram (histogram.* in YAML)
|
|
463
|
-
"histogram_edge_mm": style.get("histogram", {}).get("edge_mm", 0.2),
|
|
464
|
-
# Pie chart (pie.* in YAML)
|
|
465
|
-
"pie_text_pt": style.get("pie", {}).get("text_pt", 6),
|
|
466
|
-
"pie_show_axes": style.get("pie", {}).get("show_axes", False),
|
|
467
|
-
# Imshow (imshow.* in YAML)
|
|
468
|
-
"imshow_show_axes": style.get("imshow", {}).get("show_axes", False),
|
|
469
|
-
"imshow_show_labels": style.get("imshow", {}).get("show_labels", False),
|
|
470
|
-
# Fonts (fonts.* in YAML)
|
|
471
|
-
"fonts_family": style.fonts.family,
|
|
472
|
-
"fonts_axis_label_pt": style.fonts.axis_label_pt,
|
|
473
|
-
"fonts_tick_label_pt": style.fonts.tick_label_pt,
|
|
474
|
-
"fonts_title_pt": style.fonts.title_pt,
|
|
475
|
-
"fonts_suptitle_pt": style.fonts.suptitle_pt,
|
|
476
|
-
"fonts_legend_pt": style.fonts.legend_pt,
|
|
477
|
-
"fonts_annotation_pt": style.fonts.get("annotation_pt", 6),
|
|
478
|
-
# Padding (padding.* in YAML)
|
|
479
|
-
"padding_label_pt": style.padding.label_pt,
|
|
480
|
-
"padding_tick_pt": style.padding.tick_pt,
|
|
481
|
-
"padding_title_pt": style.padding.title_pt,
|
|
482
|
-
# Output (output.* in YAML)
|
|
483
|
-
"output_dpi": style.output.dpi,
|
|
484
|
-
"output_transparent": style.output.get("transparent", True),
|
|
485
|
-
"output_format": style.output.get("format", "pdf"),
|
|
486
|
-
# Theme (theme.* in YAML)
|
|
487
|
-
"theme_mode": style.theme.mode,
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
# Add theme colors from preset if available
|
|
491
|
-
theme_mode = style.theme.mode
|
|
492
|
-
if "theme" in style and theme_mode in style.theme:
|
|
493
|
-
result["theme_colors"] = dict(style.theme[theme_mode])
|
|
494
|
-
|
|
495
|
-
# Add color palette if available
|
|
496
|
-
if "colors" in style and "palette" in style.colors:
|
|
497
|
-
result["color_palette"] = list(style.colors.palette)
|
|
498
|
-
|
|
499
|
-
# Add behavior settings (behavior.* in YAML)
|
|
500
|
-
if "behavior" in style:
|
|
501
|
-
behavior = style.behavior
|
|
502
|
-
if hasattr(behavior, "hide_top_spine"):
|
|
503
|
-
result["behavior_hide_top_spine"] = behavior.hide_top_spine
|
|
504
|
-
if hasattr(behavior, "hide_right_spine"):
|
|
505
|
-
result["behavior_hide_right_spine"] = behavior.hide_right_spine
|
|
506
|
-
if hasattr(behavior, "grid"):
|
|
507
|
-
result["behavior_grid"] = behavior.grid
|
|
508
|
-
if hasattr(behavior, "auto_scale_axes"):
|
|
509
|
-
result["behavior_auto_scale_axes"] = behavior.auto_scale_axes
|
|
510
|
-
if hasattr(behavior, "constrained_layout"):
|
|
511
|
-
result["behavior_constrained_layout"] = behavior.constrained_layout
|
|
512
|
-
|
|
513
|
-
# Legacy key aliases for backwards compatibility
|
|
514
|
-
# (These allow existing code using old keys to still work)
|
|
515
|
-
result["margin_left_mm"] = result["margins_left_mm"]
|
|
516
|
-
result["margin_right_mm"] = result["margins_right_mm"]
|
|
517
|
-
result["margin_bottom_mm"] = result["margins_bottom_mm"]
|
|
518
|
-
result["margin_top_mm"] = result["margins_top_mm"]
|
|
519
|
-
result["space_w_mm"] = result["spacing_horizontal_mm"]
|
|
520
|
-
result["space_h_mm"] = result["spacing_vertical_mm"]
|
|
521
|
-
result["tick_length_mm"] = result["ticks_length_mm"]
|
|
522
|
-
result["tick_thickness_mm"] = result["ticks_thickness_mm"]
|
|
523
|
-
result["n_ticks"] = result["ticks_n_ticks"]
|
|
524
|
-
result["trace_thickness_mm"] = result["lines_trace_mm"]
|
|
525
|
-
result["marker_size_mm"] = result["markers_size_mm"]
|
|
526
|
-
result["font_family"] = result["fonts_family"]
|
|
527
|
-
result["axis_font_size_pt"] = result["fonts_axis_label_pt"]
|
|
528
|
-
result["tick_font_size_pt"] = result["fonts_tick_label_pt"]
|
|
529
|
-
result["title_font_size_pt"] = result["fonts_title_pt"]
|
|
530
|
-
result["suptitle_font_size_pt"] = result["fonts_suptitle_pt"]
|
|
531
|
-
result["legend_font_size_pt"] = result["fonts_legend_pt"]
|
|
532
|
-
result["label_pad_pt"] = result["padding_label_pt"]
|
|
533
|
-
result["tick_pad_pt"] = result["padding_tick_pt"]
|
|
534
|
-
result["title_pad_pt"] = result["padding_title_pt"]
|
|
535
|
-
result["dpi"] = result["output_dpi"]
|
|
536
|
-
result["theme"] = result["theme_mode"]
|
|
537
|
-
result["hide_top_spine"] = result.get("behavior_hide_top_spine", True)
|
|
538
|
-
result["hide_right_spine"] = result.get("behavior_hide_right_spine", True)
|
|
539
|
-
result["grid"] = result.get("behavior_grid", False)
|
|
540
|
-
result["auto_scale_axes"] = result.get("behavior_auto_scale_axes", True)
|
|
541
|
-
result["constrained_layout"] = result.get("behavior_constrained_layout", False)
|
|
542
|
-
|
|
543
|
-
return result
|
|
362
|
+
global _STYLE_CACHE
|
|
363
|
+
if _STYLE_CACHE is None:
|
|
364
|
+
return {}
|
|
365
|
+
return to_subplots_kwargs(_STYLE_CACHE)
|
|
544
366
|
|
|
545
367
|
|
|
546
368
|
# Lazy-loaded global STYLE object
|
|
@@ -576,3 +398,5 @@ if __name__ == "__main__":
|
|
|
576
398
|
|
|
577
399
|
print("\nUsing STYLE proxy...")
|
|
578
400
|
print(f" STYLE.fonts.family: {STYLE.fonts.family}")
|
|
401
|
+
|
|
402
|
+
# EOF
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Theme color utilities for figrecipe styles."""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, Optional
|
|
6
|
+
|
|
7
|
+
from matplotlib.axes import Axes
|
|
8
|
+
|
|
9
|
+
# Default theme color palettes (Monaco/VS Code style for dark)
|
|
10
|
+
THEME_COLORS = {
|
|
11
|
+
"dark": {
|
|
12
|
+
"figure_bg": "#1e1e1e", # VS Code main background
|
|
13
|
+
"axes_bg": "#252526", # VS Code panel background
|
|
14
|
+
"legend_bg": "#252526", # Same as axes
|
|
15
|
+
"text": "#d4d4d4", # VS Code default text
|
|
16
|
+
"spine": "#3c3c3c", # Subtle border color
|
|
17
|
+
"tick": "#d4d4d4", # Match text
|
|
18
|
+
"grid": "#3a3a3a", # Subtle grid
|
|
19
|
+
},
|
|
20
|
+
"light": {
|
|
21
|
+
"figure_bg": "none", # Transparent
|
|
22
|
+
"axes_bg": "none", # Transparent
|
|
23
|
+
"legend_bg": "none", # Transparent
|
|
24
|
+
"text": "black",
|
|
25
|
+
"spine": "black",
|
|
26
|
+
"tick": "black",
|
|
27
|
+
"grid": "#cccccc",
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def apply_theme_colors(
|
|
33
|
+
ax: Axes,
|
|
34
|
+
theme: str = "light",
|
|
35
|
+
custom_colors: Optional[Dict[str, str]] = None,
|
|
36
|
+
) -> None:
|
|
37
|
+
"""Apply theme colors to axes for dark/light mode support.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
ax : matplotlib.axes.Axes
|
|
42
|
+
Target axes to apply theme to
|
|
43
|
+
theme : str or dict
|
|
44
|
+
Color theme: "light" or "dark" (default: "light")
|
|
45
|
+
If dict, extracts 'mode' key (for YAML-style theme dicts)
|
|
46
|
+
custom_colors : dict, optional
|
|
47
|
+
Custom color overrides. Keys: figure_bg, axes_bg, legend_bg, text, spine, tick, grid
|
|
48
|
+
|
|
49
|
+
Examples
|
|
50
|
+
--------
|
|
51
|
+
>>> fig, ax = plt.subplots()
|
|
52
|
+
>>> apply_theme_colors(ax, theme="dark") # Eye-friendly dark mode
|
|
53
|
+
"""
|
|
54
|
+
# Handle dict-style theme (from YAML: {mode: "light", dark: {...}})
|
|
55
|
+
if isinstance(theme, dict):
|
|
56
|
+
theme = theme.get("mode", "light")
|
|
57
|
+
|
|
58
|
+
# Ensure theme is a string
|
|
59
|
+
if not isinstance(theme, str):
|
|
60
|
+
theme = "light"
|
|
61
|
+
|
|
62
|
+
# Get base theme colors
|
|
63
|
+
colors = THEME_COLORS.get(theme, THEME_COLORS["light"]).copy()
|
|
64
|
+
|
|
65
|
+
# Apply custom overrides
|
|
66
|
+
if custom_colors:
|
|
67
|
+
# Handle legacy key name (background -> figure_bg)
|
|
68
|
+
if "background" in custom_colors and "figure_bg" not in custom_colors:
|
|
69
|
+
custom_colors["figure_bg"] = custom_colors.pop("background")
|
|
70
|
+
colors.update(custom_colors)
|
|
71
|
+
|
|
72
|
+
# Helper to check for transparent/none
|
|
73
|
+
def is_transparent(color):
|
|
74
|
+
if color is None:
|
|
75
|
+
return False
|
|
76
|
+
return str(color).lower() in ("none", "transparent")
|
|
77
|
+
|
|
78
|
+
# Apply axes background (handle "none"/"transparent" for transparency)
|
|
79
|
+
axes_bg = colors.get("axes_bg", "none")
|
|
80
|
+
if is_transparent(axes_bg):
|
|
81
|
+
ax.set_facecolor("none")
|
|
82
|
+
ax.patch.set_alpha(0)
|
|
83
|
+
else:
|
|
84
|
+
ax.set_facecolor(axes_bg)
|
|
85
|
+
|
|
86
|
+
# Apply figure background if accessible
|
|
87
|
+
fig = ax.get_figure()
|
|
88
|
+
if fig is not None:
|
|
89
|
+
fig_bg = colors.get("figure_bg", "none")
|
|
90
|
+
if is_transparent(fig_bg):
|
|
91
|
+
fig.patch.set_facecolor("none")
|
|
92
|
+
fig.patch.set_alpha(0)
|
|
93
|
+
else:
|
|
94
|
+
fig.patch.set_facecolor(fig_bg)
|
|
95
|
+
|
|
96
|
+
# Apply text colors to figure-level text elements
|
|
97
|
+
if hasattr(fig, "_suptitle") and fig._suptitle is not None:
|
|
98
|
+
fig._suptitle.set_color(colors["text"])
|
|
99
|
+
if hasattr(fig, "_supxlabel") and fig._supxlabel is not None:
|
|
100
|
+
fig._supxlabel.set_color(colors["text"])
|
|
101
|
+
if hasattr(fig, "_supylabel") and fig._supylabel is not None:
|
|
102
|
+
fig._supylabel.set_color(colors["text"])
|
|
103
|
+
|
|
104
|
+
# Apply text colors (labels, titles)
|
|
105
|
+
ax.xaxis.label.set_color(colors["text"])
|
|
106
|
+
ax.yaxis.label.set_color(colors["text"])
|
|
107
|
+
ax.title.set_color(colors["text"])
|
|
108
|
+
|
|
109
|
+
# Update rcParams for dark mode support (pie charts, panel labels)
|
|
110
|
+
import matplotlib as mpl
|
|
111
|
+
|
|
112
|
+
mpl.rcParams["text.color"] = colors["text"]
|
|
113
|
+
mpl.rcParams["axes.labelcolor"] = colors["text"]
|
|
114
|
+
mpl.rcParams["xtick.color"] = colors["tick"]
|
|
115
|
+
mpl.rcParams["ytick.color"] = colors["tick"]
|
|
116
|
+
|
|
117
|
+
# Apply spine colors
|
|
118
|
+
for spine in ax.spines.values():
|
|
119
|
+
spine.set_color(colors["spine"])
|
|
120
|
+
|
|
121
|
+
# Apply tick colors (both marks and labels)
|
|
122
|
+
ax.tick_params(colors=colors["tick"], which="both")
|
|
123
|
+
for label in ax.get_xticklabels() + ax.get_yticklabels():
|
|
124
|
+
label.set_color(colors["tick"])
|
|
125
|
+
|
|
126
|
+
# Apply text colors to all text objects (panel labels, pie labels, annotations)
|
|
127
|
+
for text in ax.texts:
|
|
128
|
+
text.set_color(colors["text"])
|
|
129
|
+
|
|
130
|
+
# Apply legend colors if legend exists
|
|
131
|
+
legend = ax.get_legend()
|
|
132
|
+
if legend is not None:
|
|
133
|
+
for text in legend.get_texts():
|
|
134
|
+
text.set_color(colors["text"])
|
|
135
|
+
title = legend.get_title()
|
|
136
|
+
if title:
|
|
137
|
+
title.set_color(colors["text"])
|
|
138
|
+
frame = legend.get_frame()
|
|
139
|
+
if frame:
|
|
140
|
+
legend_bg = colors.get("legend_bg", colors.get("axes_bg", "none"))
|
|
141
|
+
if is_transparent(legend_bg):
|
|
142
|
+
frame.set_facecolor("none")
|
|
143
|
+
frame.set_alpha(0)
|
|
144
|
+
else:
|
|
145
|
+
frame.set_facecolor(legend_bg)
|
|
146
|
+
frame.set_edgecolor(colors["spine"])
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
__all__ = ["THEME_COLORS", "apply_theme_colors"]
|
|
150
|
+
|
|
151
|
+
# EOF
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Timestamp: "2025-12-
|
|
1
|
+
# Timestamp: "2025-12-24 14:11:46 (ywatanabe)"
|
|
2
2
|
# File: ./src/figrecipe/styles/presets/SCITEX.yaml
|
|
3
3
|
# FIGRECIPE Style Preset
|
|
4
4
|
# ======================
|
|
@@ -49,7 +49,8 @@ ticks:
|
|
|
49
49
|
length_mm: 0.8
|
|
50
50
|
thickness_mm: 0.2
|
|
51
51
|
direction: "out"
|
|
52
|
-
|
|
52
|
+
n_ticks_min: 3
|
|
53
|
+
n_ticks_max: 4
|
|
53
54
|
|
|
54
55
|
markers:
|
|
55
56
|
size_mm: 0.8
|
|
@@ -85,6 +86,8 @@ histogram:
|
|
|
85
86
|
pie:
|
|
86
87
|
text_pt: 6 # Pie chart text size (labels, autopct)
|
|
87
88
|
show_axes: false # Hide axes for pie charts
|
|
89
|
+
edge_color: "black" # Wedge edge color (black for contrast)
|
|
90
|
+
edge_mm: 0.2 # Wedge edge thickness
|
|
88
91
|
|
|
89
92
|
imshow:
|
|
90
93
|
show_axes: false # Hide ticks, ticklabels, spines for imshow
|
|
@@ -120,6 +123,7 @@ behavior:
|
|
|
120
123
|
hide_right_spine: true
|
|
121
124
|
grid: false
|
|
122
125
|
constrained_layout: true # Auto-spacing for suptitle/supxlabel/supylabel
|
|
126
|
+
panel_labels: true # Auto-add A, B, C labels to multi-panel figures
|
|
123
127
|
|
|
124
128
|
theme:
|
|
125
129
|
mode: "light"
|
|
@@ -152,25 +156,26 @@ colors:
|
|
|
152
156
|
- [228, 94, 50] # orange
|
|
153
157
|
- [255, 150, 200] # pink
|
|
154
158
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
159
|
+
rgb:
|
|
160
|
+
# Named colors
|
|
161
|
+
- white: [255, 255, 255]
|
|
162
|
+
- black: [0, 0, 0]
|
|
163
|
+
- blue: [0, 128, 192]
|
|
164
|
+
- red: [255, 70, 50]
|
|
165
|
+
- pink: [255, 150, 200]
|
|
166
|
+
- green: [20, 180, 20]
|
|
167
|
+
- yellow: [230, 160, 20]
|
|
168
|
+
- gray: [128, 128, 128]
|
|
169
|
+
- grey: [128, 128, 128]
|
|
170
|
+
- purple: [200, 50, 255]
|
|
171
|
+
- lightblue: [20, 200, 200]
|
|
172
|
+
- brown: [128, 0, 0]
|
|
173
|
+
- navy: [0, 0, 100]
|
|
174
|
+
- orange: [228, 94, 50]
|
|
175
|
+
|
|
176
|
+
# Semantic
|
|
177
|
+
- primary: [0, 128, 192]
|
|
178
|
+
- secondary: [255, 70, 50]
|
|
179
|
+
- accent: [20, 180, 20]
|
|
180
|
+
|
|
181
|
+
# EOF
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: figrecipe
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.4
|
|
4
4
|
Summary: Reproducible matplotlib wrapper with mm-precision layouts
|
|
5
5
|
Project-URL: Homepage, https://github.com/ywatanabe1989/figrecipe
|
|
6
6
|
Project-URL: Documentation, https://github.com/ywatanabe1989/figrecipe#readme
|
|
@@ -53,7 +53,9 @@ Description-Content-Type: text/markdown
|
|
|
53
53
|
!-- --- -->
|
|
54
54
|
|
|
55
55
|
<p align="center">
|
|
56
|
-
<
|
|
56
|
+
<a href="https://scitex.ai" target="_blank">
|
|
57
|
+
<img src="docs/figrecipe_logo.png" alt="figrecipe logo" width="200"/>
|
|
58
|
+
</a>
|
|
57
59
|
</p>
|
|
58
60
|
|
|
59
61
|
# FigRecipe
|
|
@@ -112,6 +114,31 @@ The notebook includes side-by-side comparisons of original and reproduced figure
|
|
|
112
114
|
|
|
113
115
|
Source: [examples/figrecipe_demo.ipynb](examples/figrecipe_demo.ipynb)
|
|
114
116
|
|
|
117
|
+
### Supported Plot Types
|
|
118
|
+
|
|
119
|
+
FigRecipe supports 46 matplotlib plot types, organized into 9 categories:
|
|
120
|
+
|
|
121
|
+
<details>
|
|
122
|
+
<summary><b>All Plot Types</b> (click to expand)</summary>
|
|
123
|
+
<p align="center">
|
|
124
|
+
<img src="docs/images/plot_types/all_plot_types.png" alt="All Plot Types" width="100%"/>
|
|
125
|
+
</p>
|
|
126
|
+
</details>
|
|
127
|
+
|
|
128
|
+
| Line & Curve | Scatter | Distribution |
|
|
129
|
+
|:---:|:---:|:---:|
|
|
130
|
+
|  |  |  |
|
|
131
|
+
|
|
132
|
+
| Bar & Categorical | Contour & Surface | 2D/Image/Matrix |
|
|
133
|
+
|:---:|:---:|:---:|
|
|
134
|
+
|  |  |  |
|
|
135
|
+
|
|
136
|
+
| Vector & Flow | Spectral & Signal | Special |
|
|
137
|
+
|:---:|:---:|:---:|
|
|
138
|
+
|  |  |  |
|
|
139
|
+
|
|
140
|
+
Generate all plots: `python examples/demo_plot_all.py`
|
|
141
|
+
|
|
115
142
|
## Installation
|
|
116
143
|
|
|
117
144
|
```bash
|
|
@@ -216,6 +243,10 @@ overrides = fr.edit(fig, port=5050)
|
|
|
216
243
|
# Apply overrides to future figures or save to custom YAML
|
|
217
244
|
```
|
|
218
245
|
|
|
246
|
+
<p align="center">
|
|
247
|
+
<img src="docs/images/gui_editor_demo.png" alt="FigRecipe GUI Editor" width="100%"/>
|
|
248
|
+
</p>
|
|
249
|
+
|
|
219
250
|
The editor provides:
|
|
220
251
|
- **Live preview** with real-time style updates
|
|
221
252
|
- **Theme switching** between SCITEX/MATPLOTLIB presets
|
|
@@ -224,6 +255,10 @@ The editor provides:
|
|
|
224
255
|
- **Download** in PNG, SVG, PDF formats
|
|
225
256
|
- **Export** updated recipe YAML
|
|
226
257
|
|
|
258
|
+
| Axis Properties | Download Options | Dark Mode |
|
|
259
|
+
|:---:|:---:|:---:|
|
|
260
|
+
|  |  |  |
|
|
261
|
+
|
|
227
262
|
### Style Format (YAML)
|
|
228
263
|
|
|
229
264
|
``` yaml
|