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.
- figrecipe/__init__.py +161 -1030
- figrecipe/__main__.py +12 -0
- figrecipe/_api/__init__.py +48 -0
- figrecipe/_api/_extract.py +108 -0
- figrecipe/_api/_notebook.py +61 -0
- figrecipe/_api/_panel.py +113 -0
- figrecipe/_api/_save.py +287 -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/_cli/__init__.py +7 -0
- figrecipe/_cli/_compose.py +87 -0
- figrecipe/_cli/_convert.py +117 -0
- figrecipe/_cli/_crop.py +82 -0
- figrecipe/_cli/_edit.py +70 -0
- figrecipe/_cli/_extract.py +128 -0
- figrecipe/_cli/_fonts.py +47 -0
- figrecipe/_cli/_info.py +67 -0
- figrecipe/_cli/_main.py +58 -0
- figrecipe/_cli/_reproduce.py +79 -0
- figrecipe/_cli/_style.py +77 -0
- figrecipe/_cli/_validate.py +66 -0
- figrecipe/_cli/_version.py +50 -0
- figrecipe/_composition/__init__.py +32 -0
- figrecipe/_composition/_alignment.py +452 -0
- figrecipe/_composition/_compose.py +179 -0
- figrecipe/_composition/_import_axes.py +127 -0
- figrecipe/_composition/_visibility.py +125 -0
- figrecipe/_dev/__init__.py +4 -93
- figrecipe/_dev/_plotters.py +76 -0
- figrecipe/_dev/_run_demos.py +56 -0
- figrecipe/_dev/browser/__init__.py +69 -0
- figrecipe/_dev/browser/_audio.py +240 -0
- figrecipe/_dev/browser/_caption.py +356 -0
- figrecipe/_dev/browser/_click_effect.py +146 -0
- figrecipe/_dev/browser/_cursor.py +196 -0
- figrecipe/_dev/browser/_highlight.py +105 -0
- figrecipe/_dev/browser/_narration.py +237 -0
- figrecipe/_dev/browser/_recorder.py +446 -0
- figrecipe/_dev/browser/_utils.py +178 -0
- figrecipe/_dev/browser/_video_trim/__init__.py +152 -0
- figrecipe/_dev/browser/_video_trim/_detection.py +223 -0
- figrecipe/_dev/browser/_video_trim/_markers.py +140 -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 +61 -13
- 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 +402 -0
- figrecipe/_editor/_bbox/_extract_axes.py +370 -0
- figrecipe/_editor/_bbox/_extract_text.py +466 -0
- figrecipe/_editor/_bbox/_lines.py +173 -0
- figrecipe/_editor/_bbox/_transforms.py +146 -0
- figrecipe/_editor/_call_overrides.py +183 -0
- figrecipe/_editor/_datatable_plot_handlers.py +249 -0
- figrecipe/_editor/_figure_layout.py +211 -0
- figrecipe/_editor/_flask_app.py +200 -1030
- figrecipe/_editor/_helpers.py +251 -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 +194 -0
- figrecipe/_editor/_hitmap/_restore.py +154 -0
- figrecipe/_editor/_hitmap_main.py +182 -0
- figrecipe/_editor/_overrides.py +4 -1
- figrecipe/_editor/_plot_types_registry.py +190 -0
- figrecipe/_editor/_preferences.py +135 -0
- figrecipe/_editor/_render_overrides.py +507 -0
- figrecipe/_editor/_renderer.py +81 -186
- figrecipe/_editor/_routes_annotation.py +114 -0
- figrecipe/_editor/_routes_axis.py +482 -0
- figrecipe/_editor/_routes_captions.py +130 -0
- figrecipe/_editor/_routes_composition.py +270 -0
- figrecipe/_editor/_routes_core.py +126 -0
- figrecipe/_editor/_routes_datatable.py +364 -0
- figrecipe/_editor/_routes_element.py +335 -0
- figrecipe/_editor/_routes_files.py +443 -0
- figrecipe/_editor/_routes_image.py +200 -0
- figrecipe/_editor/_routes_snapshot.py +94 -0
- figrecipe/_editor/_routes_style.py +243 -0
- figrecipe/_editor/_templates/__init__.py +116 -1
- figrecipe/_editor/_templates/_html.py +154 -64
- figrecipe/_editor/_templates/_html_components/__init__.py +13 -0
- figrecipe/_editor/_templates/_html_components/_composition_toolbar.py +79 -0
- figrecipe/_editor/_templates/_html_components/_file_browser.py +41 -0
- figrecipe/_editor/_templates/_html_datatable.py +92 -0
- figrecipe/_editor/_templates/_scripts/__init__.py +178 -0
- figrecipe/_editor/_templates/_scripts/_accordion.py +328 -0
- figrecipe/_editor/_templates/_scripts/_annotation_drag.py +504 -0
- figrecipe/_editor/_templates/_scripts/_api.py +228 -0
- figrecipe/_editor/_templates/_scripts/_canvas_context_menu.py +182 -0
- figrecipe/_editor/_templates/_scripts/_captions.py +231 -0
- figrecipe/_editor/_templates/_scripts/_colors.py +485 -0
- figrecipe/_editor/_templates/_scripts/_composition.py +283 -0
- figrecipe/_editor/_templates/_scripts/_core.py +493 -0
- figrecipe/_editor/_templates/_scripts/_datatable/__init__.py +59 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_cell_edit.py +97 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_clipboard.py +164 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_context_menu.py +221 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_core.py +150 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_editable.py +511 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_import.py +161 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_plot.py +261 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_selection.py +438 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_table.py +256 -0
- figrecipe/_editor/_templates/_scripts/_datatable/_tabs.py +354 -0
- figrecipe/_editor/_templates/_scripts/_debug_snapshot.py +186 -0
- figrecipe/_editor/_templates/_scripts/_element_editor.py +325 -0
- figrecipe/_editor/_templates/_scripts/_files.py +429 -0
- figrecipe/_editor/_templates/_scripts/_files_context_menu.py +240 -0
- figrecipe/_editor/_templates/_scripts/_hitmap.py +512 -0
- figrecipe/_editor/_templates/_scripts/_image_drop.py +428 -0
- figrecipe/_editor/_templates/_scripts/_inspector.py +315 -0
- figrecipe/_editor/_templates/_scripts/_labels.py +464 -0
- figrecipe/_editor/_templates/_scripts/_legend_drag.py +270 -0
- figrecipe/_editor/_templates/_scripts/_modals.py +226 -0
- figrecipe/_editor/_templates/_scripts/_multi_select.py +198 -0
- figrecipe/_editor/_templates/_scripts/_overlays.py +292 -0
- figrecipe/_editor/_templates/_scripts/_panel_drag.py +505 -0
- figrecipe/_editor/_templates/_scripts/_panel_drag_snapshot.py +33 -0
- figrecipe/_editor/_templates/_scripts/_panel_position.py +463 -0
- figrecipe/_editor/_templates/_scripts/_panel_resize.py +230 -0
- figrecipe/_editor/_templates/_scripts/_panel_snap.py +307 -0
- figrecipe/_editor/_templates/_scripts/_region_select.py +255 -0
- figrecipe/_editor/_templates/_scripts/_selection.py +244 -0
- figrecipe/_editor/_templates/_scripts/_sync.py +242 -0
- figrecipe/_editor/_templates/_scripts/_tabs.py +89 -0
- figrecipe/_editor/_templates/_scripts/_undo_redo.py +348 -0
- figrecipe/_editor/_templates/_scripts/_view_mode.py +107 -0
- figrecipe/_editor/_templates/_scripts/_zoom.py +212 -0
- figrecipe/_editor/_templates/_styles/__init__.py +78 -0
- figrecipe/_editor/_templates/_styles/_base.py +111 -0
- figrecipe/_editor/_templates/_styles/_buttons.py +327 -0
- figrecipe/_editor/_templates/_styles/_color_input.py +123 -0
- figrecipe/_editor/_templates/_styles/_composition.py +87 -0
- figrecipe/_editor/_templates/_styles/_controls.py +430 -0
- figrecipe/_editor/_templates/_styles/_datatable/__init__.py +40 -0
- figrecipe/_editor/_templates/_styles/_datatable/_editable.py +203 -0
- figrecipe/_editor/_templates/_styles/_datatable/_panel.py +268 -0
- figrecipe/_editor/_templates/_styles/_datatable/_table.py +479 -0
- figrecipe/_editor/_templates/_styles/_datatable/_toolbar.py +384 -0
- figrecipe/_editor/_templates/_styles/_datatable/_vars.py +123 -0
- figrecipe/_editor/_templates/_styles/_dynamic_props.py +144 -0
- figrecipe/_editor/_templates/_styles/_file_browser.py +466 -0
- figrecipe/_editor/_templates/_styles/_forms.py +224 -0
- figrecipe/_editor/_templates/_styles/_hitmap.py +191 -0
- figrecipe/_editor/_templates/_styles/_inspector.py +90 -0
- figrecipe/_editor/_templates/_styles/_labels.py +118 -0
- figrecipe/_editor/_templates/_styles/_modals.py +127 -0
- figrecipe/_editor/_templates/_styles/_overlays.py +130 -0
- figrecipe/_editor/_templates/_styles/_preview.py +430 -0
- figrecipe/_editor/_templates/_styles/_selection.py +73 -0
- figrecipe/_editor/_templates/_styles/_spinner.py +117 -0
- figrecipe/_editor/static/audio/click.mp3 +0 -0
- figrecipe/_editor/static/click.mp3 +0 -0
- figrecipe/_editor/static/icons/favicon.ico +0 -0
- figrecipe/_integrations/__init__.py +17 -0
- figrecipe/_integrations/_scitex_stats.py +298 -0
- figrecipe/_params/_DECORATION_METHODS.py +8 -0
- figrecipe/_recorder.py +63 -109
- figrecipe/_recorder_utils.py +124 -0
- figrecipe/_reproducer/__init__.py +18 -0
- figrecipe/_reproducer/_core.py +509 -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/_utils/__init__.py +3 -0
- figrecipe/_utils/_bundle.py +205 -0
- figrecipe/_wrappers/_axes.py +252 -895
- figrecipe/_wrappers/_axes_helpers.py +136 -0
- figrecipe/_wrappers/_axes_plots.py +418 -0
- figrecipe/_wrappers/_axes_seaborn.py +157 -0
- figrecipe/_wrappers/_caption_generator.py +218 -0
- figrecipe/_wrappers/_figure.py +188 -1
- figrecipe/_wrappers/_panel_labels.py +127 -0
- figrecipe/_wrappers/_plot_helpers.py +143 -0
- figrecipe/_wrappers/_stat_annotation.py +274 -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 +42 -480
- 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 +40 -28
- figrecipe-0.9.0.dist-info/METADATA +427 -0
- figrecipe-0.9.0.dist-info/RECORD +277 -0
- figrecipe-0.9.0.dist-info/entry_points.txt +2 -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/METADATA +0 -394
- 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.9.0.dist-info}/WHEEL +0 -0
- {figrecipe-0.6.0.dist-info → figrecipe-0.9.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""Registry for plot types using existing figrecipe infrastructure.
|
|
4
|
+
|
|
5
|
+
Uses:
|
|
6
|
+
- figrecipe._signatures for signature introspection
|
|
7
|
+
- figrecipe._dev.demo_plotters._categories for plot type organization
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict, List
|
|
11
|
+
|
|
12
|
+
from .._dev.demo_plotters._categories import CATEGORIES as _PLOT_CATEGORIES
|
|
13
|
+
from .._dev.demo_plotters._categories import (
|
|
14
|
+
CATEGORY_DISPLAY_NAMES as _PLOT_DISPLAY_NAMES,
|
|
15
|
+
)
|
|
16
|
+
from .._signatures import get_signature
|
|
17
|
+
|
|
18
|
+
# Decoration methods to add to the selector
|
|
19
|
+
DECORATION_METHODS: List[str] = [
|
|
20
|
+
"text",
|
|
21
|
+
"annotate",
|
|
22
|
+
"arrow",
|
|
23
|
+
"axhline",
|
|
24
|
+
"axvline",
|
|
25
|
+
"axhspan",
|
|
26
|
+
"axvspan",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
# Combined categories: plots + decorations
|
|
30
|
+
CATEGORIES: Dict[str, List[str]] = {
|
|
31
|
+
**_PLOT_CATEGORIES,
|
|
32
|
+
"decoration": DECORATION_METHODS,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
CATEGORY_DISPLAY_NAMES: Dict[str, str] = {
|
|
36
|
+
**_PLOT_DISPLAY_NAMES,
|
|
37
|
+
"decoration": "Decoration & Annotation",
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _get_docstring(method_name: str) -> str:
|
|
42
|
+
"""Get first line of docstring for a matplotlib method."""
|
|
43
|
+
try:
|
|
44
|
+
import matplotlib.pyplot as plt
|
|
45
|
+
from matplotlib.axes import Axes
|
|
46
|
+
|
|
47
|
+
# Try Axes method first, then plt
|
|
48
|
+
fn = getattr(Axes, method_name, None) or getattr(plt, method_name, None)
|
|
49
|
+
if fn and fn.__doc__:
|
|
50
|
+
# Get first non-empty line of docstring
|
|
51
|
+
first_line = fn.__doc__.strip().split("\n")[0].strip()
|
|
52
|
+
# Clean up and truncate if needed
|
|
53
|
+
if len(first_line) > 100:
|
|
54
|
+
first_line = first_line[:97] + "..."
|
|
55
|
+
return first_line
|
|
56
|
+
except Exception:
|
|
57
|
+
pass
|
|
58
|
+
return ""
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_plot_type_info(method_name: str) -> Dict[str, Any]:
|
|
62
|
+
"""Get plot type information from existing signature infrastructure."""
|
|
63
|
+
sig = get_signature(method_name)
|
|
64
|
+
|
|
65
|
+
args = sig.get("args", [])
|
|
66
|
+
kwargs = sig.get("kwargs", {})
|
|
67
|
+
|
|
68
|
+
# Args that also exist in kwargs with defaults are actually optional
|
|
69
|
+
kwargs_with_defaults = {
|
|
70
|
+
k for k, v in kwargs.items() if v.get("default") is not None or "default" in v
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Format required and optional args
|
|
74
|
+
required_parts = []
|
|
75
|
+
optional_parts = []
|
|
76
|
+
|
|
77
|
+
for arg in args:
|
|
78
|
+
name = arg.get("name", "")
|
|
79
|
+
if name.startswith("*"):
|
|
80
|
+
continue
|
|
81
|
+
# Arg is optional if marked optional OR has a default in kwargs
|
|
82
|
+
is_optional = arg.get("optional", False) or name in kwargs_with_defaults
|
|
83
|
+
if is_optional:
|
|
84
|
+
optional_parts.append(name)
|
|
85
|
+
else:
|
|
86
|
+
required_parts.append(name)
|
|
87
|
+
|
|
88
|
+
required_str = ", ".join(required_parts) if required_parts else "data"
|
|
89
|
+
# Only show first 2 optional vars, deduplicated
|
|
90
|
+
optional_str = ", ".join(optional_parts[:2]) if optional_parts else ""
|
|
91
|
+
|
|
92
|
+
# Build hint string showing signature
|
|
93
|
+
hint_parts = []
|
|
94
|
+
for arg in args[:4]:
|
|
95
|
+
name = arg.get("name", "")
|
|
96
|
+
if name.startswith("*"):
|
|
97
|
+
continue
|
|
98
|
+
is_optional = arg.get("optional", False) or name in kwargs_with_defaults
|
|
99
|
+
if is_optional:
|
|
100
|
+
hint_parts.append(f"[{name}]")
|
|
101
|
+
else:
|
|
102
|
+
hint_parts.append(name)
|
|
103
|
+
|
|
104
|
+
if hint_parts:
|
|
105
|
+
hint = f"{method_name}({', '.join(hint_parts)})"
|
|
106
|
+
else:
|
|
107
|
+
hint = f"{method_name}(data)"
|
|
108
|
+
|
|
109
|
+
# Get docstring for tooltip
|
|
110
|
+
docstring = _get_docstring(method_name)
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
"name": method_name,
|
|
114
|
+
"required": required_str,
|
|
115
|
+
"optional": optional_str,
|
|
116
|
+
"hint": hint,
|
|
117
|
+
"docstring": docstring,
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def get_all_plot_types() -> Dict[str, Dict[str, Any]]:
|
|
122
|
+
"""Get all plot types organized by category."""
|
|
123
|
+
result = {}
|
|
124
|
+
for cat_key, plots in CATEGORIES.items():
|
|
125
|
+
label = CATEGORY_DISPLAY_NAMES.get(cat_key, cat_key)
|
|
126
|
+
result[cat_key] = {
|
|
127
|
+
"label": label,
|
|
128
|
+
"types": [get_plot_type_info(m) for m in plots],
|
|
129
|
+
}
|
|
130
|
+
return result
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def generate_html_options() -> str:
|
|
134
|
+
"""Generate HTML <optgroup> and <option> elements for plot type selector.
|
|
135
|
+
|
|
136
|
+
Shows required and optional arguments with brackets around optional.
|
|
137
|
+
e.g., "fill_between (x, y1, [y2], [where])"
|
|
138
|
+
Includes docstring as tooltip on hover.
|
|
139
|
+
"""
|
|
140
|
+
import html
|
|
141
|
+
|
|
142
|
+
html_parts = []
|
|
143
|
+
first = True
|
|
144
|
+
for cat_key, plots in CATEGORIES.items():
|
|
145
|
+
label = CATEGORY_DISPLAY_NAMES.get(cat_key, cat_key)
|
|
146
|
+
html_parts.append(f' <optgroup label="{label}">')
|
|
147
|
+
for method in plots:
|
|
148
|
+
info = get_plot_type_info(method)
|
|
149
|
+
# Use hint which has correct bracket notation
|
|
150
|
+
# hint format: "method(x, y, [opt1], [opt2])"
|
|
151
|
+
display_text = info["hint"]
|
|
152
|
+
selected = " selected" if first else ""
|
|
153
|
+
first = False
|
|
154
|
+
# Add docstring as title for tooltip
|
|
155
|
+
docstring = info.get("docstring", "")
|
|
156
|
+
title_attr = f' title="{html.escape(docstring)}"' if docstring else ""
|
|
157
|
+
html_parts.append(
|
|
158
|
+
f' <option value="{method}"{selected}{title_attr}>{display_text}</option>'
|
|
159
|
+
)
|
|
160
|
+
html_parts.append(" </optgroup>")
|
|
161
|
+
return "\n".join(html_parts)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def generate_js_hints() -> str:
|
|
165
|
+
"""Generate JavaScript PLOT_TYPE_HINTS object from signature infrastructure."""
|
|
166
|
+
lines = ["const PLOT_TYPE_HINTS = {"]
|
|
167
|
+
for plots in CATEGORIES.values():
|
|
168
|
+
for method in plots:
|
|
169
|
+
info = get_plot_type_info(method)
|
|
170
|
+
req = info["required"].replace("'", "\\'")
|
|
171
|
+
opt = info["optional"].replace("'", "\\'")
|
|
172
|
+
hint = info["hint"].replace("'", "\\'")
|
|
173
|
+
doc = info.get("docstring", "").replace("'", "\\'")
|
|
174
|
+
lines.append(
|
|
175
|
+
f" {method}: {{ required: '{req}', optional: '{opt}', hint: '{hint}', doc: '{doc}' }},"
|
|
176
|
+
)
|
|
177
|
+
lines.append("};")
|
|
178
|
+
return "\n".join(lines)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
__all__ = [
|
|
182
|
+
"CATEGORIES",
|
|
183
|
+
"CATEGORY_DISPLAY_NAMES",
|
|
184
|
+
"get_plot_type_info",
|
|
185
|
+
"get_all_plot_types",
|
|
186
|
+
"generate_html_options",
|
|
187
|
+
"generate_js_hints",
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
# EOF
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
"""User preferences management for the figure editor.
|
|
4
|
+
|
|
5
|
+
Preferences are stored in ~/.figrecipe/preferences.json and persist
|
|
6
|
+
across sessions. This allows users to set defaults like dark mode.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Dict
|
|
12
|
+
|
|
13
|
+
# Default preferences
|
|
14
|
+
DEFAULT_PREFERENCES = {
|
|
15
|
+
"dark_mode": False,
|
|
16
|
+
"default_style": "SCITEX",
|
|
17
|
+
"auto_save": True,
|
|
18
|
+
"show_hit_regions": False,
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
# Preferences file location
|
|
22
|
+
PREFERENCES_DIR = Path.home() / ".figrecipe"
|
|
23
|
+
PREFERENCES_FILE = PREFERENCES_DIR / "preferences.json"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_preferences_path() -> Path:
|
|
27
|
+
"""Get the path to the preferences file."""
|
|
28
|
+
return PREFERENCES_FILE
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def load_preferences() -> Dict[str, Any]:
|
|
32
|
+
"""Load user preferences from disk.
|
|
33
|
+
|
|
34
|
+
Returns
|
|
35
|
+
-------
|
|
36
|
+
dict
|
|
37
|
+
User preferences merged with defaults.
|
|
38
|
+
"""
|
|
39
|
+
prefs = DEFAULT_PREFERENCES.copy()
|
|
40
|
+
|
|
41
|
+
if PREFERENCES_FILE.exists():
|
|
42
|
+
try:
|
|
43
|
+
with open(PREFERENCES_FILE, "r") as f:
|
|
44
|
+
saved = json.load(f)
|
|
45
|
+
prefs.update(saved)
|
|
46
|
+
except (json.JSONDecodeError, IOError):
|
|
47
|
+
# If file is corrupted, use defaults
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
return prefs
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def save_preferences(prefs: Dict[str, Any]) -> bool:
|
|
54
|
+
"""Save user preferences to disk.
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
prefs : dict
|
|
59
|
+
Preferences to save.
|
|
60
|
+
|
|
61
|
+
Returns
|
|
62
|
+
-------
|
|
63
|
+
bool
|
|
64
|
+
True if save was successful.
|
|
65
|
+
"""
|
|
66
|
+
try:
|
|
67
|
+
PREFERENCES_DIR.mkdir(parents=True, exist_ok=True)
|
|
68
|
+
with open(PREFERENCES_FILE, "w") as f:
|
|
69
|
+
json.dump(prefs, f, indent=2)
|
|
70
|
+
return True
|
|
71
|
+
except IOError:
|
|
72
|
+
return False
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_preference(key: str, default: Any = None) -> Any:
|
|
76
|
+
"""Get a single preference value.
|
|
77
|
+
|
|
78
|
+
Parameters
|
|
79
|
+
----------
|
|
80
|
+
key : str
|
|
81
|
+
Preference key.
|
|
82
|
+
default : Any, optional
|
|
83
|
+
Default value if key not found.
|
|
84
|
+
|
|
85
|
+
Returns
|
|
86
|
+
-------
|
|
87
|
+
Any
|
|
88
|
+
Preference value.
|
|
89
|
+
"""
|
|
90
|
+
prefs = load_preferences()
|
|
91
|
+
return prefs.get(key, default)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def set_preference(key: str, value: Any) -> bool:
|
|
95
|
+
"""Set a single preference value.
|
|
96
|
+
|
|
97
|
+
Parameters
|
|
98
|
+
----------
|
|
99
|
+
key : str
|
|
100
|
+
Preference key.
|
|
101
|
+
value : Any
|
|
102
|
+
Value to set.
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
bool
|
|
107
|
+
True if save was successful.
|
|
108
|
+
"""
|
|
109
|
+
prefs = load_preferences()
|
|
110
|
+
prefs[key] = value
|
|
111
|
+
return save_preferences(prefs)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def reset_preferences() -> bool:
|
|
115
|
+
"""Reset all preferences to defaults.
|
|
116
|
+
|
|
117
|
+
Returns
|
|
118
|
+
-------
|
|
119
|
+
bool
|
|
120
|
+
True if reset was successful.
|
|
121
|
+
"""
|
|
122
|
+
return save_preferences(DEFAULT_PREFERENCES.copy())
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
__all__ = [
|
|
126
|
+
"DEFAULT_PREFERENCES",
|
|
127
|
+
"get_preferences_path",
|
|
128
|
+
"load_preferences",
|
|
129
|
+
"save_preferences",
|
|
130
|
+
"get_preference",
|
|
131
|
+
"set_preference",
|
|
132
|
+
"reset_preferences",
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
# EOF
|