rustplotlib 4.0.1__tar.gz → 5.0.0__tar.gz
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.
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/CLAUDE.md +18 -11
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/Cargo.lock +1 -1
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/Cargo.toml +1 -1
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/PKG-INFO +32 -10
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/README.md +31 -9
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/ROADMAP.md +39 -10
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/pyproject.toml +1 -1
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/__init__.py +5 -3
- rustplotlib-5.0.0/python/rustplotlib/backends/__init__.py +72 -0
- rustplotlib-5.0.0/python/rustplotlib/backends/backend_base.py +164 -0
- rustplotlib-5.0.0/python/rustplotlib/backends/backend_inline.py +70 -0
- rustplotlib-5.0.0/python/rustplotlib/backends/backend_tk.py +385 -0
- rustplotlib-5.0.0/python/rustplotlib/callback_registry.py +37 -0
- rustplotlib-5.0.0/python/rustplotlib/events.py +82 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/pyplot.py +39 -22
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/figure.rs +23 -0
- rustplotlib-5.0.0/tests/test_backend_tk.py +187 -0
- rustplotlib-5.0.0/tests/test_events.py +173 -0
- rustplotlib-5.0.0/tests/test_jupyter_repr.py +98 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_phase6_7.py +2 -1
- rustplotlib-4.0.1/python/rustplotlib/backends/__init__.py +0 -8
- rustplotlib-4.0.1/python/rustplotlib/backends/backend_inline.py +0 -24
- rustplotlib-4.0.1/tests/test_jupyter_repr.py +0 -15
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/.github/workflows/ci.yml +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/.gitignore +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/CONTRIBUTING.md +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/LICENSE +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/benchmark_results.csv +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/dados/Data/PerfisTemp_rustplotlib.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/docs/superpowers/plans/2026-04-04-jupyter-and-event-foundation.md +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/animation.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/axes/__init__.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/backends/backend_pdf.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/cm.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/collections.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/colors.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/cycler.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/dates.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/figure.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/font_manager.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/gridspec.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/lines.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/mpl_toolkits/__init__.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/mpl_toolkits/mplot3d/__init__.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/patches.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/patheffects.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/pyplot.pyi +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/spines.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/style/__init__.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/text.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/ticker.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/transforms.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/widgets.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/arrow.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/bar.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/bar3d.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/barh.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/boxplot.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/broken_barh.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/contour.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/contour3d.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/errorbar.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/eventplot.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/fill_between.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/fill_betweenx.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/fill_polygon.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/hexbin.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/hist.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/image.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/legend.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/line2d.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/line3d.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/mod.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/patches.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/pcolormesh.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/pie.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/quiver.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/radar.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/sankey.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/scatter.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/scatter3d.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/stem.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/step.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/streamplot.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/surface3d.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/trisurf3d.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/violin.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/wireframe3d.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/axes.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/axes3d.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/colors.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/fonts/DejaVuSans.ttf +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/lib.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/projection3d.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/svg_renderer.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/text.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/ticker.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/transforms.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/window.rs +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_3d.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_benchmark.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_colors.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_figure.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_new_features.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_new_features2.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_phase8.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_phase9_features.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_pyplot.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_spectral_features.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_ticker.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_transforms.py +0 -0
- {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_v3_features.py +0 -0
|
@@ -6,14 +6,18 @@ Reimplementação completa do matplotlib em Rust puro. Drop-in replacement via P
|
|
|
6
6
|
|
|
7
7
|
- Sempre responder em português (pt-BR)
|
|
8
8
|
|
|
9
|
-
## Estado Atual —
|
|
9
|
+
## Estado Atual — v5.0.0 (Phase 1 + Phase 2)
|
|
10
10
|
|
|
11
11
|
- 40+ plot types 2D, 7 plot types 3D
|
|
12
12
|
- 70+ colormaps (35 base + 35 reversed)
|
|
13
|
-
-
|
|
14
|
-
-
|
|
13
|
+
- 25 módulos Python compatíveis com API matplotlib
|
|
14
|
+
- 298 testes passando
|
|
15
|
+
- Tk backend interativo com toolbar, eventos de mouse/teclado e navegação
|
|
16
|
+
- Jupyter rich display (`_repr_png_`, `_repr_svg_`, `_repr_html_`)
|
|
17
|
+
- Sistema de eventos com `CallbackRegistry` e `mpl_connect`/`mpl_disconnect`
|
|
18
|
+
- Backend auto-detection (inline/tk/agg)
|
|
15
19
|
- Output: PNG (tiny-skia), SVG nativo, PDF, GIF, janela interativa
|
|
16
|
-
- Performance: até
|
|
20
|
+
- Performance: até 16x mais rápido que matplotlib (benchmark real com 12 testes)
|
|
17
21
|
- Zero blocos `unsafe` em todo o código Rust
|
|
18
22
|
|
|
19
23
|
## Estrutura do Projeto
|
|
@@ -41,10 +45,13 @@ python/rustplotlib/ # Camada Python
|
|
|
41
45
|
├── style/ # Temas (dark_background, ggplot, seaborn, bmh, fivethirtyeight)
|
|
42
46
|
├── animation.py # FuncAnimation + GIF
|
|
43
47
|
├── ticker.py # 12 Formatters + 10 Locators funcionais
|
|
48
|
+
├── backends/ # backend_base, backend_inline, backend_tk, backend_pdf
|
|
49
|
+
├── events.py # MouseEvent, KeyEvent, DrawEvent, ResizeEvent, CloseEvent
|
|
50
|
+
├── callback_registry.py # CallbackRegistry (mpl_connect/mpl_disconnect)
|
|
44
51
|
├── dates.py, colors.py, patches.py, ...
|
|
45
52
|
└── mpl_toolkits/mplot3d/ # Suporte 3D
|
|
46
53
|
|
|
47
|
-
tests/ # Testes Python (
|
|
54
|
+
tests/ # Testes Python (298 testes)
|
|
48
55
|
```
|
|
49
56
|
|
|
50
57
|
## Stack Técnica
|
|
@@ -95,15 +102,15 @@ cargo check # Verificar compilação Rust
|
|
|
95
102
|
- **Manter compatibilidade com API do matplotlib** — mesmos nomes de funções e parâmetros
|
|
96
103
|
- **Atualizar ROADMAP.md e README.md** ao completar features
|
|
97
104
|
|
|
98
|
-
## Roadmap Ativo (próximo: v5.
|
|
105
|
+
## Roadmap Ativo (próximo: v5.1.0)
|
|
99
106
|
|
|
100
107
|
Próximos itens prioritários (ver ROADMAP.md para lista completa):
|
|
101
108
|
|
|
102
|
-
1. **
|
|
103
|
-
2. **
|
|
104
|
-
3. **
|
|
105
|
-
4. **
|
|
106
|
-
5. **
|
|
109
|
+
1. **Backends adicionais** — Qt, GTK, WebAgg, macOS native
|
|
110
|
+
2. **Widgets funcionais** — Slider, Button, CheckButtons com renderização real
|
|
111
|
+
3. **Features interativas** — pick events, rotação 3D, blitting
|
|
112
|
+
4. **Triangulation plots** — tricontour, tricontourf, tripcolor (atualmente stubs)
|
|
113
|
+
5. **Customização avançada** — spine positioning, TwoSlopeNorm, interpolação bicúbica
|
|
107
114
|
|
|
108
115
|
## Git
|
|
109
116
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rustplotlib
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.0
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Intended Audience :: Science/Research
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -198,9 +198,23 @@ plt.show()
|
|
|
198
198
|
| `savefig("file.png")` | Raster PNG (with dpi, transparent options) |
|
|
199
199
|
| `savefig("file.svg")` | Native vector SVG (real `<line>`, `<text>`, `<rect>` elements) |
|
|
200
200
|
| `savefig("file.pdf")` | PDF output |
|
|
201
|
-
| `show()` | Interactive window
|
|
201
|
+
| `show()` | Interactive window with backend auto-detection (Tk/inline/agg) |
|
|
202
202
|
| `PdfPages` | Multi-page PDF export |
|
|
203
203
|
|
|
204
|
+
### Backends & Interactive
|
|
205
|
+
| Feature | Description |
|
|
206
|
+
|---|---|
|
|
207
|
+
| Backend auto-detection | Automatically selects inline (Jupyter), Tk, or Agg backend |
|
|
208
|
+
| Tk backend | Interactive window with `PhotoImage` rendering (640x480) |
|
|
209
|
+
| Navigation toolbar | Home, Back, Forward, Pan, Zoom, Save + status bar (7 widgets) |
|
|
210
|
+
| Event system | `mpl_connect` / `mpl_disconnect` with `CallbackRegistry` |
|
|
211
|
+
| Mouse events | `MouseEvent` with button, x/y data, key modifiers |
|
|
212
|
+
| Key events | `KeyEvent` for keyboard interaction |
|
|
213
|
+
| Draw/Resize/Close events | `DrawEvent`, `ResizeEvent`, `CloseEvent` |
|
|
214
|
+
| Jupyter rich display | `_repr_png_()`, `_repr_svg_()`, `_repr_html_()` on Figure |
|
|
215
|
+
| `render_to_svg_string()` | SVG output as string (PyO3) for Jupyter inline |
|
|
216
|
+
| `render_to_rgba_buffer()` | Raw RGBA buffer (PyO3) for interactive backends |
|
|
217
|
+
|
|
204
218
|
### Animation
|
|
205
219
|
| Feature | Description |
|
|
206
220
|
|---|---|
|
|
@@ -245,7 +259,7 @@ All also available as `viridis_r`, `plasma_r`, `hot_r`, etc.
|
|
|
245
259
|
- **Dates:** `date2num()`, `num2date()`, date formatters and locators
|
|
246
260
|
- **Categorical axes:** string-based x values automatically converted
|
|
247
261
|
|
|
248
|
-
### Compatibility Modules (
|
|
262
|
+
### Compatibility Modules (25 modules)
|
|
249
263
|
| Module | Status |
|
|
250
264
|
|---|---|
|
|
251
265
|
| `rustplotlib.pyplot` | Full implementation (50+ functions) |
|
|
@@ -258,7 +272,9 @@ All also available as `viridis_r`, `plasma_r`, `hot_r`, etc.
|
|
|
258
272
|
| `rustplotlib.colors` | LinearSegmentedColormap, Normalize, LogNorm, BoundaryNorm |
|
|
259
273
|
| `rustplotlib.dates` | Date conversion, DateFormatter, DateLocator, Auto/Day/Month/Year/Hour/MinuteLocator |
|
|
260
274
|
| `rustplotlib.gridspec` | GridSpec, SubplotSpec |
|
|
261
|
-
| `rustplotlib.backends` | Backend system, PdfPages |
|
|
275
|
+
| `rustplotlib.backends` | Backend system with auto-detection (inline/tk/agg), PdfPages |
|
|
276
|
+
| `rustplotlib.backends.backend_base` | FigureCanvasBase, FigureManagerBase, NavigationToolbar2 |
|
|
277
|
+
| `rustplotlib.backends.backend_tk` | Tk interactive window with toolbar and events |
|
|
262
278
|
| `rustplotlib.mpl_toolkits.mplot3d` | Axes3D for 3D plotting |
|
|
263
279
|
| `rustplotlib.cm` | Colormap access by name |
|
|
264
280
|
| `rustplotlib.collections` | LineCollection, PathCollection, PatchCollection |
|
|
@@ -267,7 +283,10 @@ All also available as `viridis_r`, `plasma_r`, `hot_r`, etc.
|
|
|
267
283
|
| `rustplotlib.transforms` | Bbox, Affine2D, BboxTransform |
|
|
268
284
|
| `rustplotlib.patheffects` | Stroke, withStroke, SimplePatchShadow |
|
|
269
285
|
| `rustplotlib.spines` | Spine |
|
|
270
|
-
| `rustplotlib.
|
|
286
|
+
| `rustplotlib.axes` | Axes class reference |
|
|
287
|
+
| `rustplotlib.figure` | Figure class with Jupyter rich display (`_repr_png_`, `_repr_svg_`, `_repr_html_`) |
|
|
288
|
+
| `rustplotlib.events` | MouseEvent, KeyEvent, DrawEvent, ResizeEvent, CloseEvent |
|
|
289
|
+
| `rustplotlib.callback_registry` | CallbackRegistry for mpl_connect/mpl_disconnect |
|
|
271
290
|
| `rustplotlib.cycler` | cycler compatibility |
|
|
272
291
|
|
|
273
292
|
---
|
|
@@ -447,21 +466,24 @@ Contributions are welcome! This is an open-source project under the MIT license.
|
|
|
447
466
|
|
|
448
467
|
**Project stats:**
|
|
449
468
|
- **45+ Rust source files** — 23,000+ lines of native code
|
|
450
|
-
- **
|
|
469
|
+
- **25 Python modules** — 4,900+ lines of API
|
|
451
470
|
- **47+ plot functions** (40 2D + 7 3D)
|
|
452
471
|
- **70+ colormaps** (35 base + 35 reversed)
|
|
453
|
-
- **
|
|
472
|
+
- **298 tests** passing
|
|
454
473
|
- **22 formatters + locators** (functional)
|
|
474
|
+
- **Tk interactive backend** with navigation toolbar and event system
|
|
475
|
+
- **Jupyter rich display** (`_repr_png_`, `_repr_svg_`, `_repr_html_`)
|
|
476
|
+
- **Event system** with `mpl_connect` / `mpl_disconnect`
|
|
455
477
|
- **RGB/RGBA imshow**, bilinear interpolation, heatmap annotations
|
|
456
478
|
- **Signal processing**: specgram, psd, acorr, xcorr, coherence
|
|
457
479
|
- **Zero `unsafe` blocks**
|
|
458
480
|
|
|
459
481
|
**Priority areas for contribution:**
|
|
460
|
-
- Jupyter inline backend (rich display protocol)
|
|
461
|
-
- Functional widgets (Slider, Button, CheckButtons with real rendering)
|
|
462
|
-
- Interactive features (mouse events, zoom/pan, 3D rotation)
|
|
463
482
|
- Qt/GTK backends
|
|
483
|
+
- Functional widgets (Slider, Button, CheckButtons with real rendering)
|
|
484
|
+
- 3D mouse rotation
|
|
464
485
|
- Triangulation plots (tricontour, tripcolor)
|
|
486
|
+
- LaTeX math rendering
|
|
465
487
|
|
|
466
488
|
---
|
|
467
489
|
|
|
@@ -178,9 +178,23 @@ plt.show()
|
|
|
178
178
|
| `savefig("file.png")` | Raster PNG (with dpi, transparent options) |
|
|
179
179
|
| `savefig("file.svg")` | Native vector SVG (real `<line>`, `<text>`, `<rect>` elements) |
|
|
180
180
|
| `savefig("file.pdf")` | PDF output |
|
|
181
|
-
| `show()` | Interactive window
|
|
181
|
+
| `show()` | Interactive window with backend auto-detection (Tk/inline/agg) |
|
|
182
182
|
| `PdfPages` | Multi-page PDF export |
|
|
183
183
|
|
|
184
|
+
### Backends & Interactive
|
|
185
|
+
| Feature | Description |
|
|
186
|
+
|---|---|
|
|
187
|
+
| Backend auto-detection | Automatically selects inline (Jupyter), Tk, or Agg backend |
|
|
188
|
+
| Tk backend | Interactive window with `PhotoImage` rendering (640x480) |
|
|
189
|
+
| Navigation toolbar | Home, Back, Forward, Pan, Zoom, Save + status bar (7 widgets) |
|
|
190
|
+
| Event system | `mpl_connect` / `mpl_disconnect` with `CallbackRegistry` |
|
|
191
|
+
| Mouse events | `MouseEvent` with button, x/y data, key modifiers |
|
|
192
|
+
| Key events | `KeyEvent` for keyboard interaction |
|
|
193
|
+
| Draw/Resize/Close events | `DrawEvent`, `ResizeEvent`, `CloseEvent` |
|
|
194
|
+
| Jupyter rich display | `_repr_png_()`, `_repr_svg_()`, `_repr_html_()` on Figure |
|
|
195
|
+
| `render_to_svg_string()` | SVG output as string (PyO3) for Jupyter inline |
|
|
196
|
+
| `render_to_rgba_buffer()` | Raw RGBA buffer (PyO3) for interactive backends |
|
|
197
|
+
|
|
184
198
|
### Animation
|
|
185
199
|
| Feature | Description |
|
|
186
200
|
|---|---|
|
|
@@ -225,7 +239,7 @@ All also available as `viridis_r`, `plasma_r`, `hot_r`, etc.
|
|
|
225
239
|
- **Dates:** `date2num()`, `num2date()`, date formatters and locators
|
|
226
240
|
- **Categorical axes:** string-based x values automatically converted
|
|
227
241
|
|
|
228
|
-
### Compatibility Modules (
|
|
242
|
+
### Compatibility Modules (25 modules)
|
|
229
243
|
| Module | Status |
|
|
230
244
|
|---|---|
|
|
231
245
|
| `rustplotlib.pyplot` | Full implementation (50+ functions) |
|
|
@@ -238,7 +252,9 @@ All also available as `viridis_r`, `plasma_r`, `hot_r`, etc.
|
|
|
238
252
|
| `rustplotlib.colors` | LinearSegmentedColormap, Normalize, LogNorm, BoundaryNorm |
|
|
239
253
|
| `rustplotlib.dates` | Date conversion, DateFormatter, DateLocator, Auto/Day/Month/Year/Hour/MinuteLocator |
|
|
240
254
|
| `rustplotlib.gridspec` | GridSpec, SubplotSpec |
|
|
241
|
-
| `rustplotlib.backends` | Backend system, PdfPages |
|
|
255
|
+
| `rustplotlib.backends` | Backend system with auto-detection (inline/tk/agg), PdfPages |
|
|
256
|
+
| `rustplotlib.backends.backend_base` | FigureCanvasBase, FigureManagerBase, NavigationToolbar2 |
|
|
257
|
+
| `rustplotlib.backends.backend_tk` | Tk interactive window with toolbar and events |
|
|
242
258
|
| `rustplotlib.mpl_toolkits.mplot3d` | Axes3D for 3D plotting |
|
|
243
259
|
| `rustplotlib.cm` | Colormap access by name |
|
|
244
260
|
| `rustplotlib.collections` | LineCollection, PathCollection, PatchCollection |
|
|
@@ -247,7 +263,10 @@ All also available as `viridis_r`, `plasma_r`, `hot_r`, etc.
|
|
|
247
263
|
| `rustplotlib.transforms` | Bbox, Affine2D, BboxTransform |
|
|
248
264
|
| `rustplotlib.patheffects` | Stroke, withStroke, SimplePatchShadow |
|
|
249
265
|
| `rustplotlib.spines` | Spine |
|
|
250
|
-
| `rustplotlib.
|
|
266
|
+
| `rustplotlib.axes` | Axes class reference |
|
|
267
|
+
| `rustplotlib.figure` | Figure class with Jupyter rich display (`_repr_png_`, `_repr_svg_`, `_repr_html_`) |
|
|
268
|
+
| `rustplotlib.events` | MouseEvent, KeyEvent, DrawEvent, ResizeEvent, CloseEvent |
|
|
269
|
+
| `rustplotlib.callback_registry` | CallbackRegistry for mpl_connect/mpl_disconnect |
|
|
251
270
|
| `rustplotlib.cycler` | cycler compatibility |
|
|
252
271
|
|
|
253
272
|
---
|
|
@@ -427,21 +446,24 @@ Contributions are welcome! This is an open-source project under the MIT license.
|
|
|
427
446
|
|
|
428
447
|
**Project stats:**
|
|
429
448
|
- **45+ Rust source files** — 23,000+ lines of native code
|
|
430
|
-
- **
|
|
449
|
+
- **25 Python modules** — 4,900+ lines of API
|
|
431
450
|
- **47+ plot functions** (40 2D + 7 3D)
|
|
432
451
|
- **70+ colormaps** (35 base + 35 reversed)
|
|
433
|
-
- **
|
|
452
|
+
- **298 tests** passing
|
|
434
453
|
- **22 formatters + locators** (functional)
|
|
454
|
+
- **Tk interactive backend** with navigation toolbar and event system
|
|
455
|
+
- **Jupyter rich display** (`_repr_png_`, `_repr_svg_`, `_repr_html_`)
|
|
456
|
+
- **Event system** with `mpl_connect` / `mpl_disconnect`
|
|
435
457
|
- **RGB/RGBA imshow**, bilinear interpolation, heatmap annotations
|
|
436
458
|
- **Signal processing**: specgram, psd, acorr, xcorr, coherence
|
|
437
459
|
- **Zero `unsafe` blocks**
|
|
438
460
|
|
|
439
461
|
**Priority areas for contribution:**
|
|
440
|
-
- Jupyter inline backend (rich display protocol)
|
|
441
|
-
- Functional widgets (Slider, Button, CheckButtons with real rendering)
|
|
442
|
-
- Interactive features (mouse events, zoom/pan, 3D rotation)
|
|
443
462
|
- Qt/GTK backends
|
|
463
|
+
- Functional widgets (Slider, Button, CheckButtons with real rendering)
|
|
464
|
+
- 3D mouse rotation
|
|
444
465
|
- Triangulation plots (tricontour, tripcolor)
|
|
466
|
+
- LaTeX math rendering
|
|
445
467
|
|
|
446
468
|
---
|
|
447
469
|
|
|
@@ -54,7 +54,40 @@ Goal: Full matplotlib reimplementation in Rust.
|
|
|
54
54
|
|
|
55
55
|
---
|
|
56
56
|
|
|
57
|
-
##
|
|
57
|
+
## DONE — v5.0.0 (Phase 1 + Phase 2)
|
|
58
|
+
|
|
59
|
+
### Backend System
|
|
60
|
+
- [x] `backend_base.py` — `FigureCanvasBase`, `FigureManagerBase`, `NavigationToolbar2`
|
|
61
|
+
- [x] `backends/__init__.py` — auto-detection (inline/tk/agg), registry, `show_figure()`
|
|
62
|
+
- [x] `switch_backend()` / `set_backend()` functional
|
|
63
|
+
|
|
64
|
+
### Tk Interactive Backend
|
|
65
|
+
- [x] `backend_tk.py` — `FigureCanvasTk`, `FigureManagerTk`, `NavigationToolbarTk`
|
|
66
|
+
- [x] Tkinter window with PhotoImage rendering
|
|
67
|
+
- [x] Mouse events (click, release, motion, scroll)
|
|
68
|
+
- [x] Keyboard events
|
|
69
|
+
- [x] Navigation toolbar (Home, Back, Fwd, Pan, Zoom, Save)
|
|
70
|
+
- [x] Coordinate display on mouse motion
|
|
71
|
+
- [x] `show()` and `FigureProxy.show()` delegate to backend system
|
|
72
|
+
|
|
73
|
+
### Jupyter Rich Display
|
|
74
|
+
- [x] `render_to_svg_string()` exposed via PyO3
|
|
75
|
+
- [x] `render_to_rgba_buffer()` exposed via PyO3
|
|
76
|
+
- [x] `_repr_png_`, `_repr_svg_`, `_repr_html_` on FigureProxy
|
|
77
|
+
- [x] `backend_inline.py` rewritten with SVG support and configurable `figure_format`
|
|
78
|
+
|
|
79
|
+
### Event System
|
|
80
|
+
- [x] Event classes: `Event`, `LocationEvent`, `MouseEvent`, `KeyEvent`, `PickEvent`, `DrawEvent`, `ResizeEvent`, `CloseEvent`
|
|
81
|
+
- [x] `CallbackRegistry` with `connect(signal, func)`, `disconnect(cid)`, `process(signal, *args)`
|
|
82
|
+
- [x] `CanvasProxy.mpl_connect()` / `mpl_disconnect()` functional
|
|
83
|
+
- [x] `events` and `callback_registry` modules registered in package
|
|
84
|
+
|
|
85
|
+
### Compatibility Modules (25 total)
|
|
86
|
+
- [x] Added: events, callback_registry (+ backends expanded with backend_base, backend_tk, backend_inline)
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## PLANNED — v5.1.0
|
|
58
91
|
|
|
59
92
|
### Remaining Plot Types
|
|
60
93
|
- [ ] `tricontour()` / `tricontourf()` — contour on triangulation (currently stubs)
|
|
@@ -71,11 +104,9 @@ Goal: Full matplotlib reimplementation in Rust.
|
|
|
71
104
|
### Image Improvements
|
|
72
105
|
- [ ] Bicubic, Lanczos, Spline interpolation
|
|
73
106
|
|
|
74
|
-
###
|
|
107
|
+
### Additional Backends
|
|
75
108
|
- [ ] Qt backend (QApplication, mouse events, toolbar, save dialog)
|
|
76
109
|
- [ ] GTK3/GTK4 backend
|
|
77
|
-
- [ ] Tk backend (tkinter integration)
|
|
78
|
-
- [ ] Jupyter inline backend (rich display protocol, `_repr_png_`)
|
|
79
110
|
- [ ] WebAgg (HTML5 Canvas, browser-based interactive)
|
|
80
111
|
- [ ] macOS native backend (NSView/Metal)
|
|
81
112
|
|
|
@@ -89,9 +120,7 @@ Goal: Full matplotlib reimplementation in Rust.
|
|
|
89
120
|
- [ ] `Cursor` with crosshair rendering
|
|
90
121
|
|
|
91
122
|
### Interactive Features
|
|
92
|
-
- [ ]
|
|
93
|
-
- [ ] Keyboard events
|
|
94
|
-
- [ ] Zoom/pan navigation toolbar
|
|
123
|
+
- [ ] Pick events (artist hit testing)
|
|
95
124
|
- [ ] 3D mouse rotation
|
|
96
125
|
- [ ] Blitting for fast animation updates
|
|
97
126
|
- [ ] Interactive data cursors
|
|
@@ -165,7 +194,7 @@ Goal: Full matplotlib reimplementation in Rust.
|
|
|
165
194
|
Pick any unchecked item, open an issue to discuss, submit a PR with tests.
|
|
166
195
|
|
|
167
196
|
**Highest impact areas:**
|
|
168
|
-
1.
|
|
169
|
-
2.
|
|
197
|
+
1. Qt/GTK backends (v5.1.0) — needed for desktop applications
|
|
198
|
+
2. Functional widgets (v5.1.0) — Slider, Button, CheckButtons with real rendering
|
|
170
199
|
3. LaTeX rendering (v6.0.0) — needed for scientific papers
|
|
171
|
-
4. Triangulation plots (v5.
|
|
200
|
+
4. Triangulation plots (v5.1.0) — tricontour, tripcolor
|
|
@@ -20,15 +20,17 @@ from rustplotlib import transforms
|
|
|
20
20
|
from rustplotlib import lines
|
|
21
21
|
from rustplotlib import text
|
|
22
22
|
from rustplotlib import spines
|
|
23
|
+
from rustplotlib import events
|
|
24
|
+
from rustplotlib import callback_registry
|
|
23
25
|
|
|
24
26
|
__all__ = ["pyplot", "style", "animation", "widgets", "dates", "backends",
|
|
25
27
|
"font_manager", "ticker", "patches", "colors", "gridspec", "cycler",
|
|
26
28
|
"axes", "figure", "collections", "cm", "patheffects", "transforms",
|
|
27
|
-
"lines", "text", "spines",
|
|
29
|
+
"lines", "text", "spines", "events", "callback_registry",
|
|
28
30
|
"__version__", "use"]
|
|
29
31
|
|
|
30
32
|
|
|
31
33
|
def use(backend):
|
|
32
34
|
"""Set the rendering backend (compatibility stub)."""
|
|
33
|
-
from rustplotlib import
|
|
34
|
-
|
|
35
|
+
from rustplotlib.backends import set_backend
|
|
36
|
+
set_backend(backend)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Backend system for rustplotlib.
|
|
2
|
+
|
|
3
|
+
Manages backend selection and auto-detection:
|
|
4
|
+
- 'inline' — Jupyter/IPython (auto-detected)
|
|
5
|
+
- 'tk' — Tkinter interactive window (default for scripts)
|
|
6
|
+
- 'agg' — Non-interactive, save-only (fallback)
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
_current_backend = None # None = auto-detect
|
|
10
|
+
|
|
11
|
+
_BACKEND_REGISTRY = {
|
|
12
|
+
'agg': 'rustplotlib.backends.backend_base',
|
|
13
|
+
'inline': 'rustplotlib.backends.backend_inline',
|
|
14
|
+
'tk': 'rustplotlib.backends.backend_tk',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
from rustplotlib.backends.backend_pdf import PdfPages
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _auto_detect():
|
|
21
|
+
"""Auto-detect the best backend for the current environment."""
|
|
22
|
+
try:
|
|
23
|
+
get_ipython() # noqa: F821
|
|
24
|
+
return 'inline'
|
|
25
|
+
except NameError:
|
|
26
|
+
pass
|
|
27
|
+
return 'agg' # Default to non-interactive
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_backend():
|
|
31
|
+
"""Get the current backend name."""
|
|
32
|
+
global _current_backend
|
|
33
|
+
if _current_backend is None:
|
|
34
|
+
_current_backend = _auto_detect()
|
|
35
|
+
return _current_backend
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def set_backend(name):
|
|
39
|
+
"""Set the backend by name."""
|
|
40
|
+
global _current_backend
|
|
41
|
+
name = name.lower()
|
|
42
|
+
if name == 'tkagg':
|
|
43
|
+
name = 'tk'
|
|
44
|
+
_current_backend = name
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def list_backends():
|
|
48
|
+
"""Return list of registered backend names."""
|
|
49
|
+
return list(_BACKEND_REGISTRY.keys())
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def show_figure(figure_proxy):
|
|
53
|
+
"""Show a figure using the current backend.
|
|
54
|
+
|
|
55
|
+
Parameters
|
|
56
|
+
----------
|
|
57
|
+
figure_proxy : FigureProxy
|
|
58
|
+
The figure to show.
|
|
59
|
+
"""
|
|
60
|
+
backend = get_backend()
|
|
61
|
+
|
|
62
|
+
if backend == 'inline':
|
|
63
|
+
from rustplotlib.backends.backend_inline import display_figure
|
|
64
|
+
display_figure(figure_proxy._fig)
|
|
65
|
+
elif backend == 'tk':
|
|
66
|
+
from rustplotlib.backends.backend_tk import FigureCanvasTk, FigureManagerTk
|
|
67
|
+
canvas = FigureCanvasTk(figure_proxy)
|
|
68
|
+
manager = FigureManagerTk(canvas, 1)
|
|
69
|
+
manager.show()
|
|
70
|
+
else:
|
|
71
|
+
# agg — non-interactive, use system viewer via Rust
|
|
72
|
+
figure_proxy._fig.show()
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
"""Base classes for rustplotlib backends.
|
|
2
|
+
|
|
3
|
+
Defines the interface that all backends must implement.
|
|
4
|
+
Compatible with matplotlib's backend_bases module.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from rustplotlib.callback_registry import CallbackRegistry
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FigureCanvasBase:
|
|
11
|
+
"""Base class for figure canvases. All backends implement this.
|
|
12
|
+
|
|
13
|
+
The canvas is responsible for:
|
|
14
|
+
- Rendering the figure (draw/draw_idle)
|
|
15
|
+
- Event handling (mpl_connect/mpl_disconnect)
|
|
16
|
+
- Size queries (get_width_height)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, figure):
|
|
20
|
+
self.figure = figure
|
|
21
|
+
self.callbacks = CallbackRegistry()
|
|
22
|
+
self._is_idle_drawing = False
|
|
23
|
+
|
|
24
|
+
def draw(self):
|
|
25
|
+
"""Render the figure. Subclasses must implement."""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
def draw_idle(self):
|
|
29
|
+
"""Request a draw at the next idle time."""
|
|
30
|
+
if not self._is_idle_drawing:
|
|
31
|
+
self._is_idle_drawing = True
|
|
32
|
+
self.draw()
|
|
33
|
+
self._is_idle_drawing = False
|
|
34
|
+
|
|
35
|
+
def mpl_connect(self, event_name, callback):
|
|
36
|
+
"""Connect a callback to an event. Returns connection id."""
|
|
37
|
+
return self.callbacks.connect(event_name, callback)
|
|
38
|
+
|
|
39
|
+
def mpl_disconnect(self, cid):
|
|
40
|
+
"""Disconnect a callback by connection id."""
|
|
41
|
+
self.callbacks.disconnect(cid)
|
|
42
|
+
|
|
43
|
+
def get_width_height(self):
|
|
44
|
+
"""Return canvas width and height in pixels."""
|
|
45
|
+
fig = self.figure
|
|
46
|
+
if hasattr(fig, '_fig'):
|
|
47
|
+
# FigureProxy wrapping RustFigure
|
|
48
|
+
rust_fig = fig._fig
|
|
49
|
+
# RustFigure stores width/height directly
|
|
50
|
+
return (640, 480) # default, overridden by subclasses with actual render info
|
|
51
|
+
return (640, 480)
|
|
52
|
+
|
|
53
|
+
def flush_events(self):
|
|
54
|
+
"""Process pending GUI events."""
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
def start_event_loop(self, timeout=0):
|
|
58
|
+
"""Start a blocking event loop."""
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
def stop_event_loop(self):
|
|
62
|
+
"""Stop the current event loop."""
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class FigureManagerBase:
|
|
67
|
+
"""Base class for figure window managers.
|
|
68
|
+
|
|
69
|
+
The manager is responsible for:
|
|
70
|
+
- Creating and managing the GUI window
|
|
71
|
+
- Embedding the canvas in the window
|
|
72
|
+
- Window operations (show, destroy, resize, title)
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def __init__(self, canvas, num):
|
|
76
|
+
self.canvas = canvas
|
|
77
|
+
self.num = num
|
|
78
|
+
self._window_title = f"Figure {num}"
|
|
79
|
+
|
|
80
|
+
def show(self):
|
|
81
|
+
"""Show the figure window."""
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
def destroy(self):
|
|
85
|
+
"""Destroy the figure window."""
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
def set_window_title(self, title):
|
|
89
|
+
"""Set the window title."""
|
|
90
|
+
self._window_title = title
|
|
91
|
+
|
|
92
|
+
def resize(self, w, h):
|
|
93
|
+
"""Resize the window."""
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class NavigationToolbar2:
|
|
98
|
+
"""Base class for navigation toolbars (zoom, pan, home, save).
|
|
99
|
+
|
|
100
|
+
Manages a stack of view limits for back/forward navigation.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
def __init__(self, canvas):
|
|
104
|
+
self.canvas = canvas
|
|
105
|
+
self._nav_stack = []
|
|
106
|
+
self._current_idx = -1
|
|
107
|
+
self._active_mode = None # None, 'zoom', 'pan'
|
|
108
|
+
|
|
109
|
+
def home(self):
|
|
110
|
+
"""Reset to the original view."""
|
|
111
|
+
if self._nav_stack:
|
|
112
|
+
self._current_idx = 0
|
|
113
|
+
self._apply_view(self._nav_stack[0])
|
|
114
|
+
|
|
115
|
+
def back(self):
|
|
116
|
+
"""Go to previous view in the stack."""
|
|
117
|
+
if self._current_idx > 0:
|
|
118
|
+
self._current_idx -= 1
|
|
119
|
+
self._apply_view(self._nav_stack[self._current_idx])
|
|
120
|
+
|
|
121
|
+
def forward(self):
|
|
122
|
+
"""Go to next view in the stack."""
|
|
123
|
+
if self._current_idx < len(self._nav_stack) - 1:
|
|
124
|
+
self._current_idx += 1
|
|
125
|
+
self._apply_view(self._nav_stack[self._current_idx])
|
|
126
|
+
|
|
127
|
+
def push_current(self):
|
|
128
|
+
"""Save current view limits to the stack."""
|
|
129
|
+
fig = self.canvas.figure
|
|
130
|
+
if hasattr(fig, '_axes') and fig._axes:
|
|
131
|
+
axes_list = fig._axes if isinstance(fig._axes, list) else [fig._axes]
|
|
132
|
+
views = []
|
|
133
|
+
for ax in axes_list:
|
|
134
|
+
xlim = ax.get_xlim() if hasattr(ax, 'get_xlim') else None
|
|
135
|
+
ylim = ax.get_ylim() if hasattr(ax, 'get_ylim') else None
|
|
136
|
+
views.append((xlim, ylim))
|
|
137
|
+
self._nav_stack = self._nav_stack[:self._current_idx + 1]
|
|
138
|
+
self._nav_stack.append(views)
|
|
139
|
+
self._current_idx = len(self._nav_stack) - 1
|
|
140
|
+
|
|
141
|
+
def _apply_view(self, views):
|
|
142
|
+
"""Apply saved view limits."""
|
|
143
|
+
fig = self.canvas.figure
|
|
144
|
+
if hasattr(fig, '_axes') and fig._axes:
|
|
145
|
+
axes_list = fig._axes if isinstance(fig._axes, list) else [fig._axes]
|
|
146
|
+
for ax, (xlim, ylim) in zip(axes_list, views):
|
|
147
|
+
if xlim is not None and hasattr(ax, 'set_xlim'):
|
|
148
|
+
ax.set_xlim(*xlim)
|
|
149
|
+
if ylim is not None and hasattr(ax, 'set_ylim'):
|
|
150
|
+
ax.set_ylim(*ylim)
|
|
151
|
+
self.canvas.draw_idle()
|
|
152
|
+
|
|
153
|
+
def zoom(self):
|
|
154
|
+
"""Toggle zoom mode."""
|
|
155
|
+
self._active_mode = None if self._active_mode == 'zoom' else 'zoom'
|
|
156
|
+
|
|
157
|
+
def pan(self):
|
|
158
|
+
"""Toggle pan mode."""
|
|
159
|
+
self._active_mode = None if self._active_mode == 'pan' else 'pan'
|
|
160
|
+
|
|
161
|
+
def save_figure(self, filename=None):
|
|
162
|
+
"""Save the figure to a file."""
|
|
163
|
+
if filename:
|
|
164
|
+
self.canvas.figure.savefig(filename)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Jupyter inline display backend for rustplotlib."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
|
|
5
|
+
_figure_format = 'png'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_figure_format():
|
|
9
|
+
"""Get the current inline figure format ('png' or 'svg')."""
|
|
10
|
+
return _figure_format
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def set_figure_format(fmt):
|
|
14
|
+
"""Set the inline figure format ('png', 'svg', or 'retina')."""
|
|
15
|
+
if fmt not in ('png', 'svg', 'retina'):
|
|
16
|
+
raise ValueError(f"Unsupported figure format: {fmt!r}. Use 'png', 'svg', or 'retina'.")
|
|
17
|
+
global _figure_format
|
|
18
|
+
_figure_format = fmt
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SVGWrapper:
|
|
22
|
+
"""Wrapper for SVG string display in Jupyter."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, data):
|
|
25
|
+
self.data = data
|
|
26
|
+
|
|
27
|
+
def _repr_svg_(self):
|
|
28
|
+
return self.data
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class PNGWrapper:
|
|
32
|
+
"""Wrapper for PNG bytes display in Jupyter."""
|
|
33
|
+
|
|
34
|
+
def __init__(self, data):
|
|
35
|
+
self.data = data
|
|
36
|
+
|
|
37
|
+
def _repr_png_(self):
|
|
38
|
+
return self.data
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def display_figure(fig, display_func=None):
|
|
42
|
+
"""Display a figure inline in Jupyter.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
fig : RustFigure
|
|
47
|
+
The Rust figure object (not the FigureProxy).
|
|
48
|
+
display_func : callable, optional
|
|
49
|
+
Custom display function (for testing). Defaults to IPython.display.display.
|
|
50
|
+
"""
|
|
51
|
+
if display_func is None:
|
|
52
|
+
try:
|
|
53
|
+
from IPython.display import display as display_func
|
|
54
|
+
except ImportError:
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
fmt = get_figure_format()
|
|
58
|
+
|
|
59
|
+
if fmt == 'svg':
|
|
60
|
+
svg_str = fig.render_to_svg_string()
|
|
61
|
+
display_func(SVGWrapper(svg_str))
|
|
62
|
+
else:
|
|
63
|
+
# 'png' or 'retina'
|
|
64
|
+
png_bytes = fig.render_to_png_bytes()
|
|
65
|
+
display_func(PNGWrapper(bytes(png_bytes)))
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def configure_inline_support(shell=None, backend=None):
|
|
69
|
+
"""Configure Jupyter to display rustplotlib figures inline."""
|
|
70
|
+
pass
|