109 lines
3.8 KiB
Python
109 lines
3.8 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Callable, ClassVar, Optional, Sequence
|
|
|
|
from textual.app import RenderResult
|
|
from textual.color import Color
|
|
from textual.reactive import reactive
|
|
from textual.renderables.sparkline import Sparkline as SparklineRenderable
|
|
from textual.widget import Widget
|
|
|
|
|
|
def _max_factory() -> Callable[[Sequence[float]], float]:
|
|
"""Callable that returns the built-in max to initialise a reactive."""
|
|
return max
|
|
|
|
|
|
class Sparkline(Widget):
|
|
"""A sparkline widget to display numerical data."""
|
|
|
|
COMPONENT_CLASSES: ClassVar[set[str]] = {
|
|
"sparkline--max-color",
|
|
"sparkline--min-color",
|
|
}
|
|
"""
|
|
Use these component classes to define the two colors that the sparkline
|
|
interpolates to represent its numerical data.
|
|
|
|
Note:
|
|
These two component classes are used exclusively for the _color_ of the
|
|
sparkline widget. Setting any style other than [`color`](/styles/color.md)
|
|
will have no effect.
|
|
|
|
| Class | Description |
|
|
| :- | :- |
|
|
| `sparkline--max-color` | The color used for the larger values in the data. |
|
|
| `sparkline--min-color` | The color used for the smaller values in the data. |
|
|
"""
|
|
|
|
DEFAULT_CSS = """
|
|
Sparkline {
|
|
height: 1;
|
|
}
|
|
Sparkline > .sparkline--max-color {
|
|
color: $primary;
|
|
}
|
|
Sparkline > .sparkline--min-color {
|
|
color: $primary 30%;
|
|
}
|
|
"""
|
|
|
|
data = reactive[Optional[Sequence[float]]](None)
|
|
"""The data that populates the sparkline."""
|
|
summary_function = reactive[Callable[[Sequence[float]], float]](_max_factory)
|
|
"""The function that computes the value that represents each bar."""
|
|
|
|
def __init__(
|
|
self,
|
|
data: Sequence[float] | None = None,
|
|
*,
|
|
min_color: Color | str | None = None,
|
|
max_color: Color | str | None = None,
|
|
summary_function: Callable[[Sequence[float]], float] | None = None,
|
|
name: str | None = None,
|
|
id: str | None = None,
|
|
classes: str | None = None,
|
|
disabled: bool = False,
|
|
) -> None:
|
|
"""Initialize a sparkline widget.
|
|
|
|
Args:
|
|
data: The initial data to populate the sparkline with.
|
|
min_color: The color of the minimum value, or `None` to take from CSS.
|
|
max_color: the color of the maximum value, or `None` to take from CSS.
|
|
summary_function: Summarizes bar values into a single value used to
|
|
represent each bar.
|
|
name: The name of the widget.
|
|
id: The ID of the widget in the DOM.
|
|
classes: The CSS classes for the widget.
|
|
disabled: Whether the widget is disabled or not.
|
|
"""
|
|
super().__init__(name=name, id=id, classes=classes, disabled=disabled)
|
|
self.min_color = None if min_color is None else Color.parse(min_color)
|
|
self.max_color = None if max_color is None else Color.parse(max_color)
|
|
self.data = data
|
|
if summary_function is not None:
|
|
self.summary_function = summary_function
|
|
|
|
def render(self) -> RenderResult:
|
|
"""Renders the sparkline when there is data available."""
|
|
data = self.data or []
|
|
_, base = self.background_colors
|
|
min_color = base + (
|
|
self.get_component_styles("sparkline--min-color").color
|
|
if self.min_color is None
|
|
else self.min_color
|
|
)
|
|
max_color = base + (
|
|
self.get_component_styles("sparkline--max-color").color
|
|
if self.max_color is None
|
|
else self.max_color
|
|
)
|
|
return SparklineRenderable(
|
|
data,
|
|
width=self.size.width,
|
|
min_color=min_color.rich_color,
|
|
max_color=max_color.rich_color,
|
|
summary_function=self.summary_function,
|
|
)
|