rgrid-python 4.5.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- grid_py/__init__.py +340 -0
- grid_py/_arrow.py +331 -0
- grid_py/_clippath.py +170 -0
- grid_py/_colour.py +815 -0
- grid_py/_coords.py +1534 -0
- grid_py/_curve.py +1668 -0
- grid_py/_display_list.py +507 -0
- grid_py/_draw.py +1397 -0
- grid_py/_edit.py +756 -0
- grid_py/_font_metrics.py +319 -0
- grid_py/_gpar.py +572 -0
- grid_py/_grab.py +501 -0
- grid_py/_grob.py +1377 -0
- grid_py/_group.py +798 -0
- grid_py/_highlevel.py +2176 -0
- grid_py/_just.py +361 -0
- grid_py/_layout.py +593 -0
- grid_py/_ls.py +895 -0
- grid_py/_mask.py +196 -0
- grid_py/_path.py +414 -0
- grid_py/_patterns.py +1049 -0
- grid_py/_primitives.py +2198 -0
- grid_py/_renderer_base.py +1184 -0
- grid_py/_scene_graph.py +248 -0
- grid_py/_size.py +1352 -0
- grid_py/_state.py +683 -0
- grid_py/_transforms.py +448 -0
- grid_py/_typeset.py +384 -0
- grid_py/_units.py +1924 -0
- grid_py/_utils.py +310 -0
- grid_py/_viewport.py +1649 -0
- grid_py/_vp_calc.py +970 -0
- grid_py/py.typed +0 -0
- grid_py/renderer.py +1762 -0
- grid_py/renderer_web.py +764 -0
- grid_py/resources/d3.v7.min.js +2 -0
- grid_py/resources/gridpy.css +80 -0
- grid_py/resources/gridpy.js +813 -0
- rgrid_python-4.5.3.dist-info/METADATA +489 -0
- rgrid_python-4.5.3.dist-info/RECORD +42 -0
- rgrid_python-4.5.3.dist-info/WHEEL +4 -0
- rgrid_python-4.5.3.dist-info/licenses/LICENSE +3 -0
grid_py/_grab.py
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
"""Scene capture and manipulation for grid_py (port of R's grid grab/force/cap).
|
|
2
|
+
|
|
3
|
+
This module provides functions for capturing, forcing, reverting, and
|
|
4
|
+
reordering the current grid scene:
|
|
5
|
+
|
|
6
|
+
* :func:`grid_grab` -- grab the current display list as a :class:`~._grob.GTree`.
|
|
7
|
+
* :func:`grid_grab_expr` -- evaluate a callable and grab the result.
|
|
8
|
+
* :func:`grid_force` -- force delayed grobs on the display list.
|
|
9
|
+
* :func:`grid_revert` -- revert previously forced grobs.
|
|
10
|
+
* :func:`grid_cap` -- capture the current display as a raster (NumPy array).
|
|
11
|
+
* :func:`grid_reorder` -- reorder children of a gTree on the display list.
|
|
12
|
+
|
|
13
|
+
References
|
|
14
|
+
----------
|
|
15
|
+
R source: ``src/library/grid/R/grab.R`` (~248 lines)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import copy
|
|
21
|
+
import warnings
|
|
22
|
+
from typing import (
|
|
23
|
+
Any,
|
|
24
|
+
Callable,
|
|
25
|
+
List,
|
|
26
|
+
Optional,
|
|
27
|
+
Sequence,
|
|
28
|
+
Union,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
import numpy as np
|
|
32
|
+
|
|
33
|
+
from ._grob import (
|
|
34
|
+
GList,
|
|
35
|
+
GTree,
|
|
36
|
+
Grob,
|
|
37
|
+
force_grob,
|
|
38
|
+
is_grob,
|
|
39
|
+
reorder_grob,
|
|
40
|
+
)
|
|
41
|
+
from ._path import GPath
|
|
42
|
+
from ._display_list import DisplayList, DLDrawGrob
|
|
43
|
+
from ._state import GridState, get_state
|
|
44
|
+
|
|
45
|
+
__all__ = [
|
|
46
|
+
"grid_grab",
|
|
47
|
+
"grid_grab_expr",
|
|
48
|
+
"grid_force",
|
|
49
|
+
"grid_revert",
|
|
50
|
+
"grid_cap",
|
|
51
|
+
"grid_reorder",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
# Internal helpers
|
|
57
|
+
# ---------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _collect_dl_grobs(dl: DisplayList, warn: int = 2) -> Optional[GList]:
|
|
61
|
+
"""Collect all grobs from the display list into a :class:`GList`.
|
|
62
|
+
|
|
63
|
+
Parameters
|
|
64
|
+
----------
|
|
65
|
+
dl : DisplayList
|
|
66
|
+
The display list to scan.
|
|
67
|
+
warn : int
|
|
68
|
+
Warning level. 0 = silent, 1 = warn on definite problems,
|
|
69
|
+
2 = warn on possible problems.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
GList or None
|
|
74
|
+
A :class:`GList` containing all grobs found, or ``None`` if the
|
|
75
|
+
display list is empty.
|
|
76
|
+
"""
|
|
77
|
+
grobs: list[Grob] = []
|
|
78
|
+
seen_names: set[str] = set()
|
|
79
|
+
|
|
80
|
+
for item in dl:
|
|
81
|
+
if isinstance(item, DLDrawGrob) and item.grob is not None:
|
|
82
|
+
grob = item.grob
|
|
83
|
+
if warn >= 1:
|
|
84
|
+
if grob.name in seen_names:
|
|
85
|
+
warnings.warn(
|
|
86
|
+
"one or more grobs overwritten "
|
|
87
|
+
"(grab may not be faithful; try wrap=True)",
|
|
88
|
+
stacklevel=3,
|
|
89
|
+
)
|
|
90
|
+
seen_names.add(grob.name)
|
|
91
|
+
grobs.append(grob)
|
|
92
|
+
|
|
93
|
+
if not grobs:
|
|
94
|
+
return None
|
|
95
|
+
return GList(*grobs)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _grab_dl(
|
|
99
|
+
warn: int = 2,
|
|
100
|
+
wrap: bool = False,
|
|
101
|
+
wrap_vps: bool = False,
|
|
102
|
+
) -> Optional[GTree]:
|
|
103
|
+
"""Grab the current display list as a :class:`GTree`.
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
warn : int
|
|
108
|
+
Warning level (0, 1, or 2).
|
|
109
|
+
wrap : bool
|
|
110
|
+
If ``True``, wrap all viewport pushes and grobs.
|
|
111
|
+
wrap_vps : bool
|
|
112
|
+
If ``True``, wrap viewport operations inside recorded grobs.
|
|
113
|
+
|
|
114
|
+
Returns
|
|
115
|
+
-------
|
|
116
|
+
GTree or None
|
|
117
|
+
A :class:`GTree` encapsulating the scene, or ``None`` if the
|
|
118
|
+
display list is empty.
|
|
119
|
+
"""
|
|
120
|
+
state = get_state()
|
|
121
|
+
dl = state.display_list
|
|
122
|
+
|
|
123
|
+
if len(dl) == 0:
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
children = _collect_dl_grobs(dl, warn=warn)
|
|
127
|
+
if children is None:
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
if wrap:
|
|
131
|
+
# When wrapping, deep-copy each grob so the grabbed tree is independent
|
|
132
|
+
wrapped: list[Grob] = []
|
|
133
|
+
for child in children:
|
|
134
|
+
wrapped.append(copy.deepcopy(child))
|
|
135
|
+
children = GList(*wrapped)
|
|
136
|
+
|
|
137
|
+
return GTree(children=children)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
# ---------------------------------------------------------------------------
|
|
141
|
+
# Public API -- grid.grab
|
|
142
|
+
# ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def grid_grab(
|
|
146
|
+
warn: int = 2,
|
|
147
|
+
wrap: bool = False,
|
|
148
|
+
wrap_vps: bool = False,
|
|
149
|
+
) -> Optional[GTree]:
|
|
150
|
+
"""Grab the current display list as a :class:`~._grob.GTree`.
|
|
151
|
+
|
|
152
|
+
Collects all grobs from the display list into a single gTree that
|
|
153
|
+
represents the current scene. This is the Python equivalent of R's
|
|
154
|
+
``grid.grab()``.
|
|
155
|
+
|
|
156
|
+
Parameters
|
|
157
|
+
----------
|
|
158
|
+
warn : int, optional
|
|
159
|
+
Warning level controlling how aggressively potential problems are
|
|
160
|
+
reported.
|
|
161
|
+
|
|
162
|
+
- ``0`` -- no warnings.
|
|
163
|
+
- ``1`` -- warn about situations that are definitely *not* captured
|
|
164
|
+
faithfully (e.g. duplicated top-level grob names).
|
|
165
|
+
- ``2`` (default) -- additionally warn about situations that *may*
|
|
166
|
+
not be captured faithfully (e.g. top-level viewport pushes).
|
|
167
|
+
wrap : bool, optional
|
|
168
|
+
If ``True``, wrap all pushes and grobs in the resulting gTree so
|
|
169
|
+
that the grabbed tree can be replayed independently.
|
|
170
|
+
wrap_vps : bool, optional
|
|
171
|
+
If ``True``, also wrap viewport operations.
|
|
172
|
+
|
|
173
|
+
Returns
|
|
174
|
+
-------
|
|
175
|
+
GTree or None
|
|
176
|
+
A :class:`~._grob.GTree` encapsulating the current scene, or
|
|
177
|
+
``None`` if the display list is empty.
|
|
178
|
+
|
|
179
|
+
Examples
|
|
180
|
+
--------
|
|
181
|
+
>>> tree = grid_grab()
|
|
182
|
+
>>> if tree is not None:
|
|
183
|
+
... print(tree)
|
|
184
|
+
"""
|
|
185
|
+
return _grab_dl(warn=warn, wrap=wrap, wrap_vps=wrap_vps)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
# ---------------------------------------------------------------------------
|
|
189
|
+
# Public API -- grid.grabExpr
|
|
190
|
+
# ---------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def grid_grab_expr(
|
|
194
|
+
expr: Callable[[], Any],
|
|
195
|
+
warn: int = 2,
|
|
196
|
+
wrap: bool = False,
|
|
197
|
+
wrap_vps: bool = False,
|
|
198
|
+
width: float = 7.0,
|
|
199
|
+
height: float = 7.0,
|
|
200
|
+
) -> Optional[GTree]:
|
|
201
|
+
"""Evaluate *expr* and grab the resulting scene.
|
|
202
|
+
|
|
203
|
+
The callable *expr* is executed in a temporary graphics context (the
|
|
204
|
+
display list is cleared and restored afterwards). The scene produced
|
|
205
|
+
by *expr* is then captured via :func:`grid_grab`. This is the Python
|
|
206
|
+
equivalent of R's ``grid.grabExpr()``.
|
|
207
|
+
|
|
208
|
+
Parameters
|
|
209
|
+
----------
|
|
210
|
+
expr : callable
|
|
211
|
+
A zero-argument callable that performs grid drawing operations.
|
|
212
|
+
warn : int, optional
|
|
213
|
+
Warning level (see :func:`grid_grab`).
|
|
214
|
+
wrap : bool, optional
|
|
215
|
+
Wrap mode (see :func:`grid_grab`).
|
|
216
|
+
wrap_vps : bool, optional
|
|
217
|
+
Wrap viewport operations (see :func:`grid_grab`).
|
|
218
|
+
width : float, optional
|
|
219
|
+
Nominal device width in inches (default ``7.0``). Stored on the
|
|
220
|
+
state but not used for physical rendering.
|
|
221
|
+
height : float, optional
|
|
222
|
+
Nominal device height in inches (default ``7.0``).
|
|
223
|
+
|
|
224
|
+
Returns
|
|
225
|
+
-------
|
|
226
|
+
GTree or None
|
|
227
|
+
A :class:`~._grob.GTree` encapsulating the scene drawn by *expr*,
|
|
228
|
+
or ``None`` if nothing was drawn.
|
|
229
|
+
"""
|
|
230
|
+
state = get_state()
|
|
231
|
+
# Save and clear the display list
|
|
232
|
+
saved_items = state.display_list.get_items()
|
|
233
|
+
state.display_list.clear()
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
# Run the user's drawing code
|
|
237
|
+
expr()
|
|
238
|
+
# Grab what was drawn
|
|
239
|
+
result = _grab_dl(warn=warn, wrap=wrap, wrap_vps=wrap_vps)
|
|
240
|
+
finally:
|
|
241
|
+
# Restore the original display list
|
|
242
|
+
state.display_list.clear()
|
|
243
|
+
for item in saved_items:
|
|
244
|
+
state.display_list.record(item)
|
|
245
|
+
|
|
246
|
+
return result
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
# ---------------------------------------------------------------------------
|
|
250
|
+
# Public API -- grid.force / grid.revert
|
|
251
|
+
# ---------------------------------------------------------------------------
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def grid_force(
|
|
255
|
+
x: Optional[Union[Grob, GTree]] = None,
|
|
256
|
+
redraw: bool = True,
|
|
257
|
+
) -> Optional[Union[Grob, GTree]]:
|
|
258
|
+
"""Force delayed grobs, materialising deferred content.
|
|
259
|
+
|
|
260
|
+
If *x* is ``None``, every grob on the current display list is forced
|
|
261
|
+
in place. Otherwise, *x* is forced and a new (forced) copy is
|
|
262
|
+
returned. This is the Python equivalent of R's ``grid.force()``.
|
|
263
|
+
|
|
264
|
+
Parameters
|
|
265
|
+
----------
|
|
266
|
+
x : Grob, GTree, or None, optional
|
|
267
|
+
The grob to force. ``None`` forces the entire display list.
|
|
268
|
+
redraw : bool, optional
|
|
269
|
+
If ``True`` (default) and *x* is ``None``, redraw after forcing.
|
|
270
|
+
|
|
271
|
+
Returns
|
|
272
|
+
-------
|
|
273
|
+
Grob, GTree, or None
|
|
274
|
+
When *x* is provided, the forced copy. When *x* is ``None``,
|
|
275
|
+
``None`` is returned (the display list is modified in place).
|
|
276
|
+
|
|
277
|
+
Examples
|
|
278
|
+
--------
|
|
279
|
+
>>> forced_tree = grid_force(my_gtree)
|
|
280
|
+
"""
|
|
281
|
+
if x is not None:
|
|
282
|
+
return force_grob(x)
|
|
283
|
+
|
|
284
|
+
# Force every grob on the display list
|
|
285
|
+
state = get_state()
|
|
286
|
+
dl = state.display_list
|
|
287
|
+
|
|
288
|
+
for item in dl:
|
|
289
|
+
if isinstance(item, DLDrawGrob) and item.grob is not None:
|
|
290
|
+
forced = force_grob(item.grob)
|
|
291
|
+
item.grob = forced
|
|
292
|
+
item.params["grob"] = forced
|
|
293
|
+
|
|
294
|
+
if redraw:
|
|
295
|
+
dl.replay(state)
|
|
296
|
+
|
|
297
|
+
return None
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def grid_revert(
|
|
301
|
+
x: Optional[Union[Grob, GTree]] = None,
|
|
302
|
+
redraw: bool = True,
|
|
303
|
+
) -> Optional[Union[Grob, GTree]]:
|
|
304
|
+
"""Revert previously forced grobs to their original (unforced) state.
|
|
305
|
+
|
|
306
|
+
If *x* is ``None``, every grob on the display list that carries an
|
|
307
|
+
``_original`` attribute (set by :func:`grid_force`) is reverted.
|
|
308
|
+
Otherwise, *x* itself is reverted. This is the Python equivalent of
|
|
309
|
+
R's ``grid.revert()``.
|
|
310
|
+
|
|
311
|
+
Parameters
|
|
312
|
+
----------
|
|
313
|
+
x : Grob, GTree, or None, optional
|
|
314
|
+
The grob to revert. ``None`` reverts the entire display list.
|
|
315
|
+
redraw : bool, optional
|
|
316
|
+
If ``True`` (default) and *x* is ``None``, redraw after reverting.
|
|
317
|
+
|
|
318
|
+
Returns
|
|
319
|
+
-------
|
|
320
|
+
Grob, GTree, or None
|
|
321
|
+
When *x* is provided, the reverted grob (or the unchanged grob if
|
|
322
|
+
it was not previously forced). When *x* is ``None``, ``None`` is
|
|
323
|
+
returned (the display list is modified in place).
|
|
324
|
+
"""
|
|
325
|
+
if x is not None:
|
|
326
|
+
original = getattr(x, "_original", None)
|
|
327
|
+
if original is not None:
|
|
328
|
+
return original
|
|
329
|
+
# For gTrees, try to revert children
|
|
330
|
+
if isinstance(x, GTree):
|
|
331
|
+
result = copy.deepcopy(x)
|
|
332
|
+
reverted_children: list[Grob] = []
|
|
333
|
+
for name in result._children_order:
|
|
334
|
+
child = result._children[name]
|
|
335
|
+
reverted = grid_revert(child)
|
|
336
|
+
reverted_children.append(reverted if reverted is not None else child)
|
|
337
|
+
result._set_children_internal(GList(*reverted_children))
|
|
338
|
+
return result
|
|
339
|
+
return x
|
|
340
|
+
|
|
341
|
+
# Revert every grob on the display list
|
|
342
|
+
state = get_state()
|
|
343
|
+
dl = state.display_list
|
|
344
|
+
|
|
345
|
+
for item in dl:
|
|
346
|
+
if isinstance(item, DLDrawGrob) and item.grob is not None:
|
|
347
|
+
original = getattr(item.grob, "_original", None)
|
|
348
|
+
if original is not None:
|
|
349
|
+
item.grob = original
|
|
350
|
+
item.params["grob"] = original
|
|
351
|
+
|
|
352
|
+
if redraw:
|
|
353
|
+
dl.replay(state)
|
|
354
|
+
|
|
355
|
+
return None
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
# ---------------------------------------------------------------------------
|
|
359
|
+
# Public API -- grid.cap
|
|
360
|
+
# ---------------------------------------------------------------------------
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def grid_cap(native: bool = True) -> Optional[np.ndarray]:
|
|
364
|
+
"""Capture the current display as a raster image.
|
|
365
|
+
|
|
366
|
+
This attempts to rasterise the current scene using the Cairo renderer.
|
|
367
|
+
This is the Python equivalent of R's ``grid.cap()``.
|
|
368
|
+
|
|
369
|
+
Parameters
|
|
370
|
+
----------
|
|
371
|
+
native : bool, optional
|
|
372
|
+
If ``True`` (default), return the raster in the device's native
|
|
373
|
+
resolution as a NumPy array of shape ``(H, W, 4)`` (RGBA uint8).
|
|
374
|
+
If ``False``, return in normalised [0, 1] float64.
|
|
375
|
+
|
|
376
|
+
Returns
|
|
377
|
+
-------
|
|
378
|
+
numpy.ndarray or None
|
|
379
|
+
The raster image, or ``None`` if no renderer is available.
|
|
380
|
+
"""
|
|
381
|
+
state = get_state()
|
|
382
|
+
renderer = state.get_renderer()
|
|
383
|
+
|
|
384
|
+
if renderer is None:
|
|
385
|
+
warnings.warn(
|
|
386
|
+
"no renderer available for grid_cap; returning None",
|
|
387
|
+
stacklevel=2,
|
|
388
|
+
)
|
|
389
|
+
return None
|
|
390
|
+
|
|
391
|
+
try:
|
|
392
|
+
import io
|
|
393
|
+
png_bytes = renderer.to_png_bytes()
|
|
394
|
+
# Decode PNG bytes to RGBA array
|
|
395
|
+
try:
|
|
396
|
+
from PIL import Image
|
|
397
|
+
img = Image.open(io.BytesIO(png_bytes)).convert("RGBA")
|
|
398
|
+
arr = np.asarray(img, dtype=np.uint8).copy()
|
|
399
|
+
except ImportError:
|
|
400
|
+
# Fallback: read directly from Cairo surface if ImageSurface
|
|
401
|
+
import cairo
|
|
402
|
+
surface = renderer.get_surface() if hasattr(renderer, "get_surface") else None
|
|
403
|
+
if surface is not None and isinstance(surface, cairo.ImageSurface):
|
|
404
|
+
w = surface.get_width()
|
|
405
|
+
h = surface.get_height()
|
|
406
|
+
buf = surface.get_data()
|
|
407
|
+
arr = np.frombuffer(bytes(buf), dtype=np.uint8).reshape(h, w, 4).copy()
|
|
408
|
+
# Cairo is BGRA; convert to RGBA
|
|
409
|
+
arr[:, :, [0, 2]] = arr[:, :, [2, 0]]
|
|
410
|
+
else:
|
|
411
|
+
warnings.warn(
|
|
412
|
+
"grid_cap requires Pillow for non-image surfaces",
|
|
413
|
+
stacklevel=2,
|
|
414
|
+
)
|
|
415
|
+
return None
|
|
416
|
+
if not native:
|
|
417
|
+
arr = arr.astype(np.float64) / 255.0
|
|
418
|
+
return arr
|
|
419
|
+
except Exception:
|
|
420
|
+
warnings.warn(
|
|
421
|
+
"failed to capture raster from renderer; returning None",
|
|
422
|
+
stacklevel=2,
|
|
423
|
+
)
|
|
424
|
+
return None
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
# ---------------------------------------------------------------------------
|
|
428
|
+
# Public API -- grid.reorder
|
|
429
|
+
# ---------------------------------------------------------------------------
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def grid_reorder(
|
|
433
|
+
gPath: Union[str, GPath],
|
|
434
|
+
order: Union[List[int], List[str]],
|
|
435
|
+
back: bool = True,
|
|
436
|
+
grep: bool = False,
|
|
437
|
+
redraw: bool = True,
|
|
438
|
+
) -> None:
|
|
439
|
+
"""Reorder the children of a gTree on the display list.
|
|
440
|
+
|
|
441
|
+
Locates the gTree identified by *gPath* on the current display list
|
|
442
|
+
and reorders its children according to *order*. This is the Python
|
|
443
|
+
equivalent of R's ``grid.reorder()``.
|
|
444
|
+
|
|
445
|
+
Parameters
|
|
446
|
+
----------
|
|
447
|
+
gPath : str or GPath
|
|
448
|
+
Path identifying the target gTree.
|
|
449
|
+
order : list[int] or list[str]
|
|
450
|
+
Indices (0-based) or names specifying the new ordering.
|
|
451
|
+
back : bool, optional
|
|
452
|
+
If ``True`` (default), specified children come first (drawn behind);
|
|
453
|
+
unspecified children are appended. If ``False``, unspecified
|
|
454
|
+
children come first; specified children are appended (drawn in
|
|
455
|
+
front).
|
|
456
|
+
grep : bool, optional
|
|
457
|
+
If ``True``, use regex matching on path components.
|
|
458
|
+
redraw : bool, optional
|
|
459
|
+
If ``True`` (default), redraw after reordering.
|
|
460
|
+
|
|
461
|
+
Raises
|
|
462
|
+
------
|
|
463
|
+
TypeError
|
|
464
|
+
If *gPath* is invalid.
|
|
465
|
+
ValueError
|
|
466
|
+
If no gTree matching *gPath* is found, or if *order* contains
|
|
467
|
+
invalid names or indices.
|
|
468
|
+
"""
|
|
469
|
+
if isinstance(gPath, str):
|
|
470
|
+
gpath = GPath(gPath)
|
|
471
|
+
elif isinstance(gPath, GPath):
|
|
472
|
+
gpath = gPath
|
|
473
|
+
else:
|
|
474
|
+
raise TypeError(f"invalid gPath: expected str or GPath, got {type(gPath).__name__}")
|
|
475
|
+
|
|
476
|
+
state = get_state()
|
|
477
|
+
dl = state.display_list
|
|
478
|
+
|
|
479
|
+
# Find the target gTree -- import the helper from _edit
|
|
480
|
+
from ._edit import _find_dl_grobs
|
|
481
|
+
|
|
482
|
+
grep_flags = [grep] * gpath.n
|
|
483
|
+
matches = _find_dl_grobs(dl, gpath, strict=False, grep=grep_flags, global_=False)
|
|
484
|
+
|
|
485
|
+
if not matches:
|
|
486
|
+
raise ValueError(f"gPath ({gpath}) does not match any grob on the display list")
|
|
487
|
+
|
|
488
|
+
_dl_idx, matched_grob = matches[0]
|
|
489
|
+
|
|
490
|
+
if not isinstance(matched_grob, GTree):
|
|
491
|
+
raise TypeError(
|
|
492
|
+
f"gPath matched '{matched_grob.name}' which is not a gTree; "
|
|
493
|
+
"cannot reorder"
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
# Perform the reorder in place
|
|
497
|
+
reordered = reorder_grob(matched_grob, order, back=back)
|
|
498
|
+
matched_grob._children_order = reordered._children_order
|
|
499
|
+
|
|
500
|
+
if redraw:
|
|
501
|
+
dl.replay(state)
|