Aluode/PerceptionLabPortable
0
1from typing import TYPE_CHECKING, List, Optional, Union, cast2 3from ._spinners import SPINNERS4from .measure import Measurement5from .table import Table6from .text import Text7 8if TYPE_CHECKING:9 from .console import Console, ConsoleOptions, RenderableType, RenderResult10 from .style import StyleType11 12 13class Spinner:14 """A spinner animation.15 16 Args:17 name (str): Name of spinner (run python -m rich.spinner).18 text (RenderableType, optional): A renderable to display at the right of the spinner (str or Text typically). Defaults to "".19 style (StyleType, optional): Style for spinner animation. Defaults to None.20 speed (float, optional): Speed factor for animation. Defaults to 1.0.21 22 Raises:23 KeyError: If name isn't one of the supported spinner animations.24 """25 26 def __init__(27 self,28 name: str,29 text: "RenderableType" = "",30 *,31 style: Optional["StyleType"] = None,32 speed: float = 1.0,33 ) -> None:34 try:35 spinner = SPINNERS[name]36 except KeyError:37 raise KeyError(f"no spinner called {name!r}")38 self.text: "Union[RenderableType, Text]" = (39 Text.from_markup(text) if isinstance(text, str) else text40 )41 self.name = name42 self.frames = cast(List[str], spinner["frames"])[:]43 self.interval = cast(float, spinner["interval"])44 self.start_time: Optional[float] = None45 self.style = style46 self.speed = speed47 self.frame_no_offset: float = 0.048 self._update_speed = 0.049 50 def __rich_console__(51 self, console: "Console", options: "ConsoleOptions"52 ) -> "RenderResult":53 yield self.render(console.get_time())54 55 def __rich_measure__(56 self, console: "Console", options: "ConsoleOptions"57 ) -> Measurement:58 text = self.render(0)59 return Measurement.get(console, options, text)60 61 def render(self, time: float) -> "RenderableType":62 """Render the spinner for a given time.63 64 Args:65 time (float): Time in seconds.66 67 Returns:68 RenderableType: A renderable containing animation frame.69 """70 if self.start_time is None:71 self.start_time = time72 73 frame_no = ((time - self.start_time) * self.speed) / (74 self.interval / 1000.075 ) + self.frame_no_offset76 frame = Text(77 self.frames[int(frame_no) % len(self.frames)], style=self.style or ""78 )79 80 if self._update_speed:81 self.frame_no_offset = frame_no82 self.start_time = time83 self.speed = self._update_speed84 self._update_speed = 0.085 86 if not self.text:87 return frame88 elif isinstance(self.text, (str, Text)):89 return Text.assemble(frame, " ", self.text)90 else:91 table = Table.grid(padding=1)92 table.add_row(frame, self.text)93 return table94 95 def update(96 self,97 *,98 text: "RenderableType" = "",99 style: Optional["StyleType"] = None,100 speed: Optional[float] = None,101 ) -> None:102 """Updates attributes of a spinner after it has been started.103 104 Args:105 text (RenderableType, optional): A renderable to display at the right of the spinner (str or Text typically). Defaults to "".106 style (StyleType, optional): Style for spinner animation. Defaults to None.107 speed (float, optional): Speed factor for animation. Defaults to None.108 """109 if text:110 self.text = Text.from_markup(text) if isinstance(text, str) else text111 if style:112 self.style = style113 if speed:114 self._update_speed = speed115 116 117if __name__ == "__main__": # pragma: no cover118 from time import sleep119 120 from .console import Group121 from .live import Live122 123 all_spinners = Group(124 *[125 Spinner(spinner_name, text=Text(repr(spinner_name), style="green"))126 for spinner_name in sorted(SPINNERS.keys())127 ]128 )129 130 with Live(all_spinners, refresh_per_second=20) as live:131 while True:132 sleep(0.1)133 