python (3.11.7)
       1  from typing import cast, List, Optional, TYPE_CHECKING, Union
       2  
       3  from ._spinners import SPINNERS
       4  from .measure import Measurement
       5  from .table import Table
       6  from .text import Text
       7  
       8  if TYPE_CHECKING:
       9      from .console import Console, ConsoleOptions, RenderResult, RenderableType
      10      from .style import StyleType
      11  
      12  
      13  class ESC[4;38;5;81mSpinner:
      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 text
      40          )
      41          self.frames = cast(List[str], spinner["frames"])[:]
      42          self.interval = cast(float, spinner["interval"])
      43          self.start_time: Optional[float] = None
      44          self.style = style
      45          self.speed = speed
      46          self.frame_no_offset: float = 0.0
      47          self._update_speed = 0.0
      48  
      49      def __rich_console__(
      50          self, console: "Console", options: "ConsoleOptions"
      51      ) -> "RenderResult":
      52          yield self.render(console.get_time())
      53  
      54      def __rich_measure__(
      55          self, console: "Console", options: "ConsoleOptions"
      56      ) -> Measurement:
      57          text = self.render(0)
      58          return Measurement.get(console, options, text)
      59  
      60      def render(self, time: float) -> "RenderableType":
      61          """Render the spinner for a given time.
      62  
      63          Args:
      64              time (float): Time in seconds.
      65  
      66          Returns:
      67              RenderableType: A renderable containing animation frame.
      68          """
      69          if self.start_time is None:
      70              self.start_time = time
      71  
      72          frame_no = ((time - self.start_time) * self.speed) / (
      73              self.interval / 1000.0
      74          ) + self.frame_no_offset
      75          frame = Text(
      76              self.frames[int(frame_no) % len(self.frames)], style=self.style or ""
      77          )
      78  
      79          if self._update_speed:
      80              self.frame_no_offset = frame_no
      81              self.start_time = time
      82              self.speed = self._update_speed
      83              self._update_speed = 0.0
      84  
      85          if not self.text:
      86              return frame
      87          elif isinstance(self.text, (str, Text)):
      88              return Text.assemble(frame, " ", self.text)
      89          else:
      90              table = Table.grid(padding=1)
      91              table.add_row(frame, self.text)
      92              return table
      93  
      94      def update(
      95          self,
      96          *,
      97          text: "RenderableType" = "",
      98          style: Optional["StyleType"] = None,
      99          speed: Optional[float] = None,
     100      ) -> None:
     101          """Updates attributes of a spinner after it has been started.
     102  
     103          Args:
     104              text (RenderableType, optional): A renderable to display at the right of the spinner (str or Text typically). Defaults to "".
     105              style (StyleType, optional): Style for spinner animation. Defaults to None.
     106              speed (float, optional): Speed factor for animation. Defaults to None.
     107          """
     108          if text:
     109              self.text = Text.from_markup(text) if isinstance(text, str) else text
     110          if style:
     111              self.style = style
     112          if speed:
     113              self._update_speed = speed
     114  
     115  
     116  if __name__ == "__main__":  # pragma: no cover
     117      from time import sleep
     118  
     119      from .columns import Columns
     120      from .panel import Panel
     121      from .live import Live
     122  
     123      all_spinners = Columns(
     124          [
     125              Spinner(spinner_name, text=Text(repr(spinner_name), style="green"))
     126              for spinner_name in sorted(SPINNERS.keys())
     127          ],
     128          column_first=True,
     129          expand=True,
     130      )
     131  
     132      with Live(
     133          Panel(all_spinners, title="Spinners", border_style="blue"),
     134          refresh_per_second=20,
     135      ) as live:
     136          while True:
     137              sleep(0.1)