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.
Files changed (112) hide show
  1. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/CLAUDE.md +18 -11
  2. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/Cargo.lock +1 -1
  3. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/Cargo.toml +1 -1
  4. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/PKG-INFO +32 -10
  5. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/README.md +31 -9
  6. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/ROADMAP.md +39 -10
  7. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/pyproject.toml +1 -1
  8. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/__init__.py +5 -3
  9. rustplotlib-5.0.0/python/rustplotlib/backends/__init__.py +72 -0
  10. rustplotlib-5.0.0/python/rustplotlib/backends/backend_base.py +164 -0
  11. rustplotlib-5.0.0/python/rustplotlib/backends/backend_inline.py +70 -0
  12. rustplotlib-5.0.0/python/rustplotlib/backends/backend_tk.py +385 -0
  13. rustplotlib-5.0.0/python/rustplotlib/callback_registry.py +37 -0
  14. rustplotlib-5.0.0/python/rustplotlib/events.py +82 -0
  15. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/pyplot.py +39 -22
  16. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/figure.rs +23 -0
  17. rustplotlib-5.0.0/tests/test_backend_tk.py +187 -0
  18. rustplotlib-5.0.0/tests/test_events.py +173 -0
  19. rustplotlib-5.0.0/tests/test_jupyter_repr.py +98 -0
  20. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_phase6_7.py +2 -1
  21. rustplotlib-4.0.1/python/rustplotlib/backends/__init__.py +0 -8
  22. rustplotlib-4.0.1/python/rustplotlib/backends/backend_inline.py +0 -24
  23. rustplotlib-4.0.1/tests/test_jupyter_repr.py +0 -15
  24. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/.github/workflows/ci.yml +0 -0
  25. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/.gitignore +0 -0
  26. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/CONTRIBUTING.md +0 -0
  27. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/LICENSE +0 -0
  28. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/benchmark_results.csv +0 -0
  29. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/dados/Data/PerfisTemp_rustplotlib.py +0 -0
  30. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/docs/superpowers/plans/2026-04-04-jupyter-and-event-foundation.md +0 -0
  31. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/animation.py +0 -0
  32. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/axes/__init__.py +0 -0
  33. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/backends/backend_pdf.py +0 -0
  34. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/cm.py +0 -0
  35. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/collections.py +0 -0
  36. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/colors.py +0 -0
  37. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/cycler.py +0 -0
  38. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/dates.py +0 -0
  39. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/figure.py +0 -0
  40. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/font_manager.py +0 -0
  41. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/gridspec.py +0 -0
  42. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/lines.py +0 -0
  43. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/mpl_toolkits/__init__.py +0 -0
  44. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/mpl_toolkits/mplot3d/__init__.py +0 -0
  45. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/patches.py +0 -0
  46. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/patheffects.py +0 -0
  47. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/pyplot.pyi +0 -0
  48. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/spines.py +0 -0
  49. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/style/__init__.py +0 -0
  50. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/text.py +0 -0
  51. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/ticker.py +0 -0
  52. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/transforms.py +0 -0
  53. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/python/rustplotlib/widgets.py +0 -0
  54. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/arrow.rs +0 -0
  55. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/bar.rs +0 -0
  56. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/bar3d.rs +0 -0
  57. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/barh.rs +0 -0
  58. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/boxplot.rs +0 -0
  59. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/broken_barh.rs +0 -0
  60. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/contour.rs +0 -0
  61. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/contour3d.rs +0 -0
  62. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/errorbar.rs +0 -0
  63. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/eventplot.rs +0 -0
  64. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/fill_between.rs +0 -0
  65. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/fill_betweenx.rs +0 -0
  66. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/fill_polygon.rs +0 -0
  67. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/hexbin.rs +0 -0
  68. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/hist.rs +0 -0
  69. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/image.rs +0 -0
  70. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/legend.rs +0 -0
  71. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/line2d.rs +0 -0
  72. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/line3d.rs +0 -0
  73. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/mod.rs +0 -0
  74. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/patches.rs +0 -0
  75. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/pcolormesh.rs +0 -0
  76. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/pie.rs +0 -0
  77. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/quiver.rs +0 -0
  78. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/radar.rs +0 -0
  79. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/sankey.rs +0 -0
  80. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/scatter.rs +0 -0
  81. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/scatter3d.rs +0 -0
  82. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/stem.rs +0 -0
  83. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/step.rs +0 -0
  84. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/streamplot.rs +0 -0
  85. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/surface3d.rs +0 -0
  86. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/trisurf3d.rs +0 -0
  87. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/violin.rs +0 -0
  88. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/artists/wireframe3d.rs +0 -0
  89. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/axes.rs +0 -0
  90. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/axes3d.rs +0 -0
  91. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/colors.rs +0 -0
  92. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/fonts/DejaVuSans.ttf +0 -0
  93. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/lib.rs +0 -0
  94. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/projection3d.rs +0 -0
  95. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/svg_renderer.rs +0 -0
  96. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/text.rs +0 -0
  97. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/ticker.rs +0 -0
  98. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/transforms.rs +0 -0
  99. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/src/window.rs +0 -0
  100. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_3d.py +0 -0
  101. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_benchmark.py +0 -0
  102. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_colors.py +0 -0
  103. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_figure.py +0 -0
  104. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_new_features.py +0 -0
  105. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_new_features2.py +0 -0
  106. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_phase8.py +0 -0
  107. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_phase9_features.py +0 -0
  108. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_pyplot.py +0 -0
  109. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_spectral_features.py +0 -0
  110. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_ticker.py +0 -0
  111. {rustplotlib-4.0.1 → rustplotlib-5.0.0}/tests/test_transforms.py +0 -0
  112. {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 — v4.0.0
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
- - 21 módulos Python compatíveis com API matplotlib
14
- - 267 testes passando
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é 30x mais rápido que matplotlib
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 (267 testes)
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.0.0)
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. **Jupyter inline backend** — rich display protocol, _repr_png_
103
- 2. **Backends interativos** — Qt, GTK, macOS native
104
- 3. **Widgets funcionais** — Slider, Button, CheckButtons com renderização real
105
- 4. **Features interativas** — mouse events, zoom/pan, rotação 3D
106
- 5. **Triangulation plots** — tricontour, tricontourf, tripcolor (atualmente stubs)
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
 
