babelplot 2025.7__tar.gz → 2025.9__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 (48) hide show
  1. {babelplot-2025.7 → babelplot-2025.9}/PKG-INFO +1 -1
  2. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/__init__.py +1 -1
  3. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/backend/catalog/bokeh_.py +89 -25
  4. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/backend/catalog/matplotlib_.py +46 -45
  5. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/backend/catalog/plotly_.py +36 -40
  6. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/backend/catalog/vedo_.py +36 -12
  7. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/backend/helper/validation.py +53 -1
  8. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/constant/path.py +1 -1
  9. babelplot-2025.7/package/babelplot/runtime/figure.py → babelplot-2025.9/package/babelplot/runtime/application.py +7 -2
  10. babelplot-2025.9/package/babelplot/task/color.py +112 -0
  11. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/task/help.py +6 -2
  12. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/task/instantiation.py +2 -1
  13. babelplot-2025.9/package/babelplot/task/plotting.py +85 -0
  14. babelplot-2025.9/package/babelplot/task/showing.py +116 -0
  15. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/type/backend.py +23 -20
  16. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/type/ffp_base.py +3 -3
  17. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/type/figure.py +17 -20
  18. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/type/frame.py +2 -1
  19. babelplot-2025.7/package/babelplot/type/translator.py → babelplot-2025.9/package/babelplot/type/plot_function.py +6 -4
  20. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/type/plot_type.py +3 -60
  21. babelplot-2025.9/package/babelplot/type/translator.py +70 -0
  22. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/version.py +1 -1
  23. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot.egg-info/PKG-INFO +1 -1
  24. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot.egg-info/SOURCES.txt +4 -1
  25. babelplot-2025.7/package/babelplot/task/showing.py +0 -137
  26. {babelplot-2025.7 → babelplot-2025.9}/MANIFEST.in +0 -0
  27. {babelplot-2025.7 → babelplot-2025.9}/README-COPYRIGHT-utf8.txt +0 -0
  28. {babelplot-2025.7 → babelplot-2025.9}/README-LICENCE-utf8.txt +0 -0
  29. {babelplot-2025.7 → babelplot-2025.9}/README.rst +0 -0
  30. {babelplot-2025.7 → babelplot-2025.9}/documentation/wiki/description.asciidoc +0 -0
  31. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/api/backend.py +0 -0
  32. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/api/definition.py +0 -0
  33. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/api/help.py +0 -0
  34. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/backend/helper/creation.py +0 -0
  35. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/constant/backend.py +0 -0
  36. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/constant/project.py +0 -0
  37. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/extension/enum_.py +0 -0
  38. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/extension/function.py +0 -0
  39. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/runtime/backends.py +0 -0
  40. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/type/dimension.py +0 -0
  41. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot/type/plot.py +0 -0
  42. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot.egg-info/dependency_links.txt +0 -0
  43. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot.egg-info/requires.txt +0 -0
  44. {babelplot-2025.7 → babelplot-2025.9}/package/babelplot.egg-info/top_level.txt +0 -0
  45. {babelplot-2025.7 → babelplot-2025.9}/pyproject.toml +0 -0
  46. {babelplot-2025.7 → babelplot-2025.9}/requirements.txt +0 -0
  47. {babelplot-2025.7 → babelplot-2025.9}/setup.cfg +0 -0
  48. {babelplot-2025.7 → babelplot-2025.9}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: babelplot
3
- Version: 2025.7
3
+ Version: 2025.9
4
4
  Summary: A Meta Plotting Library That Speaks Several Backends
5
5
  Home-page: https://src.koda.cnrs.fr/eric.debreuve/babelplot/
6
6
  Author: Eric Debreuve
@@ -18,7 +18,7 @@ else:
18
18
  beartype_this_package()
19
19
 
20
20
  from babelplot.task.instantiation import NewFigure, NewPlot
21
- from babelplot.task.showing import ShowAllFigures
21
+ from babelplot.task.showing import ShowFigures
22
22
  from babelplot.type.dimension import dim_e
23
23
  from babelplot.type.plot_type import plot_e
24
24
  from babelplot.version import __version__
@@ -7,26 +7,27 @@ SEE COPYRIGHT NOTICE BELOW
7
7
  import dataclasses as d
8
8
  import typing as h
9
9
 
