321 lines
8.2 KiB
Python
321 lines
8.2 KiB
Python
"""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)
|
|
|
|
"""
|
|
|
|
# HACK: See for `LiteralString` requirement in `name`
|
|
# https://github.com/vega/altair/pull/3526#discussion_r1743350127
|
|
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: ... # type: ignore[misc]
|
|
@_overload
|
|
def __getattr__(name: Literal["options"]) -> dict[str, Any]: ... # type: ignore[misc]
|
|
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)
|