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
figrecipe/_editor/_renderer.py
CHANGED
|
@@ -10,10 +10,9 @@ applied, enabling real-time preview updates in the GUI editor.
|
|
|
10
10
|
import io
|
|
11
11
|
from typing import Any, Dict, Optional, Tuple
|
|
12
12
|
|
|
13
|
-
from matplotlib.figure import Figure
|
|
14
|
-
|
|
15
13
|
from .._wrappers import RecordingFigure
|
|
16
14
|
from ._bbox import extract_bboxes
|
|
15
|
+
from ._render_overrides import apply_dark_mode, apply_overrides
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
def render_preview(
|
|
@@ -48,13 +47,19 @@ def render_preview(
|
|
|
48
47
|
# Get underlying matplotlib figure
|
|
49
48
|
mpl_fig = fig.fig if hasattr(fig, "fig") else fig
|
|
50
49
|
|
|
50
|
+
# Get record for call_id grouping (if fig is a RecordingFigure)
|
|
51
|
+
record = fig.record if hasattr(fig, "record") else None
|
|
52
|
+
|
|
51
53
|
# Apply style overrides
|
|
52
54
|
if overrides:
|
|
53
|
-
|
|
55
|
+
apply_overrides(mpl_fig, overrides, record)
|
|
54
56
|
|
|
55
57
|
# Apply dark mode if requested
|
|
56
58
|
if dark_mode:
|
|
57
|
-
|
|
59
|
+
apply_dark_mode(mpl_fig)
|
|
60
|
+
|
|
61
|
+
# Finalize ticks and special plots (must be done after all plotting)
|
|
62
|
+
_finalize_figure(fig, mpl_fig)
|
|
58
63
|
|
|
59
64
|
# Render to buffer first
|
|
60
65
|
buf = io.BytesIO()
|
|
@@ -120,185 +125,6 @@ def render_to_base64(
|
|
|
120
125
|
return base64_str, bboxes, img_size
|
|
121
126
|
|
|
122
127
|
|
|
123
|
-
def _apply_overrides(fig: Figure, overrides: Dict[str, Any]) -> None:
|
|
124
|
-
"""
|
|
125
|
-
Apply style overrides to figure.
|
|
126
|
-
|
|
127
|
-
Parameters
|
|
128
|
-
----------
|
|
129
|
-
fig : Figure
|
|
130
|
-
Matplotlib figure.
|
|
131
|
-
overrides : dict
|
|
132
|
-
Style overrides with keys like:
|
|
133
|
-
- axes_width_mm, axes_height_mm
|
|
134
|
-
- fonts_axis_label_pt, fonts_tick_label_pt
|
|
135
|
-
- lines_trace_mm
|
|
136
|
-
- etc.
|
|
137
|
-
"""
|
|
138
|
-
from ..styles._style_applier import apply_style_mm
|
|
139
|
-
|
|
140
|
-
axes_list = fig.get_axes()
|
|
141
|
-
|
|
142
|
-
for ax in axes_list:
|
|
143
|
-
# Apply mm-based styling
|
|
144
|
-
apply_style_mm(ax, overrides)
|
|
145
|
-
|
|
146
|
-
# Apply specific overrides that aren't handled by apply_style_mm
|
|
147
|
-
# YAML-compatible keys are canonical, legacy keys supported for backwards compatibility
|
|
148
|
-
|
|
149
|
-
# Font sizes (YAML: fonts_axis_label_pt, legacy: axis_font_size_pt)
|
|
150
|
-
axis_fs = overrides.get(
|
|
151
|
-
"fonts_axis_label_pt", overrides.get("axis_font_size_pt")
|
|
152
|
-
)
|
|
153
|
-
if axis_fs is not None:
|
|
154
|
-
ax.xaxis.label.set_fontsize(axis_fs)
|
|
155
|
-
ax.yaxis.label.set_fontsize(axis_fs)
|
|
156
|
-
|
|
157
|
-
tick_fs = overrides.get(
|
|
158
|
-
"fonts_tick_label_pt", overrides.get("tick_font_size_pt")
|
|
159
|
-
)
|
|
160
|
-
if tick_fs is not None:
|
|
161
|
-
ax.tick_params(labelsize=tick_fs)
|
|
162
|
-
|
|
163
|
-
title_fs = overrides.get("fonts_title_pt", overrides.get("title_font_size_pt"))
|
|
164
|
-
if title_fs is not None:
|
|
165
|
-
ax.title.set_fontsize(title_fs)
|
|
166
|
-
|
|
167
|
-
family = overrides.get("fonts_family", overrides.get("font_family"))
|
|
168
|
-
if family is not None:
|
|
169
|
-
ax.xaxis.label.set_fontfamily(family)
|
|
170
|
-
ax.yaxis.label.set_fontfamily(family)
|
|
171
|
-
ax.title.set_fontfamily(family)
|
|
172
|
-
for label in ax.get_xticklabels() + ax.get_yticklabels():
|
|
173
|
-
label.set_fontfamily(family)
|
|
174
|
-
|
|
175
|
-
# Ticks (YAML: ticks_direction, legacy: tick_direction)
|
|
176
|
-
tick_dir = overrides.get("ticks_direction", overrides.get("tick_direction"))
|
|
177
|
-
if tick_dir is not None and tick_dir in ("in", "out", "inout"):
|
|
178
|
-
ax.tick_params(direction=tick_dir)
|
|
179
|
-
|
|
180
|
-
tick_len = overrides.get("ticks_length_mm", overrides.get("tick_length_mm"))
|
|
181
|
-
if tick_len is not None:
|
|
182
|
-
from .._utils._units import mm_to_pt
|
|
183
|
-
|
|
184
|
-
length = mm_to_pt(tick_len)
|
|
185
|
-
ax.tick_params(length=length)
|
|
186
|
-
|
|
187
|
-
# Grid (YAML: behavior_grid, legacy: grid)
|
|
188
|
-
grid_value = overrides.get("behavior_grid", overrides.get("grid"))
|
|
189
|
-
if grid_value is not None:
|
|
190
|
-
if grid_value:
|
|
191
|
-
ax.grid(True, alpha=0.3)
|
|
192
|
-
else:
|
|
193
|
-
ax.grid(False)
|
|
194
|
-
|
|
195
|
-
# Spines (YAML: behavior_hide_top_spine, legacy: hide_top_spine)
|
|
196
|
-
hide_top = overrides.get(
|
|
197
|
-
"behavior_hide_top_spine", overrides.get("hide_top_spine")
|
|
198
|
-
)
|
|
199
|
-
if hide_top is not None:
|
|
200
|
-
ax.spines["top"].set_visible(not hide_top)
|
|
201
|
-
|
|
202
|
-
hide_right = overrides.get(
|
|
203
|
-
"behavior_hide_right_spine", overrides.get("hide_right_spine")
|
|
204
|
-
)
|
|
205
|
-
if hide_right is not None:
|
|
206
|
-
ax.spines["right"].set_visible(not hide_right)
|
|
207
|
-
|
|
208
|
-
# Legend
|
|
209
|
-
legend = ax.get_legend()
|
|
210
|
-
if legend is not None:
|
|
211
|
-
if "legend_frameon" in overrides:
|
|
212
|
-
legend.set_frame_on(overrides["legend_frameon"])
|
|
213
|
-
|
|
214
|
-
if "legend_alpha" in overrides:
|
|
215
|
-
frame = legend.get_frame()
|
|
216
|
-
fc = frame.get_facecolor()
|
|
217
|
-
frame.set_facecolor((*fc[:3], overrides["legend_alpha"]))
|
|
218
|
-
|
|
219
|
-
# Line widths (YAML: lines_trace_mm, legacy: trace_thickness_mm)
|
|
220
|
-
trace_mm = overrides.get("lines_trace_mm", overrides.get("trace_thickness_mm"))
|
|
221
|
-
if trace_mm is not None:
|
|
222
|
-
from .._utils._units import mm_to_pt
|
|
223
|
-
|
|
224
|
-
lw = mm_to_pt(trace_mm)
|
|
225
|
-
for line in ax.get_lines():
|
|
226
|
-
line.set_linewidth(lw)
|
|
227
|
-
|
|
228
|
-
# Marker sizes (YAML: markers_scatter_mm, legacy: marker_size_mm)
|
|
229
|
-
# Only apply to PathCollection (scatter), not PolyCollection (violin/fill)
|
|
230
|
-
scatter_mm = overrides.get(
|
|
231
|
-
"markers_scatter_mm",
|
|
232
|
-
overrides.get("markers_size_mm", overrides.get("marker_size_mm")),
|
|
233
|
-
)
|
|
234
|
-
if scatter_mm is not None:
|
|
235
|
-
from matplotlib.collections import PathCollection
|
|
236
|
-
|
|
237
|
-
from .._utils._units import mm_to_scatter_size
|
|
238
|
-
|
|
239
|
-
size = mm_to_scatter_size(scatter_mm)
|
|
240
|
-
for coll in ax.collections:
|
|
241
|
-
# Only apply to scatter plots (PathCollection), not violin/fill (PolyCollection)
|
|
242
|
-
if isinstance(coll, PathCollection):
|
|
243
|
-
try:
|
|
244
|
-
coll.set_sizes([size])
|
|
245
|
-
except Exception:
|
|
246
|
-
pass
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
def _apply_dark_mode(fig: Figure) -> None:
|
|
250
|
-
"""
|
|
251
|
-
Apply dark mode colors to figure.
|
|
252
|
-
|
|
253
|
-
Parameters
|
|
254
|
-
----------
|
|
255
|
-
fig : Figure
|
|
256
|
-
Matplotlib figure.
|
|
257
|
-
"""
|
|
258
|
-
# Dark theme colors
|
|
259
|
-
bg_color = "#1a1a1a"
|
|
260
|
-
text_color = "#e8e8e8"
|
|
261
|
-
|
|
262
|
-
# Figure background
|
|
263
|
-
fig.patch.set_facecolor(bg_color)
|
|
264
|
-
|
|
265
|
-
# Figure-level text elements (suptitle, supxlabel, supylabel)
|
|
266
|
-
if hasattr(fig, "_suptitle") and fig._suptitle is not None:
|
|
267
|
-
fig._suptitle.set_color(text_color)
|
|
268
|
-
if hasattr(fig, "_supxlabel") and fig._supxlabel is not None:
|
|
269
|
-
fig._supxlabel.set_color(text_color)
|
|
270
|
-
if hasattr(fig, "_supylabel") and fig._supylabel is not None:
|
|
271
|
-
fig._supylabel.set_color(text_color)
|
|
272
|
-
|
|
273
|
-
for ax in fig.get_axes():
|
|
274
|
-
# Axes background
|
|
275
|
-
ax.set_facecolor(bg_color)
|
|
276
|
-
|
|
277
|
-
# Text colors
|
|
278
|
-
ax.xaxis.label.set_color(text_color)
|
|
279
|
-
ax.yaxis.label.set_color(text_color)
|
|
280
|
-
ax.title.set_color(text_color)
|
|
281
|
-
|
|
282
|
-
# Tick labels
|
|
283
|
-
ax.tick_params(colors=text_color)
|
|
284
|
-
|
|
285
|
-
# Spines
|
|
286
|
-
for spine in ax.spines.values():
|
|
287
|
-
spine.set_color(text_color)
|
|
288
|
-
|
|
289
|
-
# Grid
|
|
290
|
-
ax.tick_params(color=text_color)
|
|
291
|
-
|
|
292
|
-
# Legend
|
|
293
|
-
legend = ax.get_legend()
|
|
294
|
-
if legend is not None:
|
|
295
|
-
frame = legend.get_frame()
|
|
296
|
-
frame.set_facecolor(bg_color)
|
|
297
|
-
frame.set_edgecolor(text_color)
|
|
298
|
-
for text in legend.get_texts():
|
|
299
|
-
text.set_color(text_color)
|
|
300
|
-
|
|
301
|
-
|
|
302
128
|
def render_download(
|
|
303
129
|
fig: RecordingFigure,
|
|
304
130
|
fmt: str = "png",
|
|
@@ -329,11 +155,17 @@ def render_download(
|
|
|
329
155
|
"""
|
|
330
156
|
mpl_fig = fig.fig if hasattr(fig, "fig") else fig
|
|
331
157
|
|
|
158
|
+
# Get record for call_id grouping (if fig is a RecordingFigure)
|
|
159
|
+
record = fig.record if hasattr(fig, "record") else None
|
|
160
|
+
|
|
332
161
|
if overrides:
|
|
333
|
-
|
|
162
|
+
apply_overrides(mpl_fig, overrides, record)
|
|
334
163
|
|
|
335
164
|
if dark_mode:
|
|
336
|
-
|
|
165
|
+
apply_dark_mode(mpl_fig)
|
|
166
|
+
|
|
167
|
+
# Finalize ticks and special plots (must be done after all plotting)
|
|
168
|
+
_finalize_figure(fig, mpl_fig)
|
|
337
169
|
|
|
338
170
|
buf = io.BytesIO()
|
|
339
171
|
mpl_fig.savefig(buf, format=fmt, dpi=dpi, bbox_inches="tight")
|
|
@@ -342,8 +174,26 @@ def render_download(
|
|
|
342
174
|
return buf.read()
|
|
343
175
|
|
|
344
176
|
|
|
177
|
+
def _finalize_figure(fig: RecordingFigure, mpl_fig: Any) -> None:
|
|
178
|
+
"""Finalize ticks and special plots for all axes in the figure."""
|
|
179
|
+
from ..styles._style_applier import finalize_special_plots, finalize_ticks
|
|
180
|
+
|
|
181
|
+
# Get style dict for finalization
|
|
182
|
+
style_dict = {}
|
|
183
|
+
if hasattr(fig, "style") and fig.style:
|
|
184
|
+
from ..styles import get_style
|
|
185
|
+
|
|
186
|
+
style_dict = get_style(fig.style)
|
|
187
|
+
|
|
188
|
+
for ax in mpl_fig.get_axes():
|
|
189
|
+
finalize_ticks(ax)
|
|
190
|
+
finalize_special_plots(ax, style_dict)
|
|
191
|
+
|
|
192
|
+
|
|
345
193
|
__all__ = [
|
|
346
194
|
"render_preview",
|
|
347
195
|
"render_to_base64",
|
|
348
196
|
"render_download",
|
|
349
197
|
]
|
|
198
|
+
|
|
199
|
+
# EOF
|