@@ -1368,7 +1368,7 @@ dependencies = [
1368
1368
 
1369
1369
  [[package]]
1370
1370
  name = "rustplotlib"
1371
- version = "4.0.1"
1371
+ version = "5.0.0"
1372
1372
  dependencies = [
1373
1373
  "ab_glyph",
1374
1374
  "numpy",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rustplotlib"
3
- version = "4.0.1"
3
+ version = "5.0.0"
4
4
  edition = "2021"
5
5
  readme = "README.md"
6
6
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rustplotlib
3
- Version: 4.0.1
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 display |
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 (21 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.figure` | Figure class reference |
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
- - **21 Python modules** — 8,000+ lines of API
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
- - **267 tests** passing
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 display |
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 (21 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.figure` | Figure class reference |
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
- - **21 Python modules** — 8,000+ lines of API
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
- - **267 tests** passing
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
- ## PLANNED — v5.0.0
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
- ### Functional Backends
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
- - [ ] Mouse events (click, motion, scroll, pick)
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. Jupyter backend (v5.0.0) — huge for data scientists
169
- 2. Interactive features (v5.0.0) — needed for exploratory analysis
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.0.0) — tricontour, tripcolor
200
+ 4. Triangulation plots (v5.1.0) — tricontour, tripcolor
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "rustplotlib"
7
- version = "4.0.1"
7
+ version = "5.0.0"
8
8
  description = "Matplotlib drop-in replacement powered by Rust"
9
9
  requires-python = ">=3.9"
10
10
  dependencies = ["numpy"]
@@ -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 backends as _backends
34
- _backends._current_backend = backend.lower()
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