Module textual.renderables.underline_bar
Expand source code
from __future__ import annotations
from rich.console import ConsoleOptions, Console, RenderResult
from rich.style import StyleType
from rich.text import Text
class UnderlineBar:
"""Thin horizontal bar with a portion highlighted.
Args:
highlight_range (tuple[float, float]): The range to highlight. Defaults to ``(0, 0)`` (no highlight)
highlight_style (StyleType): The style of the highlighted range of the bar.
background_style (StyleType): The style of the non-highlighted range(s) of the bar.
width (int, optional): The width of the bar, or ``None`` to fill available width.
"""
def __init__(
self,
highlight_range: tuple[float, float] = (0, 0),
highlight_style: StyleType = "magenta",
background_style: StyleType = "grey37",
clickable_ranges: dict[str, tuple[int, int]] | None = None,
width: int | None = None,
) -> None:
self.highlight_range = highlight_range
self.highlight_style = highlight_style
self.background_style = background_style
self.clickable_ranges = clickable_ranges or {}
self.width = width
def __rich_console__(
self, console: Console, options: ConsoleOptions
) -> RenderResult:
highlight_style = console.get_style(self.highlight_style)
background_style = console.get_style(self.background_style)
half_bar_right = "╸"
half_bar_left = "╺"
bar = "━"
width = self.width or options.max_width
start, end = self.highlight_range
start = max(start, 0)
end = min(end, width)
output_bar = Text("", end="")
if start == end == 0 or end < 0 or start > end:
output_bar.append(Text(bar * width, style=background_style, end=""))
yield output_bar
return
# Round start and end to nearest half
start = round(start * 2) / 2
end = round(end * 2) / 2
# Check if we start/end on a number that rounds to a .5
half_start = start - int(start) > 0
half_end = end - int(end) > 0
# Initial non-highlighted portion of bar
output_bar.append(
Text(bar * (int(start - 0.5)), style=background_style, end="")
)
if not half_start and start > 0:
output_bar.append(Text(half_bar_right, style=background_style, end=""))
# The highlighted portion
bar_width = int(end) - int(start)
if half_start:
output_bar.append(
Text(
half_bar_left + bar * (bar_width - 1), style=highlight_style, end=""
)
)
else:
output_bar.append(Text(bar * bar_width, style=highlight_style, end=""))
if half_end:
output_bar.append(Text(half_bar_right, style=highlight_style, end=""))
# The non-highlighted tail
if not half_end and end - width != 0:
output_bar.append(Text(half_bar_left, style=background_style, end=""))
output_bar.append(
Text(bar * (int(width) - int(end) - 1), style=background_style, end="")
)
# Fire actions when certain ranges are clicked (e.g. for tabs)
for range_name, (start, end) in self.clickable_ranges.items():
output_bar.apply_meta(
{"@click": f"range_clicked('{range_name}')"}, start, end
)
yield output_bar
if __name__ == "__main__":
import random
from time import sleep
from rich.color import ANSI_COLOR_NAMES
console = Console()
def frange(start, end, step):
current = start
while current < end:
yield current
current += step
while current >= 0:
yield current
current -= step
step = 0.1
start_range = frange(0.5, 10.5, step)
end_range = frange(10, 20, step)
ranges = zip(start_range, end_range)
console.print(UnderlineBar(width=20), f" (.0, .0)")
for range in ranges:
color = random.choice(list(ANSI_COLOR_NAMES.keys()))
console.print(
UnderlineBar(range, highlight_style=color, width=20),
f" {range}",
)
from rich.live import Live
bar = UnderlineBar(highlight_range=(0, 4.5), width=80)
with Live(bar, refresh_per_second=60) as live:
while True:
bar.highlight_range = (
bar.highlight_range[0] + 0.1,
bar.highlight_range[1] + 0.1,
)
sleep(0.005)
Classes
class UnderlineBar (highlight_range: tuple[float, float] = (0, 0), highlight_style: StyleType = 'magenta', background_style: StyleType = 'grey37', clickable_ranges: dict[str, tuple[int, int]] | None = None, width: int | None = None)
-
Thin horizontal bar with a portion highlighted.
Args
highlight_range
:tuple[float, float]
- The range to highlight. Defaults to
(0, 0)
(no highlight) highlight_style
:StyleType
- The style of the highlighted range of the bar.
background_style
:StyleType
- The style of the non-highlighted range(s) of the bar.
width
:int
, optional- The width of the bar, or
None
to fill available width.
Expand source code
class UnderlineBar: """Thin horizontal bar with a portion highlighted. Args: highlight_range (tuple[float, float]): The range to highlight. Defaults to ``(0, 0)`` (no highlight) highlight_style (StyleType): The style of the highlighted range of the bar. background_style (StyleType): The style of the non-highlighted range(s) of the bar. width (int, optional): The width of the bar, or ``None`` to fill available width. """ def __init__( self, highlight_range: tuple[float, float] = (0, 0), highlight_style: StyleType = "magenta", background_style: StyleType = "grey37", clickable_ranges: dict[str, tuple[int, int]] | None = None, width: int | None = None, ) -> None: self.highlight_range = highlight_range self.highlight_style = highlight_style self.background_style = background_style self.clickable_ranges = clickable_ranges or {} self.width = width def __rich_console__( self, console: Console, options: ConsoleOptions ) -> RenderResult: highlight_style = console.get_style(self.highlight_style) background_style = console.get_style(self.background_style) half_bar_right = "╸" half_bar_left = "╺" bar = "━" width = self.width or options.max_width start, end = self.highlight_range start = max(start, 0) end = min(end, width) output_bar = Text("", end="") if start == end == 0 or end < 0 or start > end: output_bar.append(Text(bar * width, style=background_style, end="")) yield output_bar return # Round start and end to nearest half start = round(start * 2) / 2 end = round(end * 2) / 2 # Check if we start/end on a number that rounds to a .5 half_start = start - int(start) > 0 half_end = end - int(end) > 0 # Initial non-highlighted portion of bar output_bar.append( Text(bar * (int(start - 0.5)), style=background_style, end="") ) if not half_start and start > 0: output_bar.append(Text(half_bar_right, style=background_style, end="")) # The highlighted portion bar_width = int(end) - int(start) if half_start: output_bar.append( Text( half_bar_left + bar * (bar_width - 1), style=highlight_style, end="" ) ) else: output_bar.append(Text(bar * bar_width, style=highlight_style, end="")) if half_end: output_bar.append(Text(half_bar_right, style=highlight_style, end="")) # The non-highlighted tail if not half_end and end - width != 0: output_bar.append(Text(half_bar_left, style=background_style, end="")) output_bar.append( Text(bar * (int(width) - int(end) - 1), style=background_style, end="") ) # Fire actions when certain ranges are clicked (e.g. for tabs) for range_name, (start, end) in self.clickable_ranges.items(): output_bar.apply_meta( {"@click": f"range_clicked('{range_name}')"}, start, end ) yield output_bar