10
- from babelplot.task.showing import ShowHTMLWithPyQt
10
+ from babelplot.runtime.application import QT_APPLICATION
11
+ from babelplot.task.plotting import NewPlotFunctionsTemplate, SetDefaultPlotFunction
12
+ from babelplot.task.showing import NewPyQtWidgetWithHTML, widget_t
11
13
  from babelplot.type.dimension import dim_e
12
14
  from babelplot.type.figure import figure_t as base_figure_t
13
15
  from babelplot.type.frame import frame_t as base_frame_t
14
16
  from babelplot.type.plot import plot_t as base_plot_t
15
- from babelplot.type.plot_type import plot_e, plot_function_h
17
+ from babelplot.type.plot_function import plot_function_h
18
+ from babelplot.type.plot_type import plot_e
16
19
  from bokeh.embed import file_html as HTMLofBackendContent # noqa
17
20
  from bokeh.layouts import column as NewBackendColLayout # noqa
18
21
  from bokeh.layouts import grid as NewBackendGridLayout # noqa
19
22
  from bokeh.layouts import row as NewBackendRowLayout # noqa
20
23
  from bokeh.models.renderers import GlyphRenderer as backend_plot_t # noqa
21
- from bokeh.plotting import figure as backend_figure_t # noqa
24
+ from bokeh.plotting import figure as backend_frame_t # noqa
22
25
  from bokeh.resources import INLINE # noqa
23
26
 
24
- # from bokeh.layouts import LayoutDOM as backend_content_t # noqa
25
-
26
27
  NAME = "bokeh"
27
28
 
28
29
 
29
- backend_frame_t = backend_figure_t
30
+ class backend_figure_t: ...
30
31
 
31
32
 
32
33
  @d.dataclass(slots=True, repr=False, eq=False)
@@ -44,26 +45,23 @@ class frame_t(base_frame_t):
44
45
  **kwargs,
45
46
  ) -> plot_t:
46
47
  """"""
47
- output = plot_t(
48
+ return plot_t(
48
49
  title=title,
49
50
  property=kwargs.copy(),
50
51
  backend_name=self.backend_name,
52
+ raw=plot_function(self.raw, *args, **kwargs),
51
53
  )
52
54
 
53
- output.raw = plot_function(self.raw, *args, **kwargs)
54
-
55
- return output
56
-
57
55
 
58
56
  @d.dataclass(slots=True, repr=False, eq=False)
59
57
  class figure_t(base_figure_t):
60
- layout: h.Any = None
61
58
 
62
- _BackendShow = ShowHTMLWithPyQt
59
+ widget: widget_t | None = d.field(init=False, default=None)
60
+ layout: h.Any = d.field(init=False, default=None)
63
61
 
64
62
  def _NewBackendFigure(self, *args, **kwargs) -> backend_figure_t:
65
63
  """"""
66
- return backend_figure_t(*args, **kwargs)
64
+ return backend_figure_t() # self.raw is never used.
67
65
 
68
66
  def _NewFrame(
69
67
  self,
@@ -75,23 +73,45 @@ class figure_t(base_figure_t):
75
73
  **kwargs,
76
74
  ) -> frame_t:
77
75
  """"""
78
- output = frame_t(
76
+ return frame_t(
79
77
  title=title,
80
78
  dim=dim,
81
79
  backend_name=self.backend_name,
80
+ raw=backend_frame_t(title=title, **kwargs),
82
81
  )
83
82
 
84
- output.raw = backend_frame_t(*args, title=title, **kwargs)
85
-
86
- return output
87
-
88
83
  def AdjustLayout(self) -> None:
89
84
  """"""
85
+ # arranged_frames must be composed of list since Bokeh does not support tuples
86
+ # here!
90
87
  n_rows, n_cols = self.shape
91
88
  arranged_frames = [n_cols * [None] for _ in range(n_rows)]
92
89
  for frame, (row, col) in zip(self.frames, self.locations):
93
- arranged_frames[row][col] = frame.raw
94
- arranged_frames: list[list[backend_frame_t]]
90
+ raw = frame.raw
91
+ if raw.renderers.__len__() == 0:
92
+ _ = backend_frame_t.text(
93
+ raw,
94
+ x=(0,),
95
+ y=(0,),
96
+ text=("Empty Frame",),
97
+ text_font_size="30px",
98
+ text_color="#FF0000",
99
+ )
100
+ arranged_frames[row][col] = raw
101
+
102
+ # Bokeh does not support inserting None as an indicator of empty space. As a
103
+ # workaround, the frames are currently flattened (and None_s are filtered out).
104
+ should_be_filtered = False
105
+ for one_row in arranged_frames:
106
+ if any(_ is None for _ in one_row):
107
+ should_be_filtered = True
108
+ break
109
+ if should_be_filtered:
110
+ arranged_frames = [_ for one_row in arranged_frames for _ in one_row]
111
+ arranged_frames = list(filter(lambda _: _ is not None, arranged_frames))
112
+ arranged_frames = [arranged_frames]
113
+ n_rows = 1
114
+ n_cols = arranged_frames[0].__len__()
95
115
 
96
116
  if n_rows > 1:
97
117
  if n_cols > 1:
@@ -104,17 +124,61 @@ class figure_t(base_figure_t):
104
124
 
105
125
  self.layout = layout
106
126
 
107
- def AsHTML(self) -> str:
127
+ def _BackendShow(self, modal: bool, /) -> None:
108
128
  """"""
109
- return HTMLofBackendContent(self.layout, INLINE)
129
+ try:
130
+ html = HTMLofBackendContent(self.layout, INLINE)
131
+ except ValueError as exception:
132
+ html = f"""
133
+ <!DOCTYPE html>
134
+ <html>
135
+ <body>
136
+ <h1>Figure cannot be shown</h1>
137
+ <p>{str(exception)}</p>
138
+ </body>
139
+ </html>
140
+ """
141
+ self.widget = NewPyQtWidgetWithHTML(html)
142
+
143
+ if modal:
144
+ QT_APPLICATION.exec()
145
+
146
+
147
+ def _DefaultFunction(type_: plot_e, frame_dim: int, /) -> plot_function_h:
148
+ """"""
149
+
150
+ def Actual(frame: backend_frame_t, *args, **kwargs) -> backend_plot_t:
151
+ """"""
152
+ args = ", ".join(map(lambda _: type(_).__name__, args))
153
+ kwargs = ", ".join(
154
+ f"{_key}={type(_vle).__name__}" for _key, _vle in kwargs.items()
155
+ )
156
+
157
+ return backend_frame_t.text(
158
+ frame,
159
+ x=(0,),
160
+ y=(0,),
161
+ text=(
162
+ f"Unhandled Plot Request\n{type_.name}.{frame_dim}\nargs: {args}\nkwargs: {kwargs}",
163
+ ),
164
+ text_font_size="30px",
165
+ text_align="center",
166
+ text_color="#FF0000",
167
+ )
168
+
169
+ return Actual
110
170
 
111
171
 
112
- PLOTS = plot_e.NewPlotsTemplate()
113
- PLOTS[plot_e.SCATTER][1] = backend_frame_t.scatter
172
+ PLOTS = NewPlotFunctionsTemplate()
173
+ PLOTS[plot_e.SCATTER][0] = backend_frame_t.scatter
174
+ SetDefaultPlotFunction(PLOTS, _DefaultFunction)
114
175
 
115
176
 
116
177
  TRANSLATIONS = {
117
178
  "color_face": "fill_color",
179
+ "opacity": "alpha",
180
+ ("AddFrame", "azimuth"): None,
181
+ ("AddFrame", "elevation"): None,
118
182
  }
119
183
 
120
184
 
@@ -5,17 +5,22 @@ SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
7
  import dataclasses as d
8
+ import importlib as mprt
8
9
  import typing as h
9
10
 
10
11
  import matplotlib.pyplot as pypl # noqa
11
12
  import numpy as nmpy
12
13
  import skimage.measure as msre
14
+ from babelplot.constant.path import QT_RUNTIME_PY_PATH
15
+ from babelplot.task.color import NewConvertedColor
16
+ from babelplot.task.plotting import NewPlotFunctionsTemplate
13
17
  from babelplot.type.dimension import dim_e
14
18
  from babelplot.type.ffp_base import backend_element_h
15
19
  from babelplot.type.figure import figure_t as base_figure_t
16
20
  from babelplot.type.frame import frame_t as base_frame_t
17
21
  from babelplot.type.plot import plot_t as base_plot_t
18
- from babelplot.type.plot_type import plot_e, plot_function_h, plot_type_h
22
+ from babelplot.type.plot_function import plot_function_h
23
+ from babelplot.type.plot_type import plot_e, plot_type_h
19
24
  from logger_36 import L
20
25
  from matplotlib.artist import Artist as backend_plot_t # noqa
21
26
  from matplotlib.container import Container as backend_plots_t # noqa
@@ -27,6 +32,7 @@ from matplotlib.pyplot import Figure as backend_figure_t # noqa
27
32
  from matplotlib.pyplot import figure as NewBackendFigure # noqa
28
33
  from mpl_toolkits.mplot3d import Axes3D as backend_frame_3d_t # noqa
29
34
 
35
+ import matplotlib as mtpl # noqa
30
36
  import matplotlib as mlpl # noqa
31
37
 
32
38
  NAME = "matplotlib"
@@ -108,16 +114,13 @@ class frame_t(base_frame_t):
108
114
  **kwargs,
109
115
  ) -> plot_t:
110
116
  """"""
111
- output = plot_t(
117
+ return plot_t(
112
118
  title=title,
113
119
  property=kwargs.copy(),
114
120
  backend_name=self.backend_name,
121
+ raw=plot_function(self.raw, *args, **kwargs),
115
122
  )
116
123
 
117
- output.raw = plot_function(self.raw, *args, **kwargs)
118
-
119
- return output
120
-
121
124
 
122
125
  @d.dataclass(slots=True, repr=False, eq=False)
123
126
  class figure_t(base_figure_t):
@@ -126,11 +129,7 @@ class figure_t(base_figure_t):
126
129
 
127
130
  def _NewBackendFigure(self, *args, **kwargs) -> backend_figure_t:
128
131
  """"""
129
- output = NewBackendFigure(*args, num=None, **kwargs)
130
-
131
- output.canvas.mpl_connect("close_event", lambda *_, **__: _OnCloseEvent(self))
132
-
133
- return output
132
+ return NewBackendFigure(*args, num=None, **kwargs)
134
133
 
135
134
  def _NewFrame(
136
135
  self,
@@ -204,25 +203,27 @@ class figure_t(base_figure_t):
204
203
 
205
204
  def _BackendShow(self, modal: bool, /) -> None:
206
205
  """"""
207
- raw = self.raw
208
- raw.show()
206
+ self.raw.show()
209
207
 
210
208
  if modal:
211
- title = raw.get_suptitle()
212
- if title.__len__() > 0:
213
- title = f"{title} [MASTER FIGURE]"
209
+ if "qt" in mtpl.get_backend().lower():
210
+ module = mprt.import_module(QT_RUNTIME_PY_PATH)
211
+ module.QT_APPLICATION.exec()
214
212
  else:
215
- title = "MASTER FIGURE"
216
- raw.suptitle(title)
213
+ event_manager = self.raw.canvas
214
+ event_manager.mpl_connect("close_event", _OnCloseEvent)
215
+ event_manager.start_event_loop()
217
216
 
218
- raw.canvas.start_event_loop()
219
217
 
220
-
221
- def _OnCloseEvent(figure: figure_t, /) -> None:
218
+ def _OnCloseEvent(event: h.Any, /) -> None:
222
219
  """"""
223
- # Hopefully, the event loop does not mind being stopped several times.
224
- figure.raw.canvas.stop_event_loop()
225
- figure.AcknowledgeClosure()
220
+ event.canvas.stop_event_loop()
221
+
222
+ figures = tuple(map(pypl.figure, pypl.get_fignums()))
223
+ if figures.__len__() > 0:
224
+ event_manager = figures[0].canvas
225
+ event_manager.mpl_connect("close_event", _OnCloseEvent)
226
+ event_manager.start_event_loop()
226
227
 
227
228
 
228
229
  def _Arrows2(frame: backend_frame_2d_t, *args, **kwargs) -> backend_plot_t:
@@ -390,26 +391,26 @@ def _Text3(frame: backend_frame_2d_t, text, x, y, z, *_, **kwargs) -> backend_pl
390
391
  return frame.text(x, y, z, text, **kwargs)
391
392
 
392
393
 
393
- PLOTS = plot_e.NewPlotsTemplate()
394
-
395
- PLOTS[plot_e.ARROWS][1] = _Arrows2
396
- PLOTS[plot_e.ARROWS][2] = _Arrows3
397
- PLOTS[plot_e.BAR3][2] = _Bar3
398
- PLOTS[plot_e.BARH][1] = _BarH
399
- PLOTS[plot_e.BARV][1] = _BarV
400
- PLOTS[plot_e.ELEVATION][2] = _ElevationSurface
401
- PLOTS[plot_e.IMAGE][1] = backend_frame_2d_t.matshow
402
- PLOTS[plot_e.ISOSET][1] = _IsoContour
403
- PLOTS[plot_e.ISOSET][2] = _IsoSurface
404
- PLOTS[plot_e.MESH][2] = _Mesh
405
- PLOTS[plot_e.PIE][1] = backend_frame_2d_t.pie
406
- PLOTS[plot_e.POLYGON][1] = _Polygon
407
- PLOTS[plot_e.POLYLINE][1] = backend_frame_2d_t.plot
408
- PLOTS[plot_e.POLYLINE][2] = backend_frame_3d_t.plot
409
- PLOTS[plot_e.SCATTER][1] = backend_frame_2d_t.scatter
410
- PLOTS[plot_e.SCATTER][2] = backend_frame_3d_t.scatter
411
- PLOTS[plot_e.TEXT][1] = _Text2
412
- PLOTS[plot_e.TEXT][2] = _Text3
394
+ PLOTS = NewPlotFunctionsTemplate()
395
+
396
+ PLOTS[plot_e.ARROWS][0] = _Arrows2
397
+ PLOTS[plot_e.ARROWS][1] = _Arrows3
398
+ PLOTS[plot_e.BAR3][1] = _Bar3
399
+ PLOTS[plot_e.BARH][0] = _BarH
400
+ PLOTS[plot_e.BARV][0] = _BarV
401
+ PLOTS[plot_e.ELEVATION][1] = _ElevationSurface
402
+ PLOTS[plot_e.IMAGE][0] = backend_frame_2d_t.matshow
403
+ PLOTS[plot_e.ISOSET][0] = _IsoContour
404
+ PLOTS[plot_e.ISOSET][1] = _IsoSurface
405
+ PLOTS[plot_e.MESH][1] = _Mesh
406
+ PLOTS[plot_e.PIE][0] = backend_frame_2d_t.pie
407
+ PLOTS[plot_e.POLYGON][0] = _Polygon
408
+ PLOTS[plot_e.POLYLINE][0] = backend_frame_2d_t.plot
409
+ PLOTS[plot_e.POLYLINE][1] = backend_frame_3d_t.plot
410
+ PLOTS[plot_e.SCATTER][0] = backend_frame_2d_t.scatter
411
+ PLOTS[plot_e.SCATTER][1] = backend_frame_3d_t.scatter
412
+ PLOTS[plot_e.TEXT][0] = _Text2
413
+ PLOTS[plot_e.TEXT][1] = _Text3
413
414
 
414
415
 
415
416
  TRANSLATIONS = {
@@ -437,7 +438,7 @@ TRANSLATIONS = {
437
438
  (_ElevationSurface, "color_face"): "color",
438
439
  (_IsoContour, "color"): "colors",
439
440
  (_IsoSurface, "color_face"): "color",
440
- (_Mesh, "color_face"): "color",
441
+ (_Mesh, "color_face"): ("color", lambda _: NewConvertedColor(_, "hex", 0)),
441
442
  (_Polygon, "color_edge"): "edgecolor",
442
443
  (_Polygon, "color_face"): "facecolor",
443
444
  (backend_frame_2d_t.pie, "color"): "colors",
@@ -5,20 +5,21 @@ SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
7
  import dataclasses as d
8
- import gzip
9
8
  import typing as h
10
- from multiprocessing import Process as process_t
11
9
  from pathlib import Path as path_t
12
10
 
13
11
  import numpy as nmpy
14
12
  import plotly.figure_factory as fcry # noqa
15
13
  import plotly.graph_objects as plly # noqa
16
- from babelplot.task.showing import ShowHTMLWithPyQt
14
+ from babelplot.runtime.application import QT_APPLICATION
15
+ from babelplot.task.plotting import NewPlotFunctionsTemplate
16
+ from babelplot.task.showing import NewPyQtWidgetWithHTML, widget_t
17
17
  from babelplot.type.dimension import dim_e
18
18
  from babelplot.type.figure import figure_t as base_figure_t
19
19
  from babelplot.type.frame import frame_t as base_frame_t
20
20
  from babelplot.type.plot import plot_t as base_plot_t
21
- from babelplot.type.plot_type import plot_e, plot_function_h
21
+ from babelplot.type.plot_function import plot_function_h
22
+ from babelplot.type.plot_type import plot_e
22
23
  from plotly.basedatatypes import BasePlotlyType as backend_plot_t # noqa
23
24
  from plotly.graph_objects import Figure as backend_figure_t # noqa
24
25
  from plotly.subplots import make_subplots as NewMultiAxesFigure # noqa
@@ -30,7 +31,7 @@ array_t = nmpy.ndarray
30
31
 
31
32
 
32
33
  _FIGURE_CONFIG = {
33
- "toImageButtonOptions": { # TODO: Does not work: no PNG export produced
34
+ "toImageButtonOptions": { # TODO: No PNG export produced actually.
34
35
  "filename": str(path_t.home() / "plotly_figure"),
35
36
  "height": None,
36
37
  "width": None,
@@ -54,20 +55,19 @@ class frame_t(base_frame_t):
54
55
  **kwargs,
55
56
  ) -> plot_t:
56
57
  """"""
57
- output = plot_t(
58
+ return plot_t(
58
59
  title=title,
59
60
  property=kwargs.copy(),
60
61
  backend_name=self.backend_name,
62
+ raw=plot_function(*args, **kwargs),
61
63
  )
62
64
 
63
- output.raw = plot_function(*args, **kwargs)
64
-
65
- return output
66
-
67
65
 
68
66
  @d.dataclass(slots=True, repr=False, eq=False)
69
67
  class figure_t(base_figure_t):
70
68
 
69
+ widget: widget_t | None = d.field(init=False, default=None)
70
+
71
71
  def _NewBackendFigure(self, *args, **kwargs) -> backend_figure_t:
72
72
  """"""
73
73
  return backend_figure_t(*args, **kwargs)
@@ -138,19 +138,15 @@ class figure_t(base_figure_t):
138
138
  Note on include_plotlyjs:
139
139
  - "cdn": works but must be online.
140
140
  - True => blank figure if using
141
- PySide6.QtWebEngineWidgets.QWebEngineView.setHtml because of html size limit.
142
- See note in babelplot.task.html.Show.
141
+ PySide6.QtWebEngineWidgets.QWebEngineView.setHtml because of html size
142
+ limit.
143
+ See note in babelplot.task.html.Show.
143
144
  """
144
145
  html = self.raw.to_html(include_plotlyjs=True, config=_FIGURE_CONFIG)
145
- uid = id(self)
146
+ self.widget = NewPyQtWidgetWithHTML(html)
147
+
146
148
  if modal:
147
- ShowHTMLWithPyQt(html, uid)
148
- self.AcknowledgeClosure()
149
- else:
150
- html = gzip.compress(html.encode())
151
- process = process_t(target=lambda: ShowHTMLWithPyQt(html, uid))
152
- process.start()
153
- self.showing_process = process
149
+ QT_APPLICATION.exec()
154
150
 
155
151
 
156
152
  def _Arrows2(*args, **kwargs) -> backend_plot_t:
@@ -456,26 +452,26 @@ def _Text3(text: str, x: float, y: float, z: float, *_, **kwargs) -> backend_plo
456
452
  )
457
453
 
458
454
 
459
- PLOTS = plot_e.NewPlotsTemplate()
460
-
461
- PLOTS[plot_e.ARROWS][1] = _Arrows2
462
- PLOTS[plot_e.ARROWS][2] = _Arrows3
463
- PLOTS[plot_e.BAR3][2] = _Bar3
464
- PLOTS[plot_e.BARH][1] = _BarH
465
- PLOTS[plot_e.BARV][1] = _BarV
466
- PLOTS[plot_e.ELEVATION][2] = _ElevationSurface
467
- PLOTS[plot_e.IMAGE][1] = _Image
468
- PLOTS[plot_e.ISOSET][1] = _IsoContour
469
- PLOTS[plot_e.ISOSET][2] = _IsoSurface
470
- PLOTS[plot_e.MESH][2] = _Mesh
471
- PLOTS[plot_e.PIE][1] = _Pie
472
- PLOTS[plot_e.POLYGON][1] = _Polygon
473
- PLOTS[plot_e.POLYLINE][1] = _Polyline2
474
- PLOTS[plot_e.POLYLINE][2] = _Polyline3
475
- PLOTS[plot_e.SCATTER][1] = _Scatter2
476
- PLOTS[plot_e.SCATTER][2] = _Scatter3
477
- PLOTS[plot_e.TEXT][1] = _Text2
478
- PLOTS[plot_e.TEXT][2] = _Text3
455
+ PLOTS = NewPlotFunctionsTemplate()
456
+
457
+ PLOTS[plot_e.ARROWS][0] = _Arrows2
458
+ PLOTS[plot_e.ARROWS][1] = _Arrows3
459
+ PLOTS[plot_e.BAR3][1] = _Bar3
460
+ PLOTS[plot_e.BARH][0] = _BarH
461
+ PLOTS[plot_e.BARV][0] = _BarV
462
+ PLOTS[plot_e.ELEVATION][1] = _ElevationSurface
463
+ PLOTS[plot_e.IMAGE][0] = _Image
464
+ PLOTS[plot_e.ISOSET][0] = _IsoContour
465
+ PLOTS[plot_e.ISOSET][1] = _IsoSurface
466
+ PLOTS[plot_e.MESH][1] = _Mesh
467
+ PLOTS[plot_e.PIE][0] = _Pie
468
+ PLOTS[plot_e.POLYGON][0] = _Polygon
469
+ PLOTS[plot_e.POLYLINE][0] = _Polyline2
470
+ PLOTS[plot_e.POLYLINE][1] = _Polyline3
471
+ PLOTS[plot_e.SCATTER][0] = _Scatter2
472
+ PLOTS[plot_e.SCATTER][1] = _Scatter3
473
+ PLOTS[plot_e.TEXT][0] = _Text2
474
+ PLOTS[plot_e.TEXT][1] = _Text3
479
475
 
480
476
 
481
477
  def _ApplyGlobalTranslation(
@@ -6,12 +6,14 @@ SEE COPYRIGHT NOTICE BELOW
6
6
 
7
7
  import dataclasses as d
8
8
 
9
- import vedo # noqa
9
+ from babelplot.task.color import NewConvertedColor
10
+ from babelplot.task.plotting import NewPlotFunctionsTemplate
10
11
  from babelplot.type.dimension import dim_e
11
12
  from babelplot.type.figure import figure_t as base_figure_t
12
13
  from babelplot.type.frame import frame_t as base_frame_t
13
14
  from babelplot.type.plot import plot_t as base_plot_t
14
- from babelplot.type.plot_type import plot_e, plot_function_h
15
+ from babelplot.type.plot_function import plot_function_h
16
+ from babelplot.type.plot_type import plot_e
15
17
  from numpy import ndarray as array_t
16
18
  from vedo import Mesh as backend_plot_t # noqa
17
19
  from vedo import Plotter as backend_figure_t # noqa
@@ -39,12 +41,11 @@ class frame_t(base_frame_t):
39
41
  title=title,
40
42
  property=kwargs.copy(),
41
43
  backend_name=self.backend_name,
44
+ raw=plot_function(*args, **kwargs),
42
45
  )
43
46
 
44
- raw = plot_function(*args, **kwargs)
45
- self.raw.__iadd__(raw)
46
-
47
- output.raw = raw
47
+ if output.raw is not None:
48
+ self.raw.__iadd__(output.raw)
48
49
 
49
50
  return output
50
51
 
@@ -72,13 +73,15 @@ class figure_t(base_figure_t):
72
73
  backend_name=self.backend_name,
73
74
  )
74
75
 
75
- output.raw = self
76
+ output.raw = self.raw
76
77
 
77
78
  return output
78
79
 
79
- def _Show(self) -> None:
80
+ def _BackendShow(self, modal: bool, /) -> None:
80
81
  """"""
81
- self.raw.show().close()
82
+ # Passing self.raw.show().close shows the figures one by one.
83
+ BackendShow = lambda: self.raw.show().close()
84
+ self._LaunchBackendShow(modal, BackendShow)
82
85
 
83
86
 
84
87
  def _IsoSurface(volume: array_t, iso_value: float, *_, **kwargs) -> backend_plot_t:
@@ -86,11 +89,32 @@ def _IsoSurface(volume: array_t, iso_value: float, *_, **kwargs) -> backend_plot
86
89
  return volume_t(volume).isosurface(value=[iso_value], **kwargs)
87
90
 
88
91
 
89
- PLOTS = plot_e.NewPlotsTemplate()
90
- PLOTS[plot_e.ISOSET][2] = _IsoSurface
92
+ def _Mesh(triangles: array_t, vertices: array_t, *_, **kwargs) -> backend_plot_t:
93
+ """"""
94
+ output = backend_plot_t((vertices, triangles))
95
+
96
+ if "width_edge" in kwargs:
97
+ output.linewidth(kwargs["width_edge"])
98
+ if "color_edge" in kwargs:
99
+ output.linecolor(kwargs["color_edge"])
100
+ if "color_face" in kwargs:
101
+ output.color(c=kwargs["color_face"])
102
+
103
+ return output
104
+
105
+
106
+ PLOTS = NewPlotFunctionsTemplate()
107
+ PLOTS[plot_e.ISOSET][1] = _IsoSurface
108
+ PLOTS[plot_e.MESH][1] = _Mesh
91
109
 
92
110
 
93
- TRANSLATIONS = {}
111
+ TRANSLATIONS = {
112
+ (_IsoSurface, "color_edge"): None,
113
+ (_IsoSurface, "color_face"): None,
114
+ (_IsoSurface, "step_size"): None,
115
+ (_IsoSurface, "width_edge"): None,
116
+ (_Mesh, "color_face"): ("color_face", lambda _: NewConvertedColor(_, "hex", 0)),
117
+ }
94
118
 
95
119
 
96
120
  """
@@ -7,9 +7,11 @@ SEE COPYRIGHT NOTICE BELOW
7
7
  import importlib as mprt
8
8
  import importlib.util as mput
9
9
  import sys as s
10
+ import typing as h
10
11
  from pathlib import Path as path_t
11
12
 
12
13
  from babelplot.runtime.backends import BACKENDS
14
+ from babelplot.type.plot_function import plot_functions_h
13
15
  from babelplot.type.plot_type import plot_e
14
16
 
15
17
 
@@ -36,7 +38,7 @@ def CheckBackend(backend: str | path_t, /) -> None:
36
38
  if hasattr(module, "PLOTS"):
37
39
  defined = getattr(module, "PLOTS")
38
40
  if isinstance(defined, dict):
39
- issues = plot_e.PlotsIssues(defined)
41
+ issues = _PlotFunctionsIssues(defined)
40
42
  if issues is not None:
41
43
  print(f"--- PLOTS:\n ", "\n ".join(issues), sep="")
42
44
  else:
@@ -49,6 +51,56 @@ def CheckBackend(backend: str | path_t, /) -> None:
49
51
  # TODO: Check that translations respect the format (to be documented by the way).
50
52
 
51
53
 
54
+ def _PlotFunctionsIssues(plots: plot_functions_h, /) -> h.Sequence[str] | None:
55
+ """"""
56
+ issues = []
57
+
58
+ for key, value in plots.items():
59
+ if not isinstance(key, plot_e):
60
+ issues.append(
61
+ f"{key}/{type(key).__name__}: Invalid plot type. "
62
+ f"Expected={plot_e.__name__}."
63
+ )
64
+ continue
65
+
66
+ if not isinstance(value, list | tuple):
67
+ issues.append(
68
+ f"{key}/{type(value).__name__}: Invalid plot record type. "
69
+ f"Expected=list or tuple."
70
+ )
71
+ continue
72
+
73
+ value = tuple(filter(lambda _: _ is not None, value))
74
+
75
+ how_defined = key.value
76
+ if (n_dimensions := value.__len__()) != (
77
+ n_required := how_defined[2].__len__()
78
+ ):
79
+ issues.append(
80
+ f"{key}: Invalid number of possible dimensions {n_dimensions}. "
81
+ f"Expected={n_required}."
82
+ )
83
+ continue
84
+
85
+ for d_idx, for_dim in enumerate(value):
86
+ if not callable(for_dim):
87
+ issues.append(
88
+ f"{key}/{for_dim}: Invalid plot function "
89
+ f"for dim {how_defined[2][d_idx]}. "
90
+ f"Expected=Callable."
91
+ )
92
+
93
+ missing = set(plot_e.__members__.values()).difference(plots.keys())
94
+ if missing.__len__() > 0:
95
+ missing = str(sorted(_elm.name for _elm in missing))[1:-1].replace("'", "")
96
+ issues.append(f"Missing plot type(s): {missing}")
97
+
98
+ if issues.__len__() > 0:
99
+ return issues
100
+
101
+ return None
102
+
103
+
52
104
  """
53
105
  COPYRIGHT NOTICE
54
106
 
@@ -5,7 +5,7 @@ SEE COPYRIGHT NOTICE BELOW
5
5
  """
6
6
 
7
7
  BACKEND_CATALOG_PY_PATH = "babelplot.backend.catalog"
8
-
8
+ QT_RUNTIME_PY_PATH = "babelplot.runtime.application"
9
9
 
10
10
  """
11
11
  COPYRIGHT NOTICE