figrecipe 0.6.0__py3-none-any.whl → 0.9.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.
Files changed (269) hide show
  1. figrecipe/__init__.py +161 -1030
  2. figrecipe/__main__.py +12 -0
  3. figrecipe/_api/__init__.py +48 -0
  4. figrecipe/_api/_extract.py +108 -0
  5. figrecipe/_api/_notebook.py +61 -0
  6. figrecipe/_api/_panel.py +113 -0
  7. figrecipe/_api/_save.py +287 -0
  8. figrecipe/_api/_seaborn_proxy.py +34 -0
  9. figrecipe/_api/_style_manager.py +153 -0
  10. figrecipe/_api/_subplots.py +333 -0
  11. figrecipe/_api/_validate.py +82 -0
  12. figrecipe/_cli/__init__.py +7 -0
  13. figrecipe/_cli/_compose.py +87 -0
  14. figrecipe/_cli/_convert.py +117 -0
  15. figrecipe/_cli/_crop.py +82 -0
  16. figrecipe/_cli/_edit.py +70 -0
  17. figrecipe/_cli/_extract.py +128 -0
  18. figrecipe/_cli/_fonts.py +47 -0
  19. figrecipe/_cli/_info.py +67 -0
  20. figrecipe/_cli/_main.py +58 -0
  21. figrecipe/_cli/_reproduce.py +79 -0
  22. figrecipe/_cli/_style.py +77 -0
  23. figrecipe/_cli/_validate.py +66 -0
  24. figrecipe/_cli/_version.py +50 -0
  25. figrecipe/_composition/__init__.py +32 -0
  26. figrecipe/_composition/_alignment.py +452 -0
  27. figrecipe/_composition/_compose.py +179 -0
  28. figrecipe/_composition/_import_axes.py +127 -0
  29. figrecipe/_composition/_visibility.py +125 -0
  30. figrecipe/_dev/__init__.py +4 -93
  31. figrecipe/_dev/_plotters.py +76 -0
  32. figrecipe/_dev/_run_demos.py +56 -0
  33. figrecipe/_dev/browser/__init__.py +69 -0
  34. figrecipe/_dev/browser/_audio.py +240 -0
  35. figrecipe/_dev/browser/_caption.py +356 -0
  36. figrecipe/_dev/browser/_click_effect.py +146 -0
  37. figrecipe/_dev/browser/_cursor.py +196 -0
  38. figrecipe/_dev/browser/_highlight.py +105 -0
  39. figrecipe/_dev/browser/_narration.py +237 -0
  40. figrecipe/_dev/browser/_recorder.py +446 -0
  41. figrecipe/_dev/browser/_utils.py +178 -0
  42. figrecipe/_dev/browser/_video_trim/__init__.py +152 -0
  43. figrecipe/_dev/browser/_video_trim/_detection.py +223 -0
  44. figrecipe/_dev/browser/_video_trim/_markers.py +140 -0
  45. figrecipe/_dev/demo_plotters/__init__.py +35 -166
  46. figrecipe/_dev/demo_plotters/_categories.py +81 -0
  47. figrecipe/_dev/demo_plotters/_figure_creators.py +119 -0
  48. figrecipe/_dev/demo_plotters/_helpers.py +31 -0
  49. figrecipe/_dev/demo_plotters/_registry.py +50 -0
  50. figrecipe/_dev/demo_plotters/bar_categorical/__init__.py +4 -0
  51. figrecipe/_dev/demo_plotters/contour_surface/__init__.py +4 -0
  52. figrecipe/_dev/demo_plotters/distribution/__init__.py +4 -0
  53. figrecipe/_dev/demo_plotters/image_matrix/__init__.py +4 -0
  54. figrecipe/_dev/demo_plotters/line_curve/__init__.py +4 -0
  55. figrecipe/_dev/demo_plotters/{plot_plot.py → line_curve/plot_plot.py} +3 -2
  56. figrecipe/_dev/demo_plotters/scatter_points/__init__.py +4 -0
  57. figrecipe/_dev/demo_plotters/special/__init__.py +4 -0
  58. figrecipe/_dev/demo_plotters/{plot_pie.py → special/plot_pie.py} +5 -1
  59. figrecipe/_dev/demo_plotters/spectral_signal/__init__.py +4 -0
  60. figrecipe/_dev/demo_plotters/vector_flow/__init__.py +4 -0
  61. figrecipe/_editor/__init__.py +61 -13
  62. figrecipe/_editor/_bbox/__init__.py +43 -0
  63. figrecipe/_editor/_bbox/_collections.py +177 -0
  64. figrecipe/_editor/_bbox/_elements.py +159 -0
  65. figrecipe/_editor/_bbox/_extract.py +402 -0
  66. figrecipe/_editor/_bbox/_extract_axes.py +370 -0
  67. figrecipe/_editor/_bbox/_extract_text.py +466 -0
  68. figrecipe/_editor/_bbox/_lines.py +173 -0
  69. figrecipe/_editor/_bbox/_transforms.py +146 -0
  70. figrecipe/_editor/_call_overrides.py +183 -0
  71. figrecipe/_editor/_datatable_plot_handlers.py +249 -0
  72. figrecipe/_editor/_figure_layout.py +211 -0
  73. figrecipe/_editor/_flask_app.py +200 -1030
  74. figrecipe/_editor/_helpers.py +251 -0
  75. figrecipe/_editor/_hitmap/__init__.py +76 -0
  76. figrecipe/_editor/_hitmap/_artists/__init__.py +21 -0
  77. figrecipe/_editor/_hitmap/_artists/_collections.py +345 -0
  78. figrecipe/_editor/_hitmap/_artists/_images.py +68 -0
  79. figrecipe/_editor/_hitmap/_artists/_lines.py +107 -0
  80. figrecipe/_editor/_hitmap/_artists/_patches.py +163 -0
  81. figrecipe/_editor/_hitmap/_artists/_text.py +190 -0
  82. figrecipe/_editor/_hitmap/_colors.py +181 -0
  83. figrecipe/_editor/_hitmap/_detect.py +194 -0
  84. figrecipe/_editor/_hitmap/_restore.py +154 -0
  85. figrecipe/_editor/_hitmap_main.py +182 -0
  86. figrecipe/_editor/_overrides.py +4 -1
  87. figrecipe/_editor/_plot_types_registry.py +190 -0
  88. figrecipe/_editor/_preferences.py +135 -0
  89. figrecipe/_editor/_render_overrides.py +507 -0
  90. figrecipe/_editor/_renderer.py +81 -186
  91. figrecipe/_editor/_routes_annotation.py +114 -0
  92. figrecipe/_editor/_routes_axis.py +482 -0
  93. figrecipe/_editor/_routes_captions.py +130 -0
  94. figrecipe/_editor/_routes_composition.py +270 -0
  95. figrecipe/_editor/_routes_core.py +126 -0
  96. figrecipe/_editor/_routes_datatable.py +364 -0
  97. figrecipe/_editor/_routes_element.py +335 -0
  98. figrecipe/_editor/_routes_files.py +443 -0
  99. figrecipe/_editor/_routes_image.py +200 -0
  100. figrecipe/_editor/_routes_snapshot.py +94 -0
  101. figrecipe/_editor/_routes_style.py +243 -0
  102. figrecipe/_editor/_templates/__init__.py +116 -1
  103. figrecipe/_editor/_templates/_html.py +154 -64
  104. figrecipe/_editor/_templates/_html_components/__init__.py +13 -0
  105. figrecipe/_editor/_templates/_html_components/_composition_toolbar.py +79 -0
  106. figrecipe/_editor/_templates/_html_components/_file_browser.py +41 -0
  107. figrecipe/_editor/_templates/_html_datatable.py +92 -0
  108. figrecipe/_editor/_templates/_scripts/__init__.py +178 -0
  109. figrecipe/_editor/_templates/_scripts/_accordion.py +328 -0
  110. figrecipe/_editor/_templates/_scripts/_annotation_drag.py +504 -0
  111. figrecipe/_editor/_templates/_scripts/_api.py +228 -0
  112. figrecipe/_editor/_templates/_scripts/_canvas_context_menu.py +182 -0
  113. figrecipe/_editor/_templates/_scripts/_captions.py +231 -0
  114. figrecipe/_editor/_templates/_scripts/_colors.py +485 -0
  115. figrecipe/_editor/_templates/_scripts/_composition.py +283 -0
  116. figrecipe/_editor/_templates/_scripts/_core.py +493 -0
  117. figrecipe/_editor/_templates/_scripts/_datatable/__init__.py +59 -0
  118. figrecipe/_editor/_templates/_scripts/_datatable/_cell_edit.py +97 -0
  119. figrecipe/_editor/_templates/_scripts/_datatable/_clipboard.py +164 -0
  120. figrecipe/_editor/_templates/_scripts/_datatable/_context_menu.py +221 -0
  121. figrecipe/_editor/_templates/_scripts/_datatable/_core.py +150 -0
  122. figrecipe/_editor/_templates/_scripts/_datatable/_editable.py +511 -0
  123. figrecipe/_editor/_templates/_scripts/_datatable/_import.py +161 -0
  124. figrecipe/_editor/_templates/_scripts/_datatable/_plot.py +261 -0
  125. figrecipe/_editor/_templates/_scripts/_datatable/_selection.py +438 -0
  126. figrecipe/_editor/_templates/_scripts/_datatable/_table.py +256 -0
  127. figrecipe/_editor/_templates/_scripts/_datatable/_tabs.py +354 -0
  128. figrecipe/_editor/_templates/_scripts/_debug_snapshot.py +186 -0
  129. figrecipe/_editor/_templates/_scripts/_element_editor.py +325 -0
  130. figrecipe/_editor/_templates/_scripts/_files.py +429 -0
  131. figrecipe/_editor/_templates/_scripts/_files_context_menu.py +240 -0
  132. figrecipe/_editor/_templates/_scripts/_hitmap.py +512 -0
  133. figrecipe/_editor/_templates/_scripts/_image_drop.py +428 -0
  134. figrecipe/_editor/_templates/_scripts/_inspector.py +315 -0
  135. figrecipe/_editor/_templates/_scripts/_labels.py +464 -0
  136. figrecipe/_editor/_templates/_scripts/_legend_drag.py +270 -0
  137. figrecipe/_editor/_templates/_scripts/_modals.py +226 -0
  138. figrecipe/_editor/_templates/_scripts/_multi_select.py +198 -0
  139. figrecipe/_editor/_templates/_scripts/_overlays.py +292 -0
  140. figrecipe/_editor/_templates/_scripts/_panel_drag.py +505 -0
  141. figrecipe/_editor/_templates/_scripts/_panel_drag_snapshot.py +33 -0
  142. figrecipe/_editor/_templates/_scripts/_panel_position.py +463 -0
  143. figrecipe/_editor/_templates/_scripts/_panel_resize.py +230 -0
  144. figrecipe/_editor/_templates/_scripts/_panel_snap.py +307 -0
  145. figrecipe/_editor/_templates/_scripts/_region_select.py +255 -0
  146. figrecipe/_editor/_templates/_scripts/_selection.py +244 -0
  147. figrecipe/_editor/_templates/_scripts/_sync.py +242 -0
  148. figrecipe/_editor/_templates/_scripts/_tabs.py +89 -0
  149. figrecipe/_editor/_templates/_scripts/_undo_redo.py +348 -0
  150. figrecipe/_editor/_templates/_scripts/_view_mode.py +107 -0
  151. figrecipe/_editor/_templates/_scripts/_zoom.py +212 -0
  152. figrecipe/_editor/_templates/_styles/__init__.py +78 -0
  153. figrecipe/_editor/_templates/_styles/_base.py +111 -0
  154. figrecipe/_editor/_templates/_styles/_buttons.py +327 -0
  155. figrecipe/_editor/_templates/_styles/_color_input.py +123 -0
  156. figrecipe/_editor/_templates/_styles/_composition.py +87 -0
  157. figrecipe/_editor/_templates/_styles/_controls.py +430 -0
  158. figrecipe/_editor/_templates/_styles/_datatable/__init__.py +40 -0
  159. figrecipe/_editor/_templates/_styles/_datatable/_editable.py +203 -0
  160. figrecipe/_editor/_templates/_styles/_datatable/_panel.py +268 -0
  161. figrecipe/_editor/_templates/_styles/_datatable/_table.py +479 -0
  162. figrecipe/_editor/_templates/_styles/_datatable/_toolbar.py +384 -0
  163. figrecipe/_editor/_templates/_styles/_datatable/_vars.py +123 -0
  164. figrecipe/_editor/_templates/_styles/_dynamic_props.py +144 -0
  165. figrecipe/_editor/_templates/_styles/_file_browser.py +466 -0
  166. figrecipe/_editor/_templates/_styles/_forms.py +224 -0
  167. figrecipe/_editor/_templates/_styles/_hitmap.py +191 -0
  168. figrecipe/_editor/_templates/_styles/_inspector.py +90 -0
  169. figrecipe/_editor/_templates/_styles/_labels.py +118 -0
  170. figrecipe/_editor/_templates/_styles/_modals.py +127 -0
  171. figrecipe/_editor/_templates/_styles/_overlays.py +130 -0
  172. figrecipe/_editor/_templates/_styles/_preview.py +430 -0
  173. figrecipe/_editor/_templates/_styles/_selection.py +73 -0
  174. figrecipe/_editor/_templates/_styles/_spinner.py +117 -0
  175. figrecipe/_editor/static/audio/click.mp3 +0 -0
  176. figrecipe/_editor/static/click.mp3 +0 -0
  177. figrecipe/_editor/static/icons/favicon.ico +0 -0
  178. figrecipe/_integrations/__init__.py +17 -0
  179. figrecipe/_integrations/_scitex_stats.py +298 -0
  180. figrecipe/_params/_DECORATION_METHODS.py +8 -0
  181. figrecipe/_recorder.py +63 -109
  182. figrecipe/_recorder_utils.py +124 -0
  183. figrecipe/_reproducer/__init__.py +18 -0
  184. figrecipe/_reproducer/_core.py +509 -0
  185. figrecipe/_reproducer/_custom_plots.py +279 -0
  186. figrecipe/_reproducer/_seaborn.py +100 -0
  187. figrecipe/_reproducer/_violin.py +186 -0
  188. figrecipe/_signatures/_kwargs.py +273 -0
  189. figrecipe/_signatures/_loader.py +21 -423
  190. figrecipe/_signatures/_parsing.py +147 -0
  191. figrecipe/_utils/__init__.py +3 -0
  192. figrecipe/_utils/_bundle.py +205 -0
  193. figrecipe/_wrappers/_axes.py +252 -895
  194. figrecipe/_wrappers/_axes_helpers.py +136 -0
  195. figrecipe/_wrappers/_axes_plots.py +418 -0
  196. figrecipe/_wrappers/_axes_seaborn.py +157 -0
  197. figrecipe/_wrappers/_caption_generator.py +218 -0
  198. figrecipe/_wrappers/_figure.py +188 -1
  199. figrecipe/_wrappers/_panel_labels.py +127 -0
  200. figrecipe/_wrappers/_plot_helpers.py +143 -0
  201. figrecipe/_wrappers/_stat_annotation.py +274 -0
  202. figrecipe/_wrappers/_violin_helpers.py +180 -0
  203. figrecipe/styles/__init__.py +8 -6
  204. figrecipe/styles/_dotdict.py +72 -0
  205. figrecipe/styles/_finalize.py +134 -0
  206. figrecipe/styles/_fonts.py +77 -0
  207. figrecipe/styles/_kwargs_converter.py +178 -0
  208. figrecipe/styles/_plot_styles.py +209 -0
  209. figrecipe/styles/_style_applier.py +42 -480
  210. figrecipe/styles/_style_loader.py +16 -192
  211. figrecipe/styles/_themes.py +151 -0
  212. figrecipe/styles/presets/MATPLOTLIB.yaml +2 -1
  213. figrecipe/styles/presets/SCITEX.yaml +40 -28
  214. figrecipe-0.9.0.dist-info/METADATA +427 -0
  215. figrecipe-0.9.0.dist-info/RECORD +277 -0
  216. figrecipe-0.9.0.dist-info/entry_points.txt +2 -0
  217. figrecipe/_editor/_bbox.py +0 -978
  218. figrecipe/_editor/_hitmap.py +0 -937
  219. figrecipe/_editor/_templates/_scripts.py +0 -2778
  220. figrecipe/_editor/_templates/_styles.py +0 -1326
  221. figrecipe/_reproducer.py +0 -975
  222. figrecipe-0.6.0.dist-info/METADATA +0 -394
  223. figrecipe-0.6.0.dist-info/RECORD +0 -90
  224. /figrecipe/_dev/demo_plotters/{plot_bar.py → bar_categorical/plot_bar.py} +0 -0
  225. /figrecipe/_dev/demo_plotters/{plot_barh.py → bar_categorical/plot_barh.py} +0 -0
  226. /figrecipe/_dev/demo_plotters/{plot_contour.py → contour_surface/plot_contour.py} +0 -0
  227. /figrecipe/_dev/demo_plotters/{plot_contourf.py → contour_surface/plot_contourf.py} +0 -0
  228. /figrecipe/_dev/demo_plotters/{plot_tricontour.py → contour_surface/plot_tricontour.py} +0 -0
  229. /figrecipe/_dev/demo_plotters/{plot_tricontourf.py → contour_surface/plot_tricontourf.py} +0 -0
  230. /figrecipe/_dev/demo_plotters/{plot_tripcolor.py → contour_surface/plot_tripcolor.py} +0 -0
  231. /figrecipe/_dev/demo_plotters/{plot_triplot.py → contour_surface/plot_triplot.py} +0 -0
  232. /figrecipe/_dev/demo_plotters/{plot_boxplot.py → distribution/plot_boxplot.py} +0 -0
  233. /figrecipe/_dev/demo_plotters/{plot_ecdf.py → distribution/plot_ecdf.py} +0 -0
  234. /figrecipe/_dev/demo_plotters/{plot_hist.py → distribution/plot_hist.py} +0 -0
  235. /figrecipe/_dev/demo_plotters/{plot_hist2d.py → distribution/plot_hist2d.py} +0 -0
  236. /figrecipe/_dev/demo_plotters/{plot_violinplot.py → distribution/plot_violinplot.py} +0 -0
  237. /figrecipe/_dev/demo_plotters/{plot_hexbin.py → image_matrix/plot_hexbin.py} +0 -0
  238. /figrecipe/_dev/demo_plotters/{plot_imshow.py → image_matrix/plot_imshow.py} +0 -0
  239. /figrecipe/_dev/demo_plotters/{plot_matshow.py → image_matrix/plot_matshow.py} +0 -0
  240. /figrecipe/_dev/demo_plotters/{plot_pcolor.py → image_matrix/plot_pcolor.py} +0 -0
  241. /figrecipe/_dev/demo_plotters/{plot_pcolormesh.py → image_matrix/plot_pcolormesh.py} +0 -0
  242. /figrecipe/_dev/demo_plotters/{plot_spy.py → image_matrix/plot_spy.py} +0 -0
  243. /figrecipe/_dev/demo_plotters/{plot_errorbar.py → line_curve/plot_errorbar.py} +0 -0
  244. /figrecipe/_dev/demo_plotters/{plot_fill.py → line_curve/plot_fill.py} +0 -0
  245. /figrecipe/_dev/demo_plotters/{plot_fill_between.py → line_curve/plot_fill_between.py} +0 -0
  246. /figrecipe/_dev/demo_plotters/{plot_fill_betweenx.py → line_curve/plot_fill_betweenx.py} +0 -0
  247. /figrecipe/_dev/demo_plotters/{plot_stackplot.py → line_curve/plot_stackplot.py} +0 -0
  248. /figrecipe/_dev/demo_plotters/{plot_stairs.py → line_curve/plot_stairs.py} +0 -0
  249. /figrecipe/_dev/demo_plotters/{plot_step.py → line_curve/plot_step.py} +0 -0
  250. /figrecipe/_dev/demo_plotters/{plot_scatter.py → scatter_points/plot_scatter.py} +0 -0
  251. /figrecipe/_dev/demo_plotters/{plot_eventplot.py → special/plot_eventplot.py} +0 -0
  252. /figrecipe/_dev/demo_plotters/{plot_loglog.py → special/plot_loglog.py} +0 -0
  253. /figrecipe/_dev/demo_plotters/{plot_semilogx.py → special/plot_semilogx.py} +0 -0
  254. /figrecipe/_dev/demo_plotters/{plot_semilogy.py → special/plot_semilogy.py} +0 -0
  255. /figrecipe/_dev/demo_plotters/{plot_stem.py → special/plot_stem.py} +0 -0
  256. /figrecipe/_dev/demo_plotters/{plot_acorr.py → spectral_signal/plot_acorr.py} +0 -0
  257. /figrecipe/_dev/demo_plotters/{plot_angle_spectrum.py → spectral_signal/plot_angle_spectrum.py} +0 -0
  258. /figrecipe/_dev/demo_plotters/{plot_cohere.py → spectral_signal/plot_cohere.py} +0 -0
  259. /figrecipe/_dev/demo_plotters/{plot_csd.py → spectral_signal/plot_csd.py} +0 -0
  260. /figrecipe/_dev/demo_plotters/{plot_magnitude_spectrum.py → spectral_signal/plot_magnitude_spectrum.py} +0 -0
  261. /figrecipe/_dev/demo_plotters/{plot_phase_spectrum.py → spectral_signal/plot_phase_spectrum.py} +0 -0
  262. /figrecipe/_dev/demo_plotters/{plot_psd.py → spectral_signal/plot_psd.py} +0 -0
  263. /figrecipe/_dev/demo_plotters/{plot_specgram.py → spectral_signal/plot_specgram.py} +0 -0
  264. /figrecipe/_dev/demo_plotters/{plot_xcorr.py → spectral_signal/plot_xcorr.py} +0 -0
  265. /figrecipe/_dev/demo_plotters/{plot_barbs.py → vector_flow/plot_barbs.py} +0 -0
  266. /figrecipe/_dev/demo_plotters/{plot_quiver.py → vector_flow/plot_quiver.py} +0 -0
  267. /figrecipe/_dev/demo_plotters/{plot_streamplot.py → vector_flow/plot_streamplot.py} +0 -0
  268. {figrecipe-0.6.0.dist-info → figrecipe-0.9.0.dist-info}/WHEEL +0 -0
  269. {figrecipe-0.6.0.dist-info → figrecipe-0.9.0.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["DotDict"] = None
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 to_subplots_kwargs(style: Optional[DotDict] = None) -> Dict[str, Any]:
395
- """Convert style DotDict to kwargs for ps.subplots().
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
- Keyword arguments for ps.subplots() with YAML-compatible flattened keys.
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
- if style is None:
417
- style = get_style()
418
-
419
- # YAML-compatible flattened keys (single source of truth)
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
@@ -43,7 +43,8 @@ ticks:
43
43
  length_mm: null
44
44
  thickness_mm: null
45
45
  direction: null
46
- n_ticks: null
46
+ n_ticks_min: null
47
+ n_ticks_max: null
47
48
 
48
49
  markers:
49
50
  size_mm: null
@@ -1,4 +1,4 @@
1
- # Timestamp: "2025-12-22 13:49:08 (ywatanabe)"
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
  # ======================
@@ -11,10 +11,10 @@ axes:
11
11
  thickness_mm: 0.2
12
12
 
13
13
  margins:
14
- left_mm: 6 # Room for supylabel
14
+ left_mm: 1 # Minimal - constrained_layout handles labels
15
15
  right_mm: 1
16
- bottom_mm: 5 # Room for supxlabel
17
- top_mm: 5 # Room for suptitle
16
+ bottom_mm: 1 # Minimal - constrained_layout handles labels
17
+ top_mm: 1 # Minimal - constrained_layout handles labels
18
18
 
19
19
  spacing:
20
20
  horizontal_mm: 10 # Between columns
@@ -49,7 +49,8 @@ ticks:
49
49
  length_mm: 0.8
50
50
  thickness_mm: 0.2
51
51
  direction: "out"
52
- n_ticks: 4
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
@@ -109,6 +112,13 @@ legend:
109
112
  alpha: 1.0 # Transparency
110
113
  loc: "best"
111
114
 
115
+ stat_annotation:
116
+ bracket_height: 0.03 # Height of bracket tips as fraction of y-range
117
+ text_offset: 0.01 # Text offset above bracket as fraction of y-range
118
+ linewidth_mm: 0.2 # Bracket line width in mm
119
+ fontsize_pt: 6 # Font size for annotation text (stars and p-values)
120
+ stars_fontweight: "bold" # Font weight for stars
121
+
112
122
  output:
113
123
  dpi: 300
114
124
  transparent: true
@@ -119,7 +129,8 @@ behavior:
119
129
  hide_top_spine: true
120
130
  hide_right_spine: true
121
131
  grid: false
122
- constrained_layout: true # Auto-spacing for suptitle/supxlabel/supylabel
132
+ constrained_layout: true # Auto-fits content; spacing controls disabled
133
+ panel_labels: true # Auto-add A, B, C labels to multi-panel figures
123
134
 
124
135
  theme:
125
136
  mode: "light"
@@ -152,25 +163,26 @@ colors:
152
163
  - [228, 94, 50] # orange
153
164
  - [255, 150, 200] # pink
154
165
 
155
- # Named colors
156
- white: [255, 255, 255]
157
- black: [0, 0, 0]
158
- blue: [0, 128, 192]
159
- red: [255, 70, 50]
160
- pink: [255, 150, 200]
161
- green: [20, 180, 20]
162
- yellow: [230, 160, 20]
163
- gray: [128, 128, 128]
164
- grey: [128, 128, 128]
165
- purple: [200, 50, 255]
166
- lightblue: [20, 200, 200]
167
- brown: [128, 0, 0]
168
- navy: [0, 0, 100]
169
- orange: [228, 94, 50]
170
-
171
- # Semantic
172
- primary: [0, 128, 192]
173
- secondary: [255, 70, 50]
174
- accent: [20, 180, 20]
175
-
176
- # EOF
166
+ rgb:
167
+ # Named colors
168
+ - white: [255, 255, 255]
169
+ - black: [0, 0, 0]
170
+ - blue: [0, 128, 192]
171
+ - red: [255, 70, 50]
172
+ - pink: [255, 150, 200]
173
+ - green: [20, 180, 20]
174
+ - yellow: [230, 160, 20]
175
+ - gray: [128, 128, 128]
176
+ - grey: [128, 128, 128]
177
+ - purple: [200, 50, 255]
178
+ - lightblue: [20, 200, 200]
179
+ - brown: [128, 0, 0]
180
+ - navy: [0, 0, 100]
181
+ - orange: [228, 94, 50]
182
+
183
+ # Semantic
184
+ - primary: [0, 128, 192]
185
+ - secondary: [255, 70, 50]
186
+ - accent: [20, 180, 20]
187
+
188
+ # EOF