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/_ls.py
ADDED
|
@@ -0,0 +1,895 @@
|
|
|
1
|
+
"""Listing and searching grobs/viewports for grid_py (port of R's grid ls/grep).
|
|
2
|
+
|
|
3
|
+
This module provides functions for inspecting the grid scene graph:
|
|
4
|
+
|
|
5
|
+
* :func:`grid_ls` -- list grobs and/or viewports on the display list or
|
|
6
|
+
within a given grob/viewport tree.
|
|
7
|
+
* :func:`grid_grep` -- search for grobs (or viewports) by name pattern.
|
|
8
|
+
* Formatting helpers: :func:`nested_listing`, :func:`path_listing`,
|
|
9
|
+
:func:`grob_path_listing`.
|
|
10
|
+
* Introspection: :func:`show_grob`, :func:`get_names`, :func:`child_names`.
|
|
11
|
+
|
|
12
|
+
References
|
|
13
|
+
----------
|
|
14
|
+
R source: ``src/library/grid/R/ls.R`` (~908 lines)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import re
|
|
20
|
+
import warnings
|
|
21
|
+
from dataclasses import dataclass, field
|
|
22
|
+
from typing import (
|
|
23
|
+
Any,
|
|
24
|
+
Callable,
|
|
25
|
+
Dict,
|
|
26
|
+
List,
|
|
27
|
+
Optional,
|
|
28
|
+
Sequence,
|
|
29
|
+
Union,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
from ._grob import GList, GTree, Grob, is_grob
|
|
33
|
+
from ._path import GPath, VpPath, PATH_SEP
|
|
34
|
+
from ._display_list import DisplayList, DLDrawGrob, DLPushViewport, DLPopViewport, DLUpViewport, DLDownViewport
|
|
35
|
+
from ._state import get_state
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"grid_ls",
|
|
39
|
+
"grid_grep",
|
|
40
|
+
"nested_listing",
|
|
41
|
+
"path_listing",
|
|
42
|
+
"grob_path_listing",
|
|
43
|
+
"show_grob",
|
|
44
|
+
"get_names",
|
|
45
|
+
"child_names",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
# GridListing -- internal data structure
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass
|
|
55
|
+
class GridListingEntry:
|
|
56
|
+
"""A single entry in a grid listing.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
name : str
|
|
61
|
+
Display name of the grob or viewport.
|
|
62
|
+
g_depth : int
|
|
63
|
+
Grob nesting depth.
|
|
64
|
+
vp_depth : int
|
|
65
|
+
Viewport nesting depth.
|
|
66
|
+
g_path : str
|
|
67
|
+
Grob path accumulated so far.
|
|
68
|
+
vp_path : str
|
|
69
|
+
Viewport path accumulated so far.
|
|
70
|
+
entry_type : str
|
|
71
|
+
One of ``"grobListing"``, ``"gTreeListing"``, ``"vpListing"``,
|
|
72
|
+
``"vpPopListing"``, ``"vpUpListing"``, ``"vpNameListing"``.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
name: str
|
|
76
|
+
g_depth: int = 0
|
|
77
|
+
vp_depth: int = 0
|
|
78
|
+
g_path: str = ""
|
|
79
|
+
vp_path: str = ""
|
|
80
|
+
entry_type: str = "grobListing"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass
|
|
84
|
+
class FlatGridListing:
|
|
85
|
+
"""Flattened listing of grobs and viewports.
|
|
86
|
+
|
|
87
|
+
This is the Python equivalent of R's ``flatGridListing`` object, storing
|
|
88
|
+
parallel vectors of metadata for each entry.
|
|
89
|
+
|
|
90
|
+
Parameters
|
|
91
|
+
----------
|
|
92
|
+
names : list[str]
|
|
93
|
+
Names of the listed elements.
|
|
94
|
+
g_depths : list[int]
|
|
95
|
+
Grob depths.
|
|
96
|
+
vp_depths : list[int]
|
|
97
|
+
Viewport depths.
|
|
98
|
+
g_paths : list[str]
|
|
99
|
+
Grob paths.
|
|
100
|
+
vp_paths : list[str]
|
|
101
|
+
Viewport paths.
|
|
102
|
+
types : list[str]
|
|
103
|
+
Entry type tags.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
names: List[str] = field(default_factory=list)
|
|
107
|
+
g_depths: List[int] = field(default_factory=list)
|
|
108
|
+
vp_depths: List[int] = field(default_factory=list)
|
|
109
|
+
g_paths: List[str] = field(default_factory=list)
|
|
110
|
+
vp_paths: List[str] = field(default_factory=list)
|
|
111
|
+
types: List[str] = field(default_factory=list)
|
|
112
|
+
|
|
113
|
+
def __len__(self) -> int:
|
|
114
|
+
return len(self.names)
|
|
115
|
+
|
|
116
|
+
def extend(self, other: "FlatGridListing") -> None:
|
|
117
|
+
"""Append all entries from *other* to this listing.
|
|
118
|
+
|
|
119
|
+
Parameters
|
|
120
|
+
----------
|
|
121
|
+
other : FlatGridListing
|
|
122
|
+
The listing to merge in.
|
|
123
|
+
"""
|
|
124
|
+
self.names.extend(other.names)
|
|
125
|
+
self.g_depths.extend(other.g_depths)
|
|
126
|
+
self.vp_depths.extend(other.vp_depths)
|
|
127
|
+
self.g_paths.extend(other.g_paths)
|
|
128
|
+
self.vp_paths.extend(other.vp_paths)
|
|
129
|
+
self.types.extend(other.types)
|
|
130
|
+
|
|
131
|
+
def append_entry(self, entry: GridListingEntry) -> None:
|
|
132
|
+
"""Append a single entry.
|
|
133
|
+
|
|
134
|
+
Parameters
|
|
135
|
+
----------
|
|
136
|
+
entry : GridListingEntry
|
|
137
|
+
The entry to append.
|
|
138
|
+
"""
|
|
139
|
+
self.names.append(entry.name)
|
|
140
|
+
self.g_depths.append(entry.g_depth)
|
|
141
|
+
self.vp_depths.append(entry.vp_depth)
|
|
142
|
+
self.g_paths.append(entry.g_path)
|
|
143
|
+
self.vp_paths.append(entry.vp_path)
|
|
144
|
+
self.types.append(entry.entry_type)
|
|
145
|
+
|
|
146
|
+
def subset(self, indices: List[int]) -> "FlatGridListing":
|
|
147
|
+
"""Return a new listing containing only the entries at *indices*.
|
|
148
|
+
|
|
149
|
+
Parameters
|
|
150
|
+
----------
|
|
151
|
+
indices : list[int]
|
|
152
|
+
The 0-based indices to keep.
|
|
153
|
+
|
|
154
|
+
Returns
|
|
155
|
+
-------
|
|
156
|
+
FlatGridListing
|
|
157
|
+
"""
|
|
158
|
+
return FlatGridListing(
|
|
159
|
+
names=[self.names[i] for i in indices],
|
|
160
|
+
g_depths=[self.g_depths[i] for i in indices],
|
|
161
|
+
vp_depths=[self.vp_depths[i] for i in indices],
|
|
162
|
+
g_paths=[self.g_paths[i] for i in indices],
|
|
163
|
+
vp_paths=[self.vp_paths[i] for i in indices],
|
|
164
|
+
types=[self.types[i] for i in indices],
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def __repr__(self) -> str:
|
|
168
|
+
return f"FlatGridListing(n={len(self.names)})"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
# ---------------------------------------------------------------------------
|
|
172
|
+
# Internal listing builder
|
|
173
|
+
# ---------------------------------------------------------------------------
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _inc_path(old_path: str, addition: str) -> str:
|
|
177
|
+
"""Append *addition* to *old_path* with the grid path separator.
|
|
178
|
+
|
|
179
|
+
Parameters
|
|
180
|
+
----------
|
|
181
|
+
old_path : str
|
|
182
|
+
Existing accumulated path.
|
|
183
|
+
addition : str
|
|
184
|
+
Component to append.
|
|
185
|
+
|
|
186
|
+
Returns
|
|
187
|
+
-------
|
|
188
|
+
str
|
|
189
|
+
"""
|
|
190
|
+
if old_path:
|
|
191
|
+
return f"{old_path}{PATH_SEP}{addition}"
|
|
192
|
+
return addition
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _list_grob(
|
|
196
|
+
x: Any,
|
|
197
|
+
grobs: bool,
|
|
198
|
+
viewports: bool,
|
|
199
|
+
full_names: bool,
|
|
200
|
+
recursive: bool,
|
|
201
|
+
g_depth: int = 0,
|
|
202
|
+
vp_depth: int = 0,
|
|
203
|
+
g_path: str = "",
|
|
204
|
+
vp_path: str = "",
|
|
205
|
+
) -> FlatGridListing:
|
|
206
|
+
"""Recursively build a flat listing for a grob or gTree.
|
|
207
|
+
|
|
208
|
+
Parameters
|
|
209
|
+
----------
|
|
210
|
+
x : Grob or GTree or GList or None
|
|
211
|
+
The object to list.
|
|
212
|
+
grobs : bool
|
|
213
|
+
Include grob entries.
|
|
214
|
+
viewports : bool
|
|
215
|
+
Include viewport entries.
|
|
216
|
+
full_names : bool
|
|
217
|
+
Use full class-qualified names.
|
|
218
|
+
recursive : bool
|
|
219
|
+
Recurse into children.
|
|
220
|
+
g_depth : int
|
|
221
|
+
Current grob depth.
|
|
222
|
+
vp_depth : int
|
|
223
|
+
Current viewport depth.
|
|
224
|
+
g_path : str
|
|
225
|
+
Accumulated grob path.
|
|
226
|
+
vp_path : str
|
|
227
|
+
Accumulated viewport path.
|
|
228
|
+
|
|
229
|
+
Returns
|
|
230
|
+
-------
|
|
231
|
+
FlatGridListing
|
|
232
|
+
"""
|
|
233
|
+
listing = FlatGridListing()
|
|
234
|
+
|
|
235
|
+
if x is None:
|
|
236
|
+
return listing
|
|
237
|
+
|
|
238
|
+
# Handle GList
|
|
239
|
+
if isinstance(x, GList):
|
|
240
|
+
for child in x:
|
|
241
|
+
child_listing = _list_grob(
|
|
242
|
+
child, grobs, viewports, full_names, recursive,
|
|
243
|
+
g_depth, vp_depth, g_path, vp_path,
|
|
244
|
+
)
|
|
245
|
+
listing.extend(child_listing)
|
|
246
|
+
return listing
|
|
247
|
+
|
|
248
|
+
if not is_grob(x):
|
|
249
|
+
return listing
|
|
250
|
+
|
|
251
|
+
# Determine display name
|
|
252
|
+
if full_names:
|
|
253
|
+
display_name = repr(x)
|
|
254
|
+
else:
|
|
255
|
+
display_name = x.name
|
|
256
|
+
|
|
257
|
+
# gTree case
|
|
258
|
+
if isinstance(x, GTree):
|
|
259
|
+
if grobs:
|
|
260
|
+
listing.append_entry(GridListingEntry(
|
|
261
|
+
name=display_name,
|
|
262
|
+
g_depth=g_depth,
|
|
263
|
+
vp_depth=vp_depth,
|
|
264
|
+
g_path=g_path,
|
|
265
|
+
vp_path=vp_path,
|
|
266
|
+
entry_type="gTreeListing",
|
|
267
|
+
))
|
|
268
|
+
|
|
269
|
+
if recursive:
|
|
270
|
+
child_g_path = _inc_path(g_path, display_name) if grobs else g_path
|
|
271
|
+
child_g_depth = g_depth + 1 if grobs else g_depth
|
|
272
|
+
for child_name in x._children_order:
|
|
273
|
+
child = x._children[child_name]
|
|
274
|
+
child_listing = _list_grob(
|
|
275
|
+
child, grobs, viewports, full_names, recursive,
|
|
276
|
+
child_g_depth, vp_depth, child_g_path, vp_path,
|
|
277
|
+
)
|
|
278
|
+
listing.extend(child_listing)
|
|
279
|
+
|
|
280
|
+
return listing
|
|
281
|
+
|
|
282
|
+
# Plain grob case
|
|
283
|
+
if grobs:
|
|
284
|
+
listing.append_entry(GridListingEntry(
|
|
285
|
+
name=display_name,
|
|
286
|
+
g_depth=g_depth,
|
|
287
|
+
vp_depth=vp_depth,
|
|
288
|
+
g_path=g_path,
|
|
289
|
+
vp_path=vp_path,
|
|
290
|
+
entry_type="grobListing",
|
|
291
|
+
))
|
|
292
|
+
|
|
293
|
+
return listing
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
def _list_display_list(
|
|
297
|
+
grobs: bool,
|
|
298
|
+
viewports: bool,
|
|
299
|
+
full_names: bool,
|
|
300
|
+
recursive: bool,
|
|
301
|
+
) -> FlatGridListing:
|
|
302
|
+
"""Build a flat listing from the current display list.
|
|
303
|
+
|
|
304
|
+
Parameters
|
|
305
|
+
----------
|
|
306
|
+
grobs : bool
|
|
307
|
+
Include grob entries.
|
|
308
|
+
viewports : bool
|
|
309
|
+
Include viewport entries.
|
|
310
|
+
full_names : bool
|
|
311
|
+
Use full class-qualified names.
|
|
312
|
+
recursive : bool
|
|
313
|
+
Recurse into gTree children.
|
|
314
|
+
|
|
315
|
+
Returns
|
|
316
|
+
-------
|
|
317
|
+
FlatGridListing
|
|
318
|
+
"""
|
|
319
|
+
state = get_state()
|
|
320
|
+
dl = state.display_list
|
|
321
|
+
listing = FlatGridListing()
|
|
322
|
+
vp_depth = 0
|
|
323
|
+
vp_path = ""
|
|
324
|
+
|
|
325
|
+
for item in dl:
|
|
326
|
+
if isinstance(item, DLDrawGrob) and item.grob is not None:
|
|
327
|
+
grob_listing = _list_grob(
|
|
328
|
+
item.grob, grobs, viewports, full_names, recursive,
|
|
329
|
+
g_depth=0, vp_depth=vp_depth, g_path="", vp_path=vp_path,
|
|
330
|
+
)
|
|
331
|
+
listing.extend(grob_listing)
|
|
332
|
+
|
|
333
|
+
elif isinstance(item, DLPushViewport) and viewports:
|
|
334
|
+
vp = item.viewport
|
|
335
|
+
vp_name = getattr(vp, "name", str(vp)) if vp is not None else "?"
|
|
336
|
+
if full_names:
|
|
337
|
+
display_name = f"viewport[{vp_name}]"
|
|
338
|
+
else:
|
|
339
|
+
display_name = vp_name
|
|
340
|
+
listing.append_entry(GridListingEntry(
|
|
341
|
+
name=display_name,
|
|
342
|
+
g_depth=0,
|
|
343
|
+
vp_depth=vp_depth,
|
|
344
|
+
g_path="",
|
|
345
|
+
vp_path=vp_path,
|
|
346
|
+
entry_type="vpListing",
|
|
347
|
+
))
|
|
348
|
+
vp_depth += 1
|
|
349
|
+
vp_path = _inc_path(vp_path, vp_name)
|
|
350
|
+
|
|
351
|
+
elif isinstance(item, DLPopViewport) and viewports:
|
|
352
|
+
n = item.n
|
|
353
|
+
if full_names:
|
|
354
|
+
display_name = f"popViewport[{n}]"
|
|
355
|
+
else:
|
|
356
|
+
display_name = str(n)
|
|
357
|
+
listing.append_entry(GridListingEntry(
|
|
358
|
+
name=display_name,
|
|
359
|
+
g_depth=0,
|
|
360
|
+
vp_depth=vp_depth,
|
|
361
|
+
g_path="",
|
|
362
|
+
vp_path=vp_path,
|
|
363
|
+
entry_type="vpPopListing",
|
|
364
|
+
))
|
|
365
|
+
# Adjust depth and path
|
|
366
|
+
vp_depth = max(0, vp_depth - n)
|
|
367
|
+
parts = vp_path.split(PATH_SEP) if vp_path else []
|
|
368
|
+
remaining = max(0, len(parts) - n)
|
|
369
|
+
vp_path = PATH_SEP.join(parts[:remaining]) if remaining > 0 else ""
|
|
370
|
+
|
|
371
|
+
elif isinstance(item, DLUpViewport) and viewports:
|
|
372
|
+
n = item.n
|
|
373
|
+
if full_names:
|
|
374
|
+
display_name = f"upViewport[{n}]"
|
|
375
|
+
else:
|
|
376
|
+
display_name = str(n)
|
|
377
|
+
listing.append_entry(GridListingEntry(
|
|
378
|
+
name=display_name,
|
|
379
|
+
g_depth=0,
|
|
380
|
+
vp_depth=vp_depth,
|
|
381
|
+
g_path="",
|
|
382
|
+
vp_path=vp_path,
|
|
383
|
+
entry_type="vpUpListing",
|
|
384
|
+
))
|
|
385
|
+
vp_depth = max(0, vp_depth - n)
|
|
386
|
+
parts = vp_path.split(PATH_SEP) if vp_path else []
|
|
387
|
+
remaining = max(0, len(parts) - n)
|
|
388
|
+
vp_path = PATH_SEP.join(parts[:remaining]) if remaining > 0 else ""
|
|
389
|
+
|
|
390
|
+
elif isinstance(item, DLDownViewport) and viewports:
|
|
391
|
+
path_obj = item.params.get("path")
|
|
392
|
+
if path_obj is not None:
|
|
393
|
+
vp_name = getattr(path_obj, "name", str(path_obj))
|
|
394
|
+
else:
|
|
395
|
+
vp_name = "?"
|
|
396
|
+
if full_names:
|
|
397
|
+
display_name = f"downViewport[{vp_name}]"
|
|
398
|
+
else:
|
|
399
|
+
display_name = vp_name
|
|
400
|
+
listing.append_entry(GridListingEntry(
|
|
401
|
+
name=display_name,
|
|
402
|
+
g_depth=0,
|
|
403
|
+
vp_depth=vp_depth,
|
|
404
|
+
g_path="",
|
|
405
|
+
vp_path=vp_path,
|
|
406
|
+
entry_type="vpNameListing",
|
|
407
|
+
))
|
|
408
|
+
vp_depth += 1
|
|
409
|
+
vp_path = _inc_path(vp_path, vp_name)
|
|
410
|
+
|
|
411
|
+
return listing
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
# ---------------------------------------------------------------------------
|
|
415
|
+
# Public API -- grid.ls
|
|
416
|
+
# ---------------------------------------------------------------------------
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def grid_ls(
|
|
420
|
+
x: Any = None,
|
|
421
|
+
grobs: bool = True,
|
|
422
|
+
viewports: bool = False,
|
|
423
|
+
fullNames: bool = False,
|
|
424
|
+
recursive: bool = True,
|
|
425
|
+
print_: Union[bool, Callable[..., Any]] = True,
|
|
426
|
+
) -> FlatGridListing:
|
|
427
|
+
"""List grobs and/or viewports.
|
|
428
|
+
|
|
429
|
+
When *x* is ``None``, the current display list is listed. Otherwise,
|
|
430
|
+
*x* should be a :class:`~._grob.Grob`, :class:`~._grob.GTree`, or
|
|
431
|
+
:class:`~._grob.GList` to inspect. This is the Python equivalent of
|
|
432
|
+
R's ``grid.ls()``.
|
|
433
|
+
|
|
434
|
+
Parameters
|
|
435
|
+
----------
|
|
436
|
+
x : Grob, GTree, GList, or None, optional
|
|
437
|
+
The object to list. ``None`` lists the display list.
|
|
438
|
+
grobs : bool, optional
|
|
439
|
+
Include grob entries (default ``True``).
|
|
440
|
+
viewports : bool, optional
|
|
441
|
+
Include viewport entries (default ``False``).
|
|
442
|
+
fullNames : bool, optional
|
|
443
|
+
Use full class-qualified names (default ``False``).
|
|
444
|
+
recursive : bool, optional
|
|
445
|
+
Recurse into gTree children (default ``True``).
|
|
446
|
+
print_ : bool or callable, optional
|
|
447
|
+
If ``True`` (default), print the listing to stdout. If a callable,
|
|
448
|
+
call it with the listing. If ``False``, suppress output.
|
|
449
|
+
|
|
450
|
+
Returns
|
|
451
|
+
-------
|
|
452
|
+
FlatGridListing
|
|
453
|
+
The generated listing object.
|
|
454
|
+
|
|
455
|
+
Raises
|
|
456
|
+
------
|
|
457
|
+
TypeError
|
|
458
|
+
If *print_* is not a bool or callable.
|
|
459
|
+
|
|
460
|
+
Examples
|
|
461
|
+
--------
|
|
462
|
+
>>> listing = grid_ls(print_=False) # capture without printing
|
|
463
|
+
"""
|
|
464
|
+
if x is None:
|
|
465
|
+
listing = _list_display_list(
|
|
466
|
+
grobs=grobs,
|
|
467
|
+
viewports=viewports,
|
|
468
|
+
full_names=fullNames,
|
|
469
|
+
recursive=recursive,
|
|
470
|
+
)
|
|
471
|
+
else:
|
|
472
|
+
listing = _list_grob(
|
|
473
|
+
x,
|
|
474
|
+
grobs=grobs,
|
|
475
|
+
viewports=viewports,
|
|
476
|
+
full_names=fullNames,
|
|
477
|
+
recursive=recursive,
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
if isinstance(print_, bool):
|
|
481
|
+
if print_:
|
|
482
|
+
nested_listing(listing)
|
|
483
|
+
elif callable(print_):
|
|
484
|
+
print_(listing)
|
|
485
|
+
else:
|
|
486
|
+
raise TypeError("invalid 'print_' argument")
|
|
487
|
+
|
|
488
|
+
return listing
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
# ---------------------------------------------------------------------------
|
|
492
|
+
# Public API -- grid.grep
|
|
493
|
+
# ---------------------------------------------------------------------------
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def grid_grep(
|
|
497
|
+
path: Union[str, GPath],
|
|
498
|
+
x: Any = None,
|
|
499
|
+
grep: bool = True,
|
|
500
|
+
global_: bool = True,
|
|
501
|
+
allDevices: bool = False,
|
|
502
|
+
viewports: bool = False,
|
|
503
|
+
strict: bool = False,
|
|
504
|
+
) -> Union[GPath, List[GPath], List[str]]:
|
|
505
|
+
"""Search for grobs (or viewports) whose names match *path*.
|
|
506
|
+
|
|
507
|
+
Returns the full grob/viewport path(s) that match the given *path*
|
|
508
|
+
pattern. This is the Python equivalent of R's ``grid.grep()``.
|
|
509
|
+
|
|
510
|
+
Parameters
|
|
511
|
+
----------
|
|
512
|
+
path : str or GPath
|
|
513
|
+
The name or pattern to search for.
|
|
514
|
+
x : Grob, GTree, or None, optional
|
|
515
|
+
Object to search within. ``None`` searches the display list.
|
|
516
|
+
grep : bool, optional
|
|
517
|
+
If ``True`` (default), use regex matching on path components.
|
|
518
|
+
global_ : bool, optional
|
|
519
|
+
If ``True`` (default), return all matches; otherwise return
|
|
520
|
+
the first match only.
|
|
521
|
+
allDevices : bool, optional
|
|
522
|
+
Not yet implemented.
|
|
523
|
+
viewports : bool, optional
|
|
524
|
+
If ``True``, also search viewport names.
|
|
525
|
+
strict : bool, optional
|
|
526
|
+
If ``True``, require exact depth matching.
|
|
527
|
+
|
|
528
|
+
Returns
|
|
529
|
+
-------
|
|
530
|
+
GPath or list[GPath] or list[str]
|
|
531
|
+
Matching path(s). Returns an empty list when no matches are found
|
|
532
|
+
and *global_* is ``True``, or ``None``-like empty list otherwise.
|
|
533
|
+
|
|
534
|
+
Raises
|
|
535
|
+
------
|
|
536
|
+
NotImplementedError
|
|
537
|
+
If *allDevices* is ``True``.
|
|
538
|
+
"""
|
|
539
|
+
if allDevices:
|
|
540
|
+
raise NotImplementedError("allDevices is not yet implemented")
|
|
541
|
+
|
|
542
|
+
if isinstance(path, str):
|
|
543
|
+
gpath = GPath(path)
|
|
544
|
+
elif isinstance(path, GPath):
|
|
545
|
+
gpath = path
|
|
546
|
+
else:
|
|
547
|
+
raise TypeError(f"invalid path: expected str or GPath, got {type(path).__name__}")
|
|
548
|
+
|
|
549
|
+
depth = gpath.n
|
|
550
|
+
path_pieces = list(gpath.components)
|
|
551
|
+
|
|
552
|
+
# Normalise grep to a per-component list
|
|
553
|
+
if isinstance(grep, bool):
|
|
554
|
+
grep_flags = [grep] * depth
|
|
555
|
+
else:
|
|
556
|
+
grep_flags = list(grep)
|
|
557
|
+
while len(grep_flags) < depth:
|
|
558
|
+
grep_flags.append(grep_flags[-1] if grep_flags else False)
|
|
559
|
+
|
|
560
|
+
# Build the flat listing
|
|
561
|
+
listing = grid_ls(
|
|
562
|
+
x,
|
|
563
|
+
grobs=True,
|
|
564
|
+
viewports=viewports,
|
|
565
|
+
fullNames=False,
|
|
566
|
+
recursive=True,
|
|
567
|
+
print_=False,
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
if not listing.names:
|
|
571
|
+
return []
|
|
572
|
+
|
|
573
|
+
# Filter to grob/gTree/vp listings only
|
|
574
|
+
keep_types = {"grobListing", "gTreeListing"}
|
|
575
|
+
if viewports:
|
|
576
|
+
keep_types.add("vpListing")
|
|
577
|
+
|
|
578
|
+
keep_indices = [
|
|
579
|
+
i for i, t in enumerate(listing.types) if t in keep_types
|
|
580
|
+
]
|
|
581
|
+
if not keep_indices:
|
|
582
|
+
return []
|
|
583
|
+
|
|
584
|
+
matches: list[GPath] = []
|
|
585
|
+
|
|
586
|
+
for i in keep_indices:
|
|
587
|
+
entry_name = listing.names[i]
|
|
588
|
+
entry_g_path = listing.g_paths[i]
|
|
589
|
+
entry_type = listing.types[i]
|
|
590
|
+
|
|
591
|
+
# Build the full path components for this entry
|
|
592
|
+
if entry_type.startswith("vp"):
|
|
593
|
+
entry_path_str = listing.vp_paths[i]
|
|
594
|
+
entry_depth = listing.vp_depths[i]
|
|
595
|
+
else:
|
|
596
|
+
entry_path_str = entry_g_path
|
|
597
|
+
entry_depth = listing.g_depths[i]
|
|
598
|
+
|
|
599
|
+
if entry_path_str:
|
|
600
|
+
dl_path_pieces = entry_path_str.split(PATH_SEP) + [entry_name]
|
|
601
|
+
else:
|
|
602
|
+
dl_path_pieces = [entry_name]
|
|
603
|
+
|
|
604
|
+
dl_depth = len(dl_path_pieces)
|
|
605
|
+
|
|
606
|
+
# Filter by depth
|
|
607
|
+
if strict:
|
|
608
|
+
if dl_depth != depth:
|
|
609
|
+
continue
|
|
610
|
+
else:
|
|
611
|
+
if dl_depth < depth:
|
|
612
|
+
continue
|
|
613
|
+
|
|
614
|
+
# Attempt match
|
|
615
|
+
matched = False
|
|
616
|
+
|
|
617
|
+
if strict:
|
|
618
|
+
# All path pieces must match at same position
|
|
619
|
+
all_match = True
|
|
620
|
+
for j in range(depth):
|
|
621
|
+
if grep_flags[j]:
|
|
622
|
+
if not re.search(path_pieces[j], dl_path_pieces[j]):
|
|
623
|
+
all_match = False
|
|
624
|
+
break
|
|
625
|
+
else:
|
|
626
|
+
if path_pieces[j] != dl_path_pieces[j]:
|
|
627
|
+
all_match = False
|
|
628
|
+
break
|
|
629
|
+
matched = all_match
|
|
630
|
+
else:
|
|
631
|
+
# Sliding window match
|
|
632
|
+
offset = 0
|
|
633
|
+
while offset + depth <= dl_depth:
|
|
634
|
+
all_match = True
|
|
635
|
+
for j in range(depth):
|
|
636
|
+
if grep_flags[j]:
|
|
637
|
+
if not re.search(path_pieces[j], dl_path_pieces[offset + j]):
|
|
638
|
+
all_match = False
|
|
639
|
+
break
|
|
640
|
+
else:
|
|
641
|
+
if path_pieces[j] != dl_path_pieces[offset + j]:
|
|
642
|
+
all_match = False
|
|
643
|
+
break
|
|
644
|
+
if all_match:
|
|
645
|
+
matched = True
|
|
646
|
+
break
|
|
647
|
+
offset += 1
|
|
648
|
+
|
|
649
|
+
if matched:
|
|
650
|
+
result_path = GPath(*dl_path_pieces)
|
|
651
|
+
if not global_:
|
|
652
|
+
return result_path
|
|
653
|
+
matches.append(result_path)
|
|
654
|
+
|
|
655
|
+
return matches
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
# ---------------------------------------------------------------------------
|
|
659
|
+
# Formatting functions
|
|
660
|
+
# ---------------------------------------------------------------------------
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
def nested_listing(
|
|
664
|
+
x: FlatGridListing,
|
|
665
|
+
gindent: str = " ",
|
|
666
|
+
vpindent: Optional[str] = None,
|
|
667
|
+
) -> None:
|
|
668
|
+
"""Print a :class:`FlatGridListing` with nested indentation.
|
|
669
|
+
|
|
670
|
+
Parameters
|
|
671
|
+
----------
|
|
672
|
+
x : FlatGridListing
|
|
673
|
+
The listing to print.
|
|
674
|
+
gindent : str, optional
|
|
675
|
+
String to repeat for each level of grob depth (default ``" "``).
|
|
676
|
+
vpindent : str or None, optional
|
|
677
|
+
String to repeat for each level of viewport depth. Defaults to
|
|
678
|
+
*gindent*.
|
|
679
|
+
|
|
680
|
+
Raises
|
|
681
|
+
------
|
|
682
|
+
TypeError
|
|
683
|
+
If *x* is not a :class:`FlatGridListing`.
|
|
684
|
+
"""
|
|
685
|
+
if not isinstance(x, FlatGridListing):
|
|
686
|
+
raise TypeError("invalid listing: expected FlatGridListing")
|
|
687
|
+
|
|
688
|
+
if vpindent is None:
|
|
689
|
+
vpindent = gindent
|
|
690
|
+
|
|
691
|
+
for i in range(len(x.names)):
|
|
692
|
+
prefix = gindent * x.g_depths[i] + vpindent * x.vp_depths[i]
|
|
693
|
+
print(f"{prefix}{x.names[i]}")
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
def path_listing(
|
|
697
|
+
x: FlatGridListing,
|
|
698
|
+
gvpSep: str = " | ",
|
|
699
|
+
gAlign: bool = True,
|
|
700
|
+
) -> None:
|
|
701
|
+
"""Print a :class:`FlatGridListing` with full paths.
|
|
702
|
+
|
|
703
|
+
Viewport entries show their accumulated path; grob entries show
|
|
704
|
+
``vpPath | grobPath``.
|
|
705
|
+
|
|
706
|
+
Parameters
|
|
707
|
+
----------
|
|
708
|
+
x : FlatGridListing
|
|
709
|
+
The listing to print.
|
|
710
|
+
gvpSep : str, optional
|
|
711
|
+
Separator between viewport path and grob path (default ``" | "``).
|
|
712
|
+
gAlign : bool, optional
|
|
713
|
+
If ``True`` (default), pad viewport paths so grob paths align.
|
|
714
|
+
|
|
715
|
+
Raises
|
|
716
|
+
------
|
|
717
|
+
TypeError
|
|
718
|
+
If *x* is not a :class:`FlatGridListing`.
|
|
719
|
+
"""
|
|
720
|
+
if not isinstance(x, FlatGridListing):
|
|
721
|
+
raise TypeError("invalid listing: expected FlatGridListing")
|
|
722
|
+
|
|
723
|
+
n = len(x.names)
|
|
724
|
+
if n == 0:
|
|
725
|
+
return
|
|
726
|
+
|
|
727
|
+
vp_listings = [t.startswith("vp") for t in x.types]
|
|
728
|
+
paths: list[str] = list(x.vp_paths)
|
|
729
|
+
|
|
730
|
+
# Build viewport display paths
|
|
731
|
+
max_len = 0
|
|
732
|
+
for i in range(n):
|
|
733
|
+
if vp_listings[i]:
|
|
734
|
+
paths[i] = _inc_path(paths[i], x.names[i])
|
|
735
|
+
max_len = max(max_len, len(paths[i]))
|
|
736
|
+
|
|
737
|
+
if not any(vp_listings):
|
|
738
|
+
max_len = max((len(p) for p in paths), default=0)
|
|
739
|
+
|
|
740
|
+
# Build grob display paths
|
|
741
|
+
for i in range(n):
|
|
742
|
+
if not vp_listings[i]:
|
|
743
|
+
grob_full = _inc_path(x.g_paths[i], x.names[i])
|
|
744
|
+
if gAlign:
|
|
745
|
+
padded = paths[i] + " " * max(0, max_len - len(paths[i]))
|
|
746
|
+
else:
|
|
747
|
+
padded = paths[i]
|
|
748
|
+
paths[i] = f"{padded}{gvpSep}{grob_full}"
|
|
749
|
+
|
|
750
|
+
for p in paths:
|
|
751
|
+
print(p)
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
def grob_path_listing(x: FlatGridListing, **kwargs: Any) -> None:
|
|
755
|
+
"""Print only the grob entries from a :class:`FlatGridListing`.
|
|
756
|
+
|
|
757
|
+
Parameters
|
|
758
|
+
----------
|
|
759
|
+
x : FlatGridListing
|
|
760
|
+
The listing to filter and print.
|
|
761
|
+
**kwargs
|
|
762
|
+
Additional keyword arguments forwarded to :func:`path_listing`.
|
|
763
|
+
"""
|
|
764
|
+
grob_indices = [
|
|
765
|
+
i for i, t in enumerate(x.types) if t.startswith("g")
|
|
766
|
+
]
|
|
767
|
+
if grob_indices:
|
|
768
|
+
sub = x.subset(grob_indices)
|
|
769
|
+
path_listing(sub, **kwargs)
|
|
770
|
+
|
|
771
|
+
|
|
772
|
+
# ---------------------------------------------------------------------------
|
|
773
|
+
# show_grob
|
|
774
|
+
# ---------------------------------------------------------------------------
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
def show_grob(
|
|
778
|
+
x: Any = None,
|
|
779
|
+
gPath: Optional[Union[str, GPath]] = None,
|
|
780
|
+
strict: bool = False,
|
|
781
|
+
grep: bool = False,
|
|
782
|
+
) -> Optional[Grob]:
|
|
783
|
+
"""Display information about a grob, optionally navigating via *gPath*.
|
|
784
|
+
|
|
785
|
+
If *x* is ``None``, the display list is searched. Returns the
|
|
786
|
+
located grob (or ``None`` if not found).
|
|
787
|
+
|
|
788
|
+
Parameters
|
|
789
|
+
----------
|
|
790
|
+
x : Grob, GTree, or None, optional
|
|
791
|
+
The grob to inspect. ``None`` searches the display list.
|
|
792
|
+
gPath : str, GPath, or None, optional
|
|
793
|
+
Path to a specific child within *x* or the display list.
|
|
794
|
+
strict : bool, optional
|
|
795
|
+
Require exact depth matching when resolving *gPath*.
|
|
796
|
+
grep : bool, optional
|
|
797
|
+
Use regex matching when resolving *gPath*.
|
|
798
|
+
|
|
799
|
+
Returns
|
|
800
|
+
-------
|
|
801
|
+
Grob or None
|
|
802
|
+
The located grob, or ``None`` if not found.
|
|
803
|
+
"""
|
|
804
|
+
if x is None and gPath is None:
|
|
805
|
+
# List the display list
|
|
806
|
+
listing = grid_ls(print_=True)
|
|
807
|
+
return None
|
|
808
|
+
|
|
809
|
+
if x is None:
|
|
810
|
+
# Search display list by gPath
|
|
811
|
+
from ._edit import grid_get
|
|
812
|
+
result = grid_get(gPath, strict=strict, grep=grep, global_=False)
|
|
813
|
+
if result is not None and is_grob(result):
|
|
814
|
+
print(repr(result))
|
|
815
|
+
return result
|
|
816
|
+
|
|
817
|
+
if gPath is not None:
|
|
818
|
+
# Navigate into x
|
|
819
|
+
if isinstance(gPath, str):
|
|
820
|
+
gPath = GPath(gPath)
|
|
821
|
+
if isinstance(x, GTree):
|
|
822
|
+
try:
|
|
823
|
+
from ._grob import get_grob
|
|
824
|
+
child = get_grob(x, gPath)
|
|
825
|
+
print(repr(child))
|
|
826
|
+
return child
|
|
827
|
+
except (KeyError, TypeError):
|
|
828
|
+
return None
|
|
829
|
+
return None
|
|
830
|
+
|
|
831
|
+
# Just show x
|
|
832
|
+
print(repr(x))
|
|
833
|
+
return x
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
# ---------------------------------------------------------------------------
|
|
837
|
+
# get_names / child_names (deprecated helpers)
|
|
838
|
+
# ---------------------------------------------------------------------------
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
def get_names(x: Any = None) -> List[str]:
|
|
842
|
+
"""Return names of grobs on the display list or children of *x*.
|
|
843
|
+
|
|
844
|
+
.. deprecated::
|
|
845
|
+
Use ``grid_ls(print_=False)`` instead.
|
|
846
|
+
|
|
847
|
+
Parameters
|
|
848
|
+
----------
|
|
849
|
+
x : GTree or None, optional
|
|
850
|
+
A gTree whose children to list. ``None`` lists the display list.
|
|
851
|
+
|
|
852
|
+
Returns
|
|
853
|
+
-------
|
|
854
|
+
list[str]
|
|
855
|
+
Names of grobs or children.
|
|
856
|
+
"""
|
|
857
|
+
warnings.warn(
|
|
858
|
+
"get_names is deprecated; use grid_ls(print_=False) instead",
|
|
859
|
+
DeprecationWarning,
|
|
860
|
+
stacklevel=2,
|
|
861
|
+
)
|
|
862
|
+
if x is None:
|
|
863
|
+
listing = grid_ls(print_=False)
|
|
864
|
+
return list(listing.names)
|
|
865
|
+
if isinstance(x, GTree):
|
|
866
|
+
return list(x._children_order)
|
|
867
|
+
if is_grob(x):
|
|
868
|
+
return [x.name]
|
|
869
|
+
return []
|
|
870
|
+
|
|
871
|
+
|
|
872
|
+
def child_names(x: Any) -> List[str]:
|
|
873
|
+
"""Return the names of the children of gTree *x*.
|
|
874
|
+
|
|
875
|
+
.. deprecated::
|
|
876
|
+
Use ``x._children_order`` directly or ``grid_ls(x, print_=False)``.
|
|
877
|
+
|
|
878
|
+
Parameters
|
|
879
|
+
----------
|
|
880
|
+
x : GTree
|
|
881
|
+
The gTree to inspect.
|
|
882
|
+
|
|
883
|
+
Returns
|
|
884
|
+
-------
|
|
885
|
+
list[str]
|
|
886
|
+
Names of children.
|
|
887
|
+
"""
|
|
888
|
+
warnings.warn(
|
|
889
|
+
"child_names is deprecated; use grid_ls(x, print_=False) instead",
|
|
890
|
+
DeprecationWarning,
|
|
891
|
+
stacklevel=2,
|
|
892
|
+
)
|
|
893
|
+
if isinstance(x, GTree):
|
|
894
|
+
return list(x._children_order)
|
|
895
|
+
return []
|