|
"""Customizing chart configuration defaults.""" |
|
|
|
from __future__ import annotations |
|
|
|
from functools import wraps as _wraps |
|
from typing import TYPE_CHECKING, Any |
|
from typing import overload as _overload |
|
|
|
from altair.vegalite.v5.schema._config import ( |
|
AreaConfigKwds, |
|
AutoSizeParamsKwds, |
|
AxisConfigKwds, |
|
AxisResolveMapKwds, |
|
BarConfigKwds, |
|
BindCheckboxKwds, |
|
BindDirectKwds, |
|
BindInputKwds, |
|
BindRadioSelectKwds, |
|
BindRangeKwds, |
|
BoxPlotConfigKwds, |
|
BrushConfigKwds, |
|
CompositionConfigKwds, |
|
ConfigKwds, |
|
DateTimeKwds, |
|
DerivedStreamKwds, |
|
ErrorBandConfigKwds, |
|
ErrorBarConfigKwds, |
|
FeatureGeometryGeoJsonPropertiesKwds, |
|
FormatConfigKwds, |
|
GeoJsonFeatureCollectionKwds, |
|
GeoJsonFeatureKwds, |
|
GeometryCollectionKwds, |
|
GradientStopKwds, |
|
HeaderConfigKwds, |
|
IntervalSelectionConfigKwds, |
|
IntervalSelectionConfigWithoutTypeKwds, |
|
LegendConfigKwds, |
|
LegendResolveMapKwds, |
|
LegendStreamBindingKwds, |
|
LinearGradientKwds, |
|
LineConfigKwds, |
|
LineStringKwds, |
|
LocaleKwds, |
|
MarkConfigKwds, |
|
MergedStreamKwds, |
|
MultiLineStringKwds, |
|
MultiPointKwds, |
|
MultiPolygonKwds, |
|
NumberLocaleKwds, |
|
OverlayMarkDefKwds, |
|
PaddingKwds, |
|
PointKwds, |
|
PointSelectionConfigKwds, |
|
PointSelectionConfigWithoutTypeKwds, |
|
PolygonKwds, |
|
ProjectionConfigKwds, |
|
ProjectionKwds, |
|
RadialGradientKwds, |
|
RangeConfigKwds, |
|
RectConfigKwds, |
|
ResolveKwds, |
|
RowColKwds, |
|
ScaleConfigKwds, |
|
ScaleInvalidDataConfigKwds, |
|
ScaleResolveMapKwds, |
|
SelectionConfigKwds, |
|
StepKwds, |
|
StyleConfigIndexKwds, |
|
ThemeConfig, |
|
TickConfigKwds, |
|
TimeIntervalStepKwds, |
|
TimeLocaleKwds, |
|
TitleConfigKwds, |
|
TitleParamsKwds, |
|
TooltipContentKwds, |
|
TopLevelSelectionParameterKwds, |
|
VariableParameterKwds, |
|
ViewBackgroundKwds, |
|
ViewConfigKwds, |
|
) |
|
from altair.vegalite.v5.theme import themes as _themes |
|
|
|
if TYPE_CHECKING: |
|
import sys |
|
from typing import Any, Callable, Literal |
|
|
|
if sys.version_info >= (3, 11): |
|
from typing import LiteralString |
|
else: |
|
from typing_extensions import LiteralString |
|
if sys.version_info >= (3, 10): |
|
from typing import ParamSpec |
|
else: |
|
from typing_extensions import ParamSpec |
|
|
|
from altair.utils.plugin_registry import Plugin |
|
|
|
P = ParamSpec("P") |
|
|
|
__all__ = [ |
|
"AreaConfigKwds", |
|
"AutoSizeParamsKwds", |
|
"AxisConfigKwds", |
|
"AxisResolveMapKwds", |
|
"BarConfigKwds", |
|
"BindCheckboxKwds", |
|
"BindDirectKwds", |
|
"BindInputKwds", |
|
"BindRadioSelectKwds", |
|
"BindRangeKwds", |
|
"BoxPlotConfigKwds", |
|
"BrushConfigKwds", |
|
"CompositionConfigKwds", |
|
"ConfigKwds", |
|
"DateTimeKwds", |
|
"DerivedStreamKwds", |
|
"ErrorBandConfigKwds", |
|
"ErrorBarConfigKwds", |
|
"FeatureGeometryGeoJsonPropertiesKwds", |
|
"FormatConfigKwds", |
|
"GeoJsonFeatureCollectionKwds", |
|
"GeoJsonFeatureKwds", |
|
"GeometryCollectionKwds", |
|
"GradientStopKwds", |
|
"HeaderConfigKwds", |
|
"IntervalSelectionConfigKwds", |
|
"IntervalSelectionConfigWithoutTypeKwds", |
|
"LegendConfigKwds", |
|
"LegendResolveMapKwds", |
|
"LegendStreamBindingKwds", |
|
"LineConfigKwds", |
|
"LineStringKwds", |
|
"LinearGradientKwds", |
|
"LocaleKwds", |
|
"MarkConfigKwds", |
|
"MergedStreamKwds", |
|
"MultiLineStringKwds", |
|
"MultiPointKwds", |
|
"MultiPolygonKwds", |
|
"NumberLocaleKwds", |
|
"OverlayMarkDefKwds", |
|
"PaddingKwds", |
|
"PointKwds", |
|
"PointSelectionConfigKwds", |
|
"PointSelectionConfigWithoutTypeKwds", |
|
"PolygonKwds", |
|
"ProjectionConfigKwds", |
|
"ProjectionKwds", |
|
"RadialGradientKwds", |
|
"RangeConfigKwds", |
|
"RectConfigKwds", |
|
"ResolveKwds", |
|
"RowColKwds", |
|
"ScaleConfigKwds", |
|
"ScaleInvalidDataConfigKwds", |
|
"ScaleResolveMapKwds", |
|
"SelectionConfigKwds", |
|
"StepKwds", |
|
"StyleConfigIndexKwds", |
|
"ThemeConfig", |
|
"TickConfigKwds", |
|
"TimeIntervalStepKwds", |
|
"TimeLocaleKwds", |
|
"TitleConfigKwds", |
|
"TitleParamsKwds", |
|
"TooltipContentKwds", |
|
"TopLevelSelectionParameterKwds", |
|
"VariableParameterKwds", |
|
"ViewBackgroundKwds", |
|
"ViewConfigKwds", |
|
"active", |
|
"enable", |
|
"get", |
|
"names", |
|
"options", |
|
"register", |
|
"unregister", |
|
] |
|
|
|
|
|
def register( |
|
name: LiteralString, *, enable: bool |
|
) -> Callable[[Plugin[ThemeConfig]], Plugin[ThemeConfig]]: |
|
""" |
|
Decorator for registering a theme function. |
|
|
|
Parameters |
|
---------- |
|
name |
|
Unique name assigned in registry. |
|
enable |
|
Auto-enable the wrapped theme. |
|
|
|
Examples |
|
-------- |
|
Register and enable a theme:: |
|
|
|
import altair as alt |
|
from altair import theme |
|
|
|
|
|
@theme.register("param_font_size", enable=True) |
|
def custom_theme() -> theme.ThemeConfig: |
|
sizes = 12, 14, 16, 18, 20 |
|
return { |
|
"autosize": {"contains": "content", "resize": True}, |
|
"background": "#F3F2F1", |
|
"config": { |
|
"axisX": {"labelFontSize": sizes[1], "titleFontSize": sizes[1]}, |
|
"axisY": {"labelFontSize": sizes[1], "titleFontSize": sizes[1]}, |
|
"font": "'Lato', 'Segoe UI', Tahoma, Verdana, sans-serif", |
|
"headerColumn": {"labelFontSize": sizes[1]}, |
|
"headerFacet": {"labelFontSize": sizes[1]}, |
|
"headerRow": {"labelFontSize": sizes[1]}, |
|
"legend": {"labelFontSize": sizes[0], "titleFontSize": sizes[1]}, |
|
"text": {"fontSize": sizes[0]}, |
|
"title": {"fontSize": sizes[-1]}, |
|
}, |
|
"height": {"step": 28}, |
|
"width": 350, |
|
} |
|
|
|
We can then see the ``name`` parameter displayed when checking:: |
|
|
|
theme.active |
|
"param_font_size" |
|
|
|
Until another theme has been enabled, all charts will use defaults set in ``custom_theme()``:: |
|
|
|
from vega_datasets import data |
|
|
|
source = data.stocks() |
|
lines = ( |
|
alt.Chart(source, title=alt.Title("Stocks")) |
|
.mark_line() |
|
.encode(x="date:T", y="price:Q", color="symbol:N") |
|
) |
|
lines.interactive(bind_y=False) |
|
|
|
""" |
|
|
|
|
|
|
|
def decorate(func: Plugin[ThemeConfig], /) -> Plugin[ThemeConfig]: |
|
_register(name, func) |
|
if enable: |
|
_themes.enable(name) |
|
|
|
@_wraps(func) |
|
def wrapper(*args: P.args, **kwargs: P.kwargs) -> ThemeConfig: |
|
return func(*args, **kwargs) |
|
|
|
return wrapper |
|
|
|
return decorate |
|
|
|
|
|
def unregister(name: LiteralString) -> Plugin[ThemeConfig]: |
|
""" |
|
Remove and return a previously registered theme. |
|
|
|
Parameters |
|
---------- |
|
name |
|
Unique name assigned during ``alt.theme.register``. |
|
|
|
Raises |
|
------ |
|
TypeError |
|
When ``name`` has not been registered. |
|
""" |
|
plugin = _register(name, None) |
|
if plugin is None: |
|
msg = ( |
|
f"Found no theme named {name!r} in registry.\n" |
|
f"Registered themes:\n" |
|
f"{names()!r}" |
|
) |
|
raise TypeError(msg) |
|
else: |
|
return plugin |
|
|
|
|
|
enable = _themes.enable |
|
get = _themes.get |
|
names = _themes.names |
|
active: str |
|
"""Return the name of the currently active theme.""" |
|
options: dict[str, Any] |
|
"""Return the current themes options dictionary.""" |
|
|
|
|
|
def __dir__() -> list[str]: |
|
return __all__ |
|
|
|
|
|
@_overload |
|
def __getattr__(name: Literal["active"]) -> str: ... |
|
@_overload |
|
def __getattr__(name: Literal["options"]) -> dict[str, Any]: ... |
|
def __getattr__(name: str) -> Any: |
|
if name == "active": |
|
return _themes.active |
|
elif name == "options": |
|
return _themes.options |
|
else: |
|
msg = f"module {__name__!r} has no attribute {name!r}" |
|
raise AttributeError(msg) |
|
|
|
|
|
def _register( |
|
name: LiteralString, fn: Plugin[ThemeConfig] | None, / |
|
) -> Plugin[ThemeConfig] | None: |
|
if fn is None: |
|
return _themes._plugins.pop(name, None) |
|
elif _themes.plugin_type(fn): |
|
_themes._plugins[name] = fn |
|
return fn |
|
else: |
|
msg = f"{type(fn).__name__!r} is not a callable theme\n\n{fn!r}" |
|
raise TypeError(msg) |
|
|