ggplot2-python 4.0.2.9000__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.
- ggplot2_py/__init__.py +852 -0
- ggplot2_py/_compat.py +475 -0
- ggplot2_py/_plugins.py +129 -0
- ggplot2_py/_utils.py +544 -0
- ggplot2_py/aes.py +586 -0
- ggplot2_py/annotation.py +540 -0
- ggplot2_py/coord.py +2108 -0
- ggplot2_py/coords/__init__.py +49 -0
- ggplot2_py/datasets.py +265 -0
- ggplot2_py/draw_key.py +454 -0
- ggplot2_py/facet.py +1456 -0
- ggplot2_py/fortify.py +95 -0
- ggplot2_py/geom.py +4516 -0
- ggplot2_py/geoms/__init__.py +12 -0
- ggplot2_py/ggproto.py +279 -0
- ggplot2_py/guide.py +2925 -0
- ggplot2_py/guide_axis.py +615 -0
- ggplot2_py/guide_colourbar.py +657 -0
- ggplot2_py/guide_legend.py +1061 -0
- ggplot2_py/guides/__init__.py +8 -0
- ggplot2_py/labeller.py +296 -0
- ggplot2_py/labels.py +309 -0
- ggplot2_py/layer.py +954 -0
- ggplot2_py/layout.py +754 -0
- ggplot2_py/limits.py +314 -0
- ggplot2_py/plot.py +1401 -0
- ggplot2_py/plot_render.py +866 -0
- ggplot2_py/position.py +1269 -0
- ggplot2_py/protocols.py +171 -0
- ggplot2_py/py.typed +0 -0
- ggplot2_py/qplot.py +233 -0
- ggplot2_py/resources/diamonds.csv +53941 -0
- ggplot2_py/resources/economics.csv +575 -0
- ggplot2_py/resources/economics_long.csv +2871 -0
- ggplot2_py/resources/faithfuld.csv +5626 -0
- ggplot2_py/resources/luv_colours.csv +658 -0
- ggplot2_py/resources/midwest.csv +438 -0
- ggplot2_py/resources/mpg.csv +235 -0
- ggplot2_py/resources/msleep.csv +84 -0
- ggplot2_py/resources/presidential.csv +13 -0
- ggplot2_py/resources/seals.csv +1156 -0
- ggplot2_py/resources/txhousing.csv +8603 -0
- ggplot2_py/save.py +316 -0
- ggplot2_py/scale.py +2727 -0
- ggplot2_py/scales/__init__.py +4252 -0
- ggplot2_py/stat.py +6071 -0
- ggplot2_py/stats/__init__.py +9 -0
- ggplot2_py/theme.py +490 -0
- ggplot2_py/theme_defaults.py +1350 -0
- ggplot2_py/theme_elements.py +2052 -0
- ggplot2_python-4.0.2.9000.dist-info/METADATA +179 -0
- ggplot2_python-4.0.2.9000.dist-info/RECORD +54 -0
- ggplot2_python-4.0.2.9000.dist-info/WHEEL +4 -0
- ggplot2_python-4.0.2.9000.dist-info/licenses/LICENSE +3 -0
ggplot2_py/theme.py
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Theme creation and management for ggplot2.
|
|
3
|
+
|
|
4
|
+
Provides the ``Theme`` class, the ``theme()`` constructor, global theme
|
|
5
|
+
state functions (``theme_get``, ``theme_set``, etc.), and the ``+`` /
|
|
6
|
+
``%+replace%`` operators for combining themes.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import copy
|
|
12
|
+
from typing import Any, Dict, Optional
|
|
13
|
+
|
|
14
|
+
from ggplot2_py._compat import cli_abort, cli_warn
|
|
15
|
+
from ggplot2_py.theme_elements import (
|
|
16
|
+
Element,
|
|
17
|
+
ElementBlank,
|
|
18
|
+
is_theme_element,
|
|
19
|
+
merge_element,
|
|
20
|
+
combine_elements,
|
|
21
|
+
_ggplot_global,
|
|
22
|
+
get_element_tree,
|
|
23
|
+
calc_element,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"Theme",
|
|
28
|
+
"theme",
|
|
29
|
+
"is_theme",
|
|
30
|
+
"complete_theme",
|
|
31
|
+
"add_theme",
|
|
32
|
+
"theme_get",
|
|
33
|
+
"theme_set",
|
|
34
|
+
"theme_update",
|
|
35
|
+
"theme_replace",
|
|
36
|
+
"set_theme",
|
|
37
|
+
"get_theme",
|
|
38
|
+
"reset_theme_settings",
|
|
39
|
+
"update_theme",
|
|
40
|
+
"replace_theme",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
# Theme class
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
class Theme:
|
|
49
|
+
"""A ggplot2 theme object.
|
|
50
|
+
|
|
51
|
+
Internally a ``Theme`` is a dictionary-like container mapping element
|
|
52
|
+
names (strings) to element objects (``Element`` subclasses, ``Unit``,
|
|
53
|
+
scalars, etc.). It supports ``+`` to merge themes and item access via
|
|
54
|
+
``[]`` or ``.get()``.
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
elements : dict
|
|
59
|
+
Mapping of element names to values.
|
|
60
|
+
complete : bool
|
|
61
|
+
``True`` for complete themes (e.g. ``theme_grey()``).
|
|
62
|
+
validate : bool
|
|
63
|
+
Whether to validate elements against the element tree.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
elements: Optional[Dict[str, Any]] = None,
|
|
69
|
+
complete: bool = False,
|
|
70
|
+
validate: bool = True,
|
|
71
|
+
) -> None:
|
|
72
|
+
self._elements: Dict[str, Any] = dict(elements) if elements else {}
|
|
73
|
+
self.complete = complete
|
|
74
|
+
self.validate = validate
|
|
75
|
+
|
|
76
|
+
# -- dict-like access ---------------------------------------------------
|
|
77
|
+
|
|
78
|
+
def __getitem__(self, key: str) -> Any:
|
|
79
|
+
return self._elements[key]
|
|
80
|
+
|
|
81
|
+
def __setitem__(self, key: str, value: Any) -> None:
|
|
82
|
+
self._elements[key] = value
|
|
83
|
+
|
|
84
|
+
def __contains__(self, key: str) -> bool:
|
|
85
|
+
return key in self._elements
|
|
86
|
+
|
|
87
|
+
def __iter__(self):
|
|
88
|
+
return iter(self._elements)
|
|
89
|
+
|
|
90
|
+
def __len__(self) -> int:
|
|
91
|
+
return len(self._elements)
|
|
92
|
+
|
|
93
|
+
def get(self, key: str, default: Any = None) -> Any:
|
|
94
|
+
"""Return the element for *key*, or *default* if absent.
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
key : str
|
|
99
|
+
Element name.
|
|
100
|
+
default : Any
|
|
101
|
+
Fallback value.
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
Any
|
|
106
|
+
"""
|
|
107
|
+
return self._elements.get(key, default)
|
|
108
|
+
|
|
109
|
+
def keys(self):
|
|
110
|
+
return self._elements.keys()
|
|
111
|
+
|
|
112
|
+
def values(self):
|
|
113
|
+
return self._elements.values()
|
|
114
|
+
|
|
115
|
+
def items(self):
|
|
116
|
+
return self._elements.items()
|
|
117
|
+
|
|
118
|
+
def names(self):
|
|
119
|
+
"""Return element names (R-compatible alias for ``keys``)."""
|
|
120
|
+
return list(self._elements.keys())
|
|
121
|
+
|
|
122
|
+
def update(self, other: Dict[str, Any]) -> None:
|
|
123
|
+
"""Update elements in-place from *other*.
|
|
124
|
+
|
|
125
|
+
Parameters
|
|
126
|
+
----------
|
|
127
|
+
other : dict
|
|
128
|
+
Mapping of element names to values.
|
|
129
|
+
"""
|
|
130
|
+
self._elements.update(other)
|
|
131
|
+
|
|
132
|
+
def copy(self) -> "Theme":
|
|
133
|
+
"""Return a shallow copy of this theme.
|
|
134
|
+
|
|
135
|
+
Returns
|
|
136
|
+
-------
|
|
137
|
+
Theme
|
|
138
|
+
"""
|
|
139
|
+
return Theme(
|
|
140
|
+
elements=dict(self._elements),
|
|
141
|
+
complete=self.complete,
|
|
142
|
+
validate=self.validate,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# -- operators ----------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
def __add__(self, other: Any) -> "Theme":
|
|
148
|
+
"""Merge another theme into this one (``self + other``).
|
|
149
|
+
|
|
150
|
+
Properties in *other* override those in *self*; ``None`` properties
|
|
151
|
+
in *other* are filled from *self* (element-level merge).
|
|
152
|
+
|
|
153
|
+
Parameters
|
|
154
|
+
----------
|
|
155
|
+
other : Theme or None
|
|
156
|
+
Theme to merge.
|
|
157
|
+
|
|
158
|
+
Returns
|
|
159
|
+
-------
|
|
160
|
+
Theme
|
|
161
|
+
"""
|
|
162
|
+
if other is None:
|
|
163
|
+
return self.copy()
|
|
164
|
+
if not isinstance(other, Theme):
|
|
165
|
+
cli_abort(
|
|
166
|
+
f"Cannot add {type(other).__name__} to a Theme object."
|
|
167
|
+
)
|
|
168
|
+
return add_theme(self, other)
|
|
169
|
+
|
|
170
|
+
def __radd__(self, other: Any) -> "Theme":
|
|
171
|
+
"""Support ``None + theme``."""
|
|
172
|
+
if other is None or other == 0:
|
|
173
|
+
return self.copy()
|
|
174
|
+
if isinstance(other, Theme):
|
|
175
|
+
return add_theme(other, self)
|
|
176
|
+
return NotImplemented
|
|
177
|
+
|
|
178
|
+
def __repr__(self) -> str:
|
|
179
|
+
n = len(self._elements)
|
|
180
|
+
tag = "complete " if self.complete else ""
|
|
181
|
+
return f"<Theme ({tag}{n} elements)>"
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# ---------------------------------------------------------------------------
|
|
185
|
+
# theme() constructor
|
|
186
|
+
# ---------------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
def theme(complete: bool = False, validate: bool = True, **kwargs: Any) -> Theme:
|
|
189
|
+
"""Create a ``Theme`` object.
|
|
190
|
+
|
|
191
|
+
Parameters
|
|
192
|
+
----------
|
|
193
|
+
complete : bool
|
|
194
|
+
If ``True``, marks this as a complete theme (like ``theme_grey()``).
|
|
195
|
+
Complete themes set ``inherit_blank=True`` on all elements.
|
|
196
|
+
validate : bool
|
|
197
|
+
If ``True``, element values are checked against the element tree.
|
|
198
|
+
**kwargs
|
|
199
|
+
Named theme elements (e.g. ``line=element_line(...)``).
|
|
200
|
+
|
|
201
|
+
Returns
|
|
202
|
+
-------
|
|
203
|
+
Theme
|
|
204
|
+
A new theme object.
|
|
205
|
+
|
|
206
|
+
Examples
|
|
207
|
+
--------
|
|
208
|
+
>>> from ggplot2_py.theme_elements import element_text, element_rect, rel
|
|
209
|
+
>>> t = theme(plot_title=element_text(size=rel(1.2), hjust=0))
|
|
210
|
+
"""
|
|
211
|
+
# Normalise dots to dashes (Python kwargs use underscores but theme
|
|
212
|
+
# element names use dots in R). We accept both forms.
|
|
213
|
+
elements: Dict[str, Any] = {}
|
|
214
|
+
for key, value in kwargs.items():
|
|
215
|
+
# Convert underscores to dots so that ``axis_text_x`` -> ``axis.text.x``
|
|
216
|
+
canonical = key.replace("_", ".")
|
|
217
|
+
# But preserve 'inherit.blank' as-is (it's an element property, not
|
|
218
|
+
# a theme key). Actually theme keys never contain "inherit.blank"
|
|
219
|
+
# so this is fine.
|
|
220
|
+
elements[canonical] = value
|
|
221
|
+
|
|
222
|
+
# If complete, set inherit_blank = True on all elements
|
|
223
|
+
if complete:
|
|
224
|
+
for key, el in elements.items():
|
|
225
|
+
if isinstance(el, Element) and hasattr(el, "inherit_blank"):
|
|
226
|
+
el.inherit_blank = True
|
|
227
|
+
|
|
228
|
+
return Theme(elements=elements, complete=complete, validate=validate)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# ---------------------------------------------------------------------------
|
|
232
|
+
# is_theme
|
|
233
|
+
# ---------------------------------------------------------------------------
|
|
234
|
+
|
|
235
|
+
def is_theme(x: Any) -> bool:
|
|
236
|
+
"""Test whether *x* is a ``Theme`` object.
|
|
237
|
+
|
|
238
|
+
Parameters
|
|
239
|
+
----------
|
|
240
|
+
x : Any
|
|
241
|
+
Object to test.
|
|
242
|
+
|
|
243
|
+
Returns
|
|
244
|
+
-------
|
|
245
|
+
bool
|
|
246
|
+
"""
|
|
247
|
+
return isinstance(x, Theme)
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
# ---------------------------------------------------------------------------
|
|
251
|
+
# add_theme — the engine behind ``+``
|
|
252
|
+
# ---------------------------------------------------------------------------
|
|
253
|
+
|
|
254
|
+
def add_theme(t1: Theme, t2: Theme) -> Theme:
|
|
255
|
+
"""Merge theme *t2* into *t1*.
|
|
256
|
+
|
|
257
|
+
Parameters
|
|
258
|
+
----------
|
|
259
|
+
t1 : Theme
|
|
260
|
+
The base theme (may also be a plain dict for the initial plot theme).
|
|
261
|
+
t2 : Theme
|
|
262
|
+
The theme to add (its elements override *t1*'s).
|
|
263
|
+
|
|
264
|
+
Returns
|
|
265
|
+
-------
|
|
266
|
+
Theme
|
|
267
|
+
A new theme with merged elements.
|
|
268
|
+
"""
|
|
269
|
+
if t2 is None:
|
|
270
|
+
if isinstance(t1, dict):
|
|
271
|
+
return Theme(t1)
|
|
272
|
+
return t1.copy()
|
|
273
|
+
|
|
274
|
+
# If t2 is complete, it replaces t1 entirely
|
|
275
|
+
if t2.complete:
|
|
276
|
+
return t2.copy()
|
|
277
|
+
|
|
278
|
+
if t1 is None:
|
|
279
|
+
return t2.copy()
|
|
280
|
+
|
|
281
|
+
# If t1 is a plain dict (e.g. initial plot theme), wrap it in a Theme
|
|
282
|
+
# so it has .validate, .copy(), .keys() etc. Mirrors R's
|
|
283
|
+
# ``if (!is_theme(t1) && is.list(t1)) t1 <- theme(!!!t1)``
|
|
284
|
+
if isinstance(t1, dict) and not isinstance(t1, Theme):
|
|
285
|
+
t1 = Theme(t1)
|
|
286
|
+
|
|
287
|
+
result = t1.copy()
|
|
288
|
+
for item in t2.keys():
|
|
289
|
+
try:
|
|
290
|
+
old_val = result.get(item)
|
|
291
|
+
new_val = t2[item]
|
|
292
|
+
merged = merge_element(new_val, old_val)
|
|
293
|
+
result[item] = merged
|
|
294
|
+
except Exception as exc:
|
|
295
|
+
cli_warn(f"Problem merging theme element '{item}': {exc}")
|
|
296
|
+
result[item] = t2[item]
|
|
297
|
+
|
|
298
|
+
result.validate = t1.validate and t2.validate
|
|
299
|
+
return result
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def theme_replace_op(e1: Theme, e2: Theme) -> Theme:
|
|
303
|
+
"""The ``%+replace%`` operator: replace elements wholesale.
|
|
304
|
+
|
|
305
|
+
Unlike ``+``, this does not merge element-level properties.
|
|
306
|
+
Missing elements in *e2* result in ``None`` in the output.
|
|
307
|
+
|
|
308
|
+
Parameters
|
|
309
|
+
----------
|
|
310
|
+
e1 : Theme
|
|
311
|
+
The base theme.
|
|
312
|
+
e2 : Theme
|
|
313
|
+
The replacement theme.
|
|
314
|
+
|
|
315
|
+
Returns
|
|
316
|
+
-------
|
|
317
|
+
Theme
|
|
318
|
+
"""
|
|
319
|
+
if not isinstance(e1, Theme) or not isinstance(e2, Theme):
|
|
320
|
+
cli_abort("%+replace% requires two Theme objects.")
|
|
321
|
+
result = e1.copy()
|
|
322
|
+
for key in e2.keys():
|
|
323
|
+
result[key] = e2[key]
|
|
324
|
+
return result
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
# ---------------------------------------------------------------------------
|
|
328
|
+
# complete_theme
|
|
329
|
+
# ---------------------------------------------------------------------------
|
|
330
|
+
|
|
331
|
+
def complete_theme(
|
|
332
|
+
theme_obj: Optional[Theme] = None,
|
|
333
|
+
default: Optional[Theme] = None,
|
|
334
|
+
) -> Theme:
|
|
335
|
+
"""Complete a theme so every element is fully resolved.
|
|
336
|
+
|
|
337
|
+
Parameters
|
|
338
|
+
----------
|
|
339
|
+
theme_obj : Theme or None
|
|
340
|
+
An incomplete theme to complete.
|
|
341
|
+
default : Theme or None
|
|
342
|
+
A complete theme to fill in missing elements. Falls back to the
|
|
343
|
+
current global theme.
|
|
344
|
+
|
|
345
|
+
Returns
|
|
346
|
+
-------
|
|
347
|
+
Theme
|
|
348
|
+
A fully resolved theme.
|
|
349
|
+
"""
|
|
350
|
+
if default is None:
|
|
351
|
+
default = get_theme()
|
|
352
|
+
if default is None:
|
|
353
|
+
# No global theme set yet; return what we have
|
|
354
|
+
if theme_obj is None:
|
|
355
|
+
return Theme(complete=True, validate=False)
|
|
356
|
+
result = theme_obj.copy()
|
|
357
|
+
result.complete = True
|
|
358
|
+
result.validate = False
|
|
359
|
+
return result
|
|
360
|
+
|
|
361
|
+
if theme_obj is None:
|
|
362
|
+
result = default.copy()
|
|
363
|
+
elif theme_obj.complete:
|
|
364
|
+
# For complete themes, only fill missing elements
|
|
365
|
+
result = theme_obj.copy()
|
|
366
|
+
for key in default.keys():
|
|
367
|
+
if key not in result:
|
|
368
|
+
result[key] = default[key]
|
|
369
|
+
else:
|
|
370
|
+
result = default + theme_obj
|
|
371
|
+
|
|
372
|
+
# Fill from global default as last resort
|
|
373
|
+
global_default = _ggplot_global.theme_default
|
|
374
|
+
if global_default is not None:
|
|
375
|
+
for key in global_default.keys():
|
|
376
|
+
if key not in result:
|
|
377
|
+
result[key] = global_default[key]
|
|
378
|
+
|
|
379
|
+
result.complete = True
|
|
380
|
+
result.validate = False
|
|
381
|
+
return result
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
# ---------------------------------------------------------------------------
|
|
385
|
+
# Global theme state
|
|
386
|
+
# ---------------------------------------------------------------------------
|
|
387
|
+
|
|
388
|
+
def get_theme() -> Optional[Theme]:
|
|
389
|
+
"""Return the currently active global theme.
|
|
390
|
+
|
|
391
|
+
Returns
|
|
392
|
+
-------
|
|
393
|
+
Theme or None
|
|
394
|
+
"""
|
|
395
|
+
return _ggplot_global.theme_current
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def set_theme(new: Optional[Theme] = None) -> Optional[Theme]:
|
|
399
|
+
"""Set the global theme, returning the previous one.
|
|
400
|
+
|
|
401
|
+
Parameters
|
|
402
|
+
----------
|
|
403
|
+
new : Theme or None
|
|
404
|
+
The theme to make active. If ``None``, resets to the default.
|
|
405
|
+
|
|
406
|
+
Returns
|
|
407
|
+
-------
|
|
408
|
+
Theme or None
|
|
409
|
+
The previously active theme.
|
|
410
|
+
"""
|
|
411
|
+
if new is None:
|
|
412
|
+
new = _ggplot_global.theme_default
|
|
413
|
+
if new is not None and not isinstance(new, Theme):
|
|
414
|
+
cli_abort("set_theme() requires a Theme object.")
|
|
415
|
+
old = _ggplot_global.theme_current
|
|
416
|
+
_ggplot_global.theme_current = new
|
|
417
|
+
return old
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
def update_theme(**kwargs: Any) -> Optional[Theme]:
|
|
421
|
+
"""Update the current global theme in-place.
|
|
422
|
+
|
|
423
|
+
Parameters
|
|
424
|
+
----------
|
|
425
|
+
**kwargs
|
|
426
|
+
Theme element overrides (passed to ``theme()``).
|
|
427
|
+
|
|
428
|
+
Returns
|
|
429
|
+
-------
|
|
430
|
+
Theme or None
|
|
431
|
+
The previously active theme.
|
|
432
|
+
"""
|
|
433
|
+
current = get_theme()
|
|
434
|
+
new_theme = theme(**kwargs)
|
|
435
|
+
if current is not None:
|
|
436
|
+
return set_theme(current + new_theme)
|
|
437
|
+
return set_theme(new_theme)
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def replace_theme(**kwargs: Any) -> Optional[Theme]:
|
|
441
|
+
"""Replace elements in the current global theme.
|
|
442
|
+
|
|
443
|
+
Unlike ``update_theme``, this replaces elements wholesale
|
|
444
|
+
(no element-level merging).
|
|
445
|
+
|
|
446
|
+
Parameters
|
|
447
|
+
----------
|
|
448
|
+
**kwargs
|
|
449
|
+
Theme element overrides.
|
|
450
|
+
|
|
451
|
+
Returns
|
|
452
|
+
-------
|
|
453
|
+
Theme or None
|
|
454
|
+
The previously active theme.
|
|
455
|
+
"""
|
|
456
|
+
current = get_theme()
|
|
457
|
+
new_theme = theme(**kwargs)
|
|
458
|
+
if current is not None:
|
|
459
|
+
return set_theme(theme_replace_op(current, new_theme))
|
|
460
|
+
return set_theme(new_theme)
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def reset_theme_settings(reset_current: bool = True) -> None:
|
|
464
|
+
"""Reset the global theme state to built-in defaults.
|
|
465
|
+
|
|
466
|
+
Parameters
|
|
467
|
+
----------
|
|
468
|
+
reset_current : bool
|
|
469
|
+
If ``True`` (default), also reset the currently active theme.
|
|
470
|
+
"""
|
|
471
|
+
from ggplot2_py.theme_elements import reset_theme_settings as _reset_tree
|
|
472
|
+
|
|
473
|
+
_reset_tree(reset_current=False)
|
|
474
|
+
# Lazy import to avoid circular dependency at module load time
|
|
475
|
+
try:
|
|
476
|
+
from ggplot2_py.theme_defaults import theme_grey
|
|
477
|
+
|
|
478
|
+
_ggplot_global.theme_default = theme_grey()
|
|
479
|
+
if reset_current:
|
|
480
|
+
_ggplot_global.theme_current = _ggplot_global.theme_default
|
|
481
|
+
except ImportError:
|
|
482
|
+
# theme_defaults may not be available yet during initial import
|
|
483
|
+
pass
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
# R-compatible aliases
|
|
487
|
+
theme_get = get_theme
|
|
488
|
+
theme_set = set_theme
|
|
489
|
+
theme_update = update_theme
|
|
490
|
+
theme_replace = replace_theme
|