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.
@@ -0,0 +1,507 @@
1
+ """Display-list management for grid_py -- Python port of R's ``grid`` display list.
2
+
3
+ In R's *grid* package the display list records every drawing operation so that
4
+ the scene can be replayed when a device is resized, copied, or printed. This
5
+ module provides the equivalent bookkeeping for grid_py:
6
+
7
+ * :class:`DisplayList` -- the mutable, iterable list that records and replays
8
+ operations (analogous to R's ``grid.display.list``).
9
+ * :class:`DLOperation` -- abstract base class for a single recorded operation.
10
+ * Concrete operation subclasses: :class:`DLDrawGrob`, :class:`DLPushViewport`,
11
+ :class:`DLPopViewport`, :class:`DLUpViewport`, :class:`DLDownViewport`,
12
+ :class:`DLEditGrob`, :class:`DLSetGpar`.
13
+
14
+ References
15
+ ----------
16
+ R source: ``src/library/grid/R/displaylist.R``, ``src/library/grid/R/grid.R``
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from typing import (
22
+ Any,
23
+ Callable,
24
+ Dict,
25
+ Iterator,
26
+ List,
27
+ Optional,
28
+ )
29
+
30
+ __all__ = [
31
+ "DisplayList",
32
+ "DLOperation",
33
+ "DLDrawGrob",
34
+ "DLPushViewport",
35
+ "DLPopViewport",
36
+ "DLUpViewport",
37
+ "DLDownViewport",
38
+ "DLEditGrob",
39
+ "DLSetGpar",
40
+ ]
41
+
42
+
43
+ # ---------------------------------------------------------------------------
44
+ # DisplayList
45
+ # ---------------------------------------------------------------------------
46
+
47
+
48
+ class DisplayList:
49
+ """A mutable list of recorded drawing operations.
50
+
51
+ The display list captures every graphical operation so it can be replayed
52
+ later (e.g. when a device is resized). Recording can be temporarily
53
+ disabled via :meth:`set_enabled`.
54
+
55
+ Parameters
56
+ ----------
57
+ None
58
+
59
+ Attributes
60
+ ----------
61
+ _items : list[DLOperation]
62
+ Recorded operations in chronological order.
63
+ _enabled : bool
64
+ Whether new operations are currently being recorded.
65
+
66
+ Examples
67
+ --------
68
+ >>> dl = DisplayList()
69
+ >>> dl.record(DLDrawGrob(grob=some_grob))
70
+ >>> len(dl)
71
+ 1
72
+ >>> dl.clear()
73
+ >>> len(dl)
74
+ 0
75
+ """
76
+
77
+ def __init__(self) -> None:
78
+ self._items: List[DLOperation] = []
79
+ self._enabled: bool = True
80
+
81
+ # -- recording -----------------------------------------------------------
82
+
83
+ def record(self, item: DLOperation) -> None:
84
+ """Append *item* to the display list if recording is enabled.
85
+
86
+ Parameters
87
+ ----------
88
+ item : DLOperation
89
+ The operation to record.
90
+ """
91
+ if self._enabled:
92
+ self._items.append(item)
93
+
94
+ # -- query / access ------------------------------------------------------
95
+
96
+ def get_items(self) -> List[DLOperation]:
97
+ """Return a shallow copy of the recorded operations.
98
+
99
+ Returns
100
+ -------
101
+ list[DLOperation]
102
+ A copy of the internal list.
103
+ """
104
+ return list(self._items)
105
+
106
+ def is_enabled(self) -> bool:
107
+ """Return whether recording is currently enabled.
108
+
109
+ Returns
110
+ -------
111
+ bool
112
+ ``True`` if recording is on.
113
+ """
114
+ return self._enabled
115
+
116
+ def set_enabled(self, on: bool) -> None:
117
+ """Enable or disable recording.
118
+
119
+ Parameters
120
+ ----------
121
+ on : bool
122
+ ``True`` to enable recording, ``False`` to disable.
123
+ """
124
+ self._enabled = on
125
+
126
+ # -- mutation ------------------------------------------------------------
127
+
128
+ def clear(self) -> None:
129
+ """Remove all recorded operations."""
130
+ self._items.clear()
131
+
132
+ # -- replay / apply ------------------------------------------------------
133
+
134
+ def replay(self, state: Any) -> None:
135
+ """Replay every recorded operation against *state*.
136
+
137
+ Each item's :meth:`~DLOperation.replay` method is called in order if
138
+ the item exposes one.
139
+
140
+ Parameters
141
+ ----------
142
+ state : Any
143
+ An opaque state object (typically the current graphics state)
144
+ passed to each operation's ``replay`` method.
145
+ """
146
+ for item in self._items:
147
+ if hasattr(item, "replay"):
148
+ item.replay(state)
149
+
150
+ def apply(self, fn: Callable[[DLOperation], Any]) -> List[Any]:
151
+ """Apply *fn* to every recorded item (``grid.DLapply`` equivalent).
152
+
153
+ Parameters
154
+ ----------
155
+ fn : callable
156
+ A function that accepts a single :class:`DLOperation` argument.
157
+
158
+ Returns
159
+ -------
160
+ list[Any]
161
+ The collected return values of *fn* for each item.
162
+ """
163
+ return [fn(item) for item in self._items]
164
+
165
+ def extend(self, items: List[DLOperation]) -> None:
166
+ """Append every element of *items* to the display list.
167
+
168
+ Parameters
169
+ ----------
170
+ items : list[DLOperation]
171
+ Operations to add.
172
+ """
173
+ self._items.extend(items)
174
+
175
+ def append(self, item: DLOperation) -> None:
176
+ """Unconditionally append *item* (ignores enabled flag).
177
+
178
+ Parameters
179
+ ----------
180
+ item : DLOperation
181
+ Operation to add.
182
+ """
183
+ self._items.append(item)
184
+
185
+ # -- dunder protocols ----------------------------------------------------
186
+
187
+ def __len__(self) -> int:
188
+ """Return the number of recorded operations."""
189
+ return len(self._items)
190
+
191
+ def __iter__(self) -> Iterator[DLOperation]:
192
+ """Iterate over recorded operations in order."""
193
+ return iter(self._items)
194
+
195
+ def __getitem__(self, i: int) -> DLOperation:
196
+ """Return the *i*-th recorded operation.
197
+
198
+ Parameters
199
+ ----------
200
+ i : int
201
+ Zero-based index.
202
+
203
+ Returns
204
+ -------
205
+ DLOperation
206
+ """
207
+ return self._items[i]
208
+
209
+ def __contains__(self, item: Any) -> bool:
210
+ """Return ``True`` if *item* is in the display list."""
211
+ return item in self._items
212
+
213
+ def __repr__(self) -> str: # pragma: no cover
214
+ return (
215
+ f"DisplayList(n_items={len(self._items)}, "
216
+ f"enabled={self._enabled})"
217
+ )
218
+
219
+
220
+ # ---------------------------------------------------------------------------
221
+ # DLOperation base class
222
+ # ---------------------------------------------------------------------------
223
+
224
+
225
+ class DLOperation:
226
+ """Base class for a single display-list entry.
227
+
228
+ Subclasses should override :meth:`replay` to perform their specific
229
+ action when the display list is replayed.
230
+
231
+ Parameters
232
+ ----------
233
+ op_type : str
234
+ A short human-readable tag describing the operation kind
235
+ (e.g. ``"draw_grob"``, ``"push_vp"``).
236
+ **kwargs : Any
237
+ Arbitrary keyword arguments stored in :attr:`params`.
238
+
239
+ Attributes
240
+ ----------
241
+ op_type : str
242
+ The operation tag.
243
+ params : dict[str, Any]
244
+ Additional data captured at recording time.
245
+ """
246
+
247
+ def __init__(self, op_type: str, **kwargs: Any) -> None:
248
+ self.op_type: str = op_type
249
+ self.params: Dict[str, Any] = kwargs
250
+
251
+ def replay(self, state: Any) -> None:
252
+ """Replay this operation.
253
+
254
+ The default implementation is a no-op. Subclasses should override
255
+ this to perform their specific action.
256
+
257
+ Parameters
258
+ ----------
259
+ state : Any
260
+ The current graphics state.
261
+ """
262
+
263
+ def __repr__(self) -> str: # pragma: no cover
264
+ kw = ", ".join(f"{k}={v!r}" for k, v in self.params.items())
265
+ extra = f", {kw}" if kw else ""
266
+ return f"{type(self).__name__}(op_type={self.op_type!r}{extra})"
267
+
268
+
269
+ # ---------------------------------------------------------------------------
270
+ # Concrete DLOperation subclasses
271
+ # ---------------------------------------------------------------------------
272
+
273
+
274
+ class DLDrawGrob(DLOperation):
275
+ """Records a grob drawing operation.
276
+
277
+ Parameters
278
+ ----------
279
+ grob : Any
280
+ The graphical object that was drawn.
281
+ **kwargs : Any
282
+ Additional parameters forwarded to :class:`DLOperation`.
283
+
284
+ Attributes
285
+ ----------
286
+ grob : Any
287
+ Reference to the drawn grob.
288
+ """
289
+
290
+ def __init__(self, grob: Any = None, **kwargs: Any) -> None:
291
+ super().__init__(op_type="draw_grob", grob=grob, **kwargs)
292
+ self.grob: Any = grob
293
+
294
+ def replay(self, state: Any) -> None:
295
+ """Replay the grob drawing.
296
+
297
+ Parameters
298
+ ----------
299
+ state : Any
300
+ The current graphics state.
301
+ """
302
+ if self.grob is not None and hasattr(self.grob, "draw"):
303
+ self.grob.draw(state)
304
+
305
+
306
+ class DLPushViewport(DLOperation):
307
+ """Records a viewport push operation.
308
+
309
+ Parameters
310
+ ----------
311
+ viewport : Any
312
+ The viewport that was pushed.
313
+ **kwargs : Any
314
+ Additional parameters forwarded to :class:`DLOperation`.
315
+
316
+ Attributes
317
+ ----------
318
+ viewport : Any
319
+ Reference to the pushed viewport.
320
+ """
321
+
322
+ def __init__(self, viewport: Any = None, **kwargs: Any) -> None:
323
+ super().__init__(op_type="push_vp", viewport=viewport, **kwargs)
324
+ self.viewport: Any = viewport
325
+
326
+ def replay(self, state: Any) -> None:
327
+ """Replay the viewport push.
328
+
329
+ Parameters
330
+ ----------
331
+ state : Any
332
+ The current graphics state.
333
+ """
334
+ if state is not None and hasattr(state, "push_viewport"):
335
+ state.push_viewport(self.viewport)
336
+
337
+
338
+ class DLPopViewport(DLOperation):
339
+ """Records a viewport pop operation.
340
+
341
+ Parameters
342
+ ----------
343
+ n : int
344
+ Number of viewports to pop (default ``1``).
345
+ **kwargs : Any
346
+ Additional parameters forwarded to :class:`DLOperation`.
347
+
348
+ Attributes
349
+ ----------
350
+ n : int
351
+ Number of viewports to pop.
352
+ """
353
+
354
+ def __init__(self, n: int = 1, **kwargs: Any) -> None:
355
+ super().__init__(op_type="pop_vp", n=n, **kwargs)
356
+ self.n: int = n
357
+
358
+ def replay(self, state: Any) -> None:
359
+ """Replay the viewport pop.
360
+
361
+ Parameters
362
+ ----------
363
+ state : Any
364
+ The current graphics state.
365
+ """
366
+ if state is not None and hasattr(state, "pop_viewport"):
367
+ state.pop_viewport(self.n)
368
+
369
+
370
+ class DLUpViewport(DLOperation):
371
+ """Records an up-viewport navigation.
372
+
373
+ Parameters
374
+ ----------
375
+ n : int
376
+ Number of levels to go up (default ``1``).
377
+ **kwargs : Any
378
+ Additional parameters forwarded to :class:`DLOperation`.
379
+
380
+ Attributes
381
+ ----------
382
+ n : int
383
+ Number of levels to navigate up.
384
+ """
385
+
386
+ def __init__(self, n: int = 1, **kwargs: Any) -> None:
387
+ super().__init__(op_type="up_vp", n=n, **kwargs)
388
+ self.n: int = n
389
+
390
+ def replay(self, state: Any) -> None:
391
+ """Replay the up-viewport navigation.
392
+
393
+ Parameters
394
+ ----------
395
+ state : Any
396
+ The current graphics state.
397
+ """
398
+ if state is not None and hasattr(state, "up_viewport"):
399
+ state.up_viewport(self.n)
400
+
401
+
402
+ class DLDownViewport(DLOperation):
403
+ """Records a down-viewport navigation.
404
+
405
+ Parameters
406
+ ----------
407
+ path : Any
408
+ Viewport name or path to navigate down to.
409
+ **kwargs : Any
410
+ Additional parameters forwarded to :class:`DLOperation`.
411
+
412
+ Attributes
413
+ ----------
414
+ path : Any
415
+ The viewport path used for navigation.
416
+ """
417
+
418
+ def __init__(self, path: Any = None, **kwargs: Any) -> None:
419
+ super().__init__(op_type="down_vp", path=path, **kwargs)
420
+ self.path: Any = path
421
+
422
+ def replay(self, state: Any) -> None:
423
+ """Replay the down-viewport navigation.
424
+
425
+ Parameters
426
+ ----------
427
+ state : Any
428
+ The current graphics state.
429
+ """
430
+ if state is not None and hasattr(state, "down_viewport"):
431
+ state.down_viewport(self.path)
432
+
433
+
434
+ class DLEditGrob(DLOperation):
435
+ """Records a grob edit operation.
436
+
437
+ Parameters
438
+ ----------
439
+ grob_name : Optional[str]
440
+ Name of the grob being edited.
441
+ specs : Any
442
+ Edit specifications (e.g. a :class:`GEdit` or dict of new values).
443
+ **kwargs : Any
444
+ Additional parameters forwarded to :class:`DLOperation`.
445
+
446
+ Attributes
447
+ ----------
448
+ grob_name : Optional[str]
449
+ Name of the target grob.
450
+ specs : Any
451
+ Edit specifications.
452
+ """
453
+
454
+ def __init__(
455
+ self,
456
+ grob_name: Optional[str] = None,
457
+ specs: Any = None,
458
+ **kwargs: Any,
459
+ ) -> None:
460
+ super().__init__(
461
+ op_type="edit_grob", grob_name=grob_name, specs=specs, **kwargs
462
+ )
463
+ self.grob_name: Optional[str] = grob_name
464
+ self.specs: Any = specs
465
+
466
+ def replay(self, state: Any) -> None:
467
+ """Replay the grob edit.
468
+
469
+ Parameters
470
+ ----------
471
+ state : Any
472
+ The current graphics state.
473
+ """
474
+ if state is not None and hasattr(state, "edit_grob"):
475
+ state.edit_grob(self.grob_name, self.specs)
476
+
477
+
478
+ class DLSetGpar(DLOperation):
479
+ """Records a graphical-parameter change.
480
+
481
+ Parameters
482
+ ----------
483
+ gpar : Any
484
+ The ``Gpar`` instance or dict of graphical parameters that was set.
485
+ **kwargs : Any
486
+ Additional parameters forwarded to :class:`DLOperation`.
487
+
488
+ Attributes
489
+ ----------
490
+ gpar : Any
491
+ The graphical parameters.
492
+ """
493
+
494
+ def __init__(self, gpar: Any = None, **kwargs: Any) -> None:
495
+ super().__init__(op_type="set_gpar", gpar=gpar, **kwargs)
496
+ self.gpar: Any = gpar
497
+
498
+ def replay(self, state: Any) -> None:
499
+ """Replay the gpar change.
500
+
501
+ Parameters
502
+ ----------
503
+ state : Any
504
+ The current graphics state.
505
+ """
506
+ if state is not None and hasattr(state, "set_gpar"):
507
+ state.set_gpar(self.gpar)