Skip to content

widgets

sleap_nn.config_generator.tui.widgets

TUI widgets for config generator.

This module exports all custom widgets used in the config generator TUI.

Modules:

Name Description
collapsible

Collapsible section widget.

info_box

Info box widget variants.

memory_gauge

Memory estimation gauge widget.

recommendation

Recommendation panel widget.

size_display

Size display widget.

slider

Custom slider widget with numeric input.

Classes:

Name Description
Collapsible

Collapsible section with expandable content.

CollapsibleGroup

Group of collapsible sections with optional accordion behavior.

DatasetStatsPanel

Widget displaying dataset statistics.

EffectiveSizeDisplay

Compact effective size display showing key dimensions.

ErrorBox

Convenience class for error-styled info box.

GuideBox

Multi-section guide box for parameter explanations.

InfoBox

Styled information box widget.

InfoBoxType

Types of info boxes with different styling.

LabeledSlider

Slider widget with label, progress bar, and numeric input.

MemoryBreakdownCard

Detailed memory breakdown card with component-level estimates.

MemoryGauge

Widget displaying memory estimation with color-coded status.

ModelInfoDisplay

Display for model architecture information.

QuickSettingsPanel

Widget showing quick summary of current settings.

RangeSlider

Dual-handle range slider for min/max values.

RecommendationPanel

Widget displaying pipeline recommendation.

SigmaVisualization

Visual representation of confidence map sigma.

SizeDisplay

Widget displaying image size transformation pipeline.

SuccessBox

Convenience class for success-styled info box.

TipBox

Convenience class for tip-styled info box.

ToggleSection

Toggle-enabled section with switch control.

WarningBox

Convenience class for warning-styled info box.

Collapsible

Bases: Widget

Collapsible section with expandable content.

A container that can be expanded or collapsed by clicking its header. Useful for organizing complex forms into logical sections.

Attributes:

Name Type Description
expanded reactive[bool]

Whether the content is currently visible.

title reactive[bool]

The header text.

Classes:

Name Description
Toggled

Posted when the collapsible is expanded or collapsed.

Methods:

Name Description
__init__

Initialize the collapsible section.

add_content

Add widgets to the collapsible content.

collapse

Collapse the content.

compose

Compose the collapsible layout.

compose_add_child

Compose a child into this widget.

expand

Expand the content.

on_click

Handle clicks on the header to toggle.

toggle

Toggle the expanded state.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
class Collapsible(Widget):
    """Collapsible section with expandable content.

    A container that can be expanded or collapsed by clicking its header.
    Useful for organizing complex forms into logical sections.

    Attributes:
        expanded: Whether the content is currently visible.
        title: The header text.
    """

    DEFAULT_CSS = """
    Collapsible {
        height: auto;
        margin: 1 0;
    }

    Collapsible .collapsible-header {
        height: auto;
        padding: 1;
        background: $surface-lighten-1;
        border: solid $surface-lighten-2;
    }

    Collapsible .collapsible-header:hover {
        background: $surface-lighten-2;
    }

    Collapsible .collapsible-header.expanded {
        border-bottom: none;
    }

    Collapsible .header-row {
        height: auto;
        width: 100%;
    }

    Collapsible .header-title {
        width: 1fr;
        text-style: bold;
    }

    Collapsible .header-indicator {
        width: auto;
        color: $text-muted;
    }

    Collapsible .collapsible-content {
        padding: 1;
        border: solid $surface-lighten-2;
        border-top: none;
    }

    Collapsible .collapsible-content.collapsed {
        display: none;
    }
    """

    expanded: reactive[bool] = reactive(True)

    class Toggled(Message):
        """Posted when the collapsible is expanded or collapsed."""

        def __init__(self, collapsible: "Collapsible", expanded: bool) -> None:
            """Initialize with collapsible widget and state."""
            super().__init__()
            self.collapsible = collapsible
            self.expanded = expanded

        @property
        def control(self) -> "Collapsible":
            """Return the collapsible widget that sent this message."""
            return self.collapsible

    def __init__(
        self,
        title: str = "Section",
        collapsed: bool = False,
        id: Optional[str] = None,
        classes: Optional[str] = None,
    ):
        """Initialize the collapsible section.

        Args:
            title: Header text.
            collapsed: Initial collapsed state (expanded if False).
            id: Widget ID.
            classes: CSS classes.
        """
        super().__init__(id=id, classes=classes)
        self._title = title
        self.expanded = not collapsed
        self._content_widgets = []

    def compose_add_child(self, widget: Widget) -> None:
        """Compose a child into this widget."""
        self._content_widgets.append(widget)

    def compose(self) -> ComposeResult:
        """Compose the collapsible layout."""
        header_classes = (
            "collapsible-header expanded" if self.expanded else "collapsible-header"
        )
        with Container(classes=header_classes, id="header"):
            with Container(classes="header-row"):
                yield Static(self._title, classes="header-title")
                yield Static(
                    "â–¼" if self.expanded else "â–¶",
                    id="indicator",
                    classes="header-indicator",
                )

        content_classes = (
            "collapsible-content" if self.expanded else "collapsible-content collapsed"
        )
        yield Container(*self._content_widgets, classes=content_classes, id="content")

    def add_content(self, *widgets: Widget) -> None:
        """Add widgets to the collapsible content.

        Args:
            widgets: Widgets to add as content.
        """
        self._content_widgets.extend(widgets)

    async def on_click(self, event) -> None:
        """Handle clicks on the header to toggle."""
        # Check if click was on header
        try:
            header = self.query_one("#header", Container)
            # Simple check - if the widget containing the click is the header or child of header
            widget = event.widget
            while widget is not None:
                if widget is header:
                    self.toggle()
                    break
                if widget is self:
                    break
                widget = widget.parent
        except Exception:
            pass

    def toggle(self) -> None:
        """Toggle the expanded state."""
        self.expanded = not self.expanded
        self._update_display()
        self.post_message(self.Toggled(self, self.expanded))

    def expand(self) -> None:
        """Expand the content."""
        if not self.expanded:
            self.expanded = True
            self._update_display()
            self.post_message(self.Toggled(self, True))

    def collapse(self) -> None:
        """Collapse the content."""
        if self.expanded:
            self.expanded = False
            self._update_display()
            self.post_message(self.Toggled(self, False))

    def _update_display(self) -> None:
        """Update the visual display based on expanded state."""
        try:
            header = self.query_one("#header", Container)
            content = self.query_one("#content", Container)
            indicator = self.query_one("#indicator", Static)

            if self.expanded:
                header.add_class("expanded")
                content.remove_class("collapsed")
                indicator.update("â–¼")
            else:
                header.remove_class("expanded")
                content.add_class("collapsed")
                indicator.update("â–¶")
        except Exception:
            pass

Toggled

Bases: Message

Posted when the collapsible is expanded or collapsed.

Methods:

Name Description
__init__

Initialize with collapsible widget and state.

Attributes:

Name Type Description
control Collapsible

Return the collapsible widget that sent this message.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
class Toggled(Message):
    """Posted when the collapsible is expanded or collapsed."""

    def __init__(self, collapsible: "Collapsible", expanded: bool) -> None:
        """Initialize with collapsible widget and state."""
        super().__init__()
        self.collapsible = collapsible
        self.expanded = expanded

    @property
    def control(self) -> "Collapsible":
        """Return the collapsible widget that sent this message."""
        return self.collapsible
control property

Return the collapsible widget that sent this message.

__init__(collapsible, expanded)

Initialize with collapsible widget and state.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
def __init__(self, collapsible: "Collapsible", expanded: bool) -> None:
    """Initialize with collapsible widget and state."""
    super().__init__()
    self.collapsible = collapsible
    self.expanded = expanded

__init__(title='Section', collapsed=False, id=None, classes=None)

Initialize the collapsible section.

Parameters:

Name Type Description Default
title str

Header text.

'Section'
collapsed bool

Initial collapsed state (expanded if False).

False
id Optional[str]

Widget ID.

None
classes Optional[str]

CSS classes.

None
Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
def __init__(
    self,
    title: str = "Section",
    collapsed: bool = False,
    id: Optional[str] = None,
    classes: Optional[str] = None,
):
    """Initialize the collapsible section.

    Args:
        title: Header text.
        collapsed: Initial collapsed state (expanded if False).
        id: Widget ID.
        classes: CSS classes.
    """
    super().__init__(id=id, classes=classes)
    self._title = title
    self.expanded = not collapsed
    self._content_widgets = []

add_content(*widgets)

Add widgets to the collapsible content.

Parameters:

Name Type Description Default
widgets Widget

Widgets to add as content.

()
Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
def add_content(self, *widgets: Widget) -> None:
    """Add widgets to the collapsible content.

    Args:
        widgets: Widgets to add as content.
    """
    self._content_widgets.extend(widgets)

collapse()

Collapse the content.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
def collapse(self) -> None:
    """Collapse the content."""
    if self.expanded:
        self.expanded = False
        self._update_display()
        self.post_message(self.Toggled(self, False))

compose()

Compose the collapsible layout.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
def compose(self) -> ComposeResult:
    """Compose the collapsible layout."""
    header_classes = (
        "collapsible-header expanded" if self.expanded else "collapsible-header"
    )
    with Container(classes=header_classes, id="header"):
        with Container(classes="header-row"):
            yield Static(self._title, classes="header-title")
            yield Static(
                "â–¼" if self.expanded else "â–¶",
                id="indicator",
                classes="header-indicator",
            )

    content_classes = (
        "collapsible-content" if self.expanded else "collapsible-content collapsed"
    )
    yield Container(*self._content_widgets, classes=content_classes, id="content")

compose_add_child(widget)

Compose a child into this widget.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
def compose_add_child(self, widget: Widget) -> None:
    """Compose a child into this widget."""
    self._content_widgets.append(widget)

expand()

Expand the content.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
def expand(self) -> None:
    """Expand the content."""
    if not self.expanded:
        self.expanded = True
        self._update_display()
        self.post_message(self.Toggled(self, True))

on_click(event) async

Handle clicks on the header to toggle.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
async def on_click(self, event) -> None:
    """Handle clicks on the header to toggle."""
    # Check if click was on header
    try:
        header = self.query_one("#header", Container)
        # Simple check - if the widget containing the click is the header or child of header
        widget = event.widget
        while widget is not None:
            if widget is header:
                self.toggle()
                break
            if widget is self:
                break
            widget = widget.parent
    except Exception:
        pass

toggle()

Toggle the expanded state.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
def toggle(self) -> None:
    """Toggle the expanded state."""
    self.expanded = not self.expanded
    self._update_display()
    self.post_message(self.Toggled(self, self.expanded))

CollapsibleGroup

Bases: Widget

Group of collapsible sections with optional accordion behavior.

Can be configured so that only one section is expanded at a time (accordion mode) or allow multiple sections to be open.

Attributes:

Name Type Description
accordion

If True, only one section can be expanded at a time.

Methods:

Name Description
__init__

Initialize the collapsible group.

handle_section_toggle

Handle section toggle events for accordion behavior.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
class CollapsibleGroup(Widget):
    """Group of collapsible sections with optional accordion behavior.

    Can be configured so that only one section is expanded at a time
    (accordion mode) or allow multiple sections to be open.

    Attributes:
        accordion: If True, only one section can be expanded at a time.
    """

    DEFAULT_CSS = """
    CollapsibleGroup {
        height: auto;
    }

    CollapsibleGroup Collapsible {
        margin: 0 0 1 0;
    }
    """

    def __init__(
        self,
        accordion: bool = False,
        id: Optional[str] = None,
        classes: Optional[str] = None,
    ):
        """Initialize the collapsible group.

        Args:
            accordion: If True, only one section can be open at a time.
            id: Widget ID.
            classes: CSS classes.
        """
        super().__init__(id=id, classes=classes)
        self._accordion = accordion

    @on(Collapsible.Toggled)
    def handle_section_toggle(self, event: Collapsible.Toggled) -> None:
        """Handle section toggle events for accordion behavior."""
        if self._accordion and event.expanded:
            # Collapse all other sections
            for collapsible in self.query(Collapsible):
                if collapsible is not event.collapsible and collapsible.expanded:
                    collapsible.collapse()

__init__(accordion=False, id=None, classes=None)

Initialize the collapsible group.

Parameters:

Name Type Description Default
accordion bool

If True, only one section can be open at a time.

False
id Optional[str]

Widget ID.

None
classes Optional[str]

CSS classes.

None
Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
def __init__(
    self,
    accordion: bool = False,
    id: Optional[str] = None,
    classes: Optional[str] = None,
):
    """Initialize the collapsible group.

    Args:
        accordion: If True, only one section can be open at a time.
        id: Widget ID.
        classes: CSS classes.
    """
    super().__init__(id=id, classes=classes)
    self._accordion = accordion

handle_section_toggle(event)

Handle section toggle events for accordion behavior.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
@on(Collapsible.Toggled)
def handle_section_toggle(self, event: Collapsible.Toggled) -> None:
    """Handle section toggle events for accordion behavior."""
    if self._accordion and event.expanded:
        # Collapse all other sections
        for collapsible in self.query(Collapsible):
            if collapsible is not event.collapsible and collapsible.expanded:
                collapsible.collapse()

DatasetStatsPanel

Bases: Static

Widget displaying dataset statistics.

Shows key statistics about the loaded SLP file including frame count, image dimensions, skeleton info, and instance counts.

Methods:

Name Description
__init__

Initialize with optional stats.

render

Render the stats panel.

update_stats

Update the displayed statistics.

Source code in sleap_nn/config_generator/tui/widgets/recommendation.py
class DatasetStatsPanel(Static):
    """Widget displaying dataset statistics.

    Shows key statistics about the loaded SLP file including
    frame count, image dimensions, skeleton info, and instance counts.
    """

    DEFAULT_CSS = """
    DatasetStatsPanel {
        height: auto;
        padding: 1;
        border: solid $primary;
        margin-bottom: 1;
    }

    DatasetStatsPanel .stats-title {
        text-style: bold;
    }

    DatasetStatsPanel .stats-value {
        color: $primary;
        text-style: bold;
    }

    DatasetStatsPanel .stats-label {
        color: $text-muted;
    }
    """

    def __init__(self, stats=None, **kwargs):
        """Initialize with optional stats.

        Args:
            stats: DatasetStats object to display.
            **kwargs: Additional arguments passed to parent.
        """
        super().__init__(**kwargs)
        self._stats = stats

    def update_stats(self, stats) -> None:
        """Update the displayed statistics.

        Args:
            stats: New DatasetStats object.
        """
        self._stats = stats
        self.refresh()

    def render(self) -> str:
        """Render the stats panel."""
        if self._stats is None:
            return "Dataset Statistics\n" "──────────────────\n" "Loading..."

        stats = self._stats

        # Format channels
        channels = "Grayscale" if stats.is_grayscale else "RGB"

        # Format instances
        if stats.is_single_instance:
            instances = "Single instance"
        else:
            instances = f"Multi-instance (max {stats.max_instances_per_frame})"

        lines = [
            "Dataset Statistics",
            "──────────────────",
            "",
            f"  Frames:     {stats.num_labeled_frames}",
            f"  Videos:     {stats.num_videos}",
            f"  Size:       {stats.max_width} × {stats.max_height}",
            f"  Channels:   {channels}",
            "",
            f"  Skeleton:   {stats.num_nodes} nodes, {stats.num_edges} edges",
            f"  Instances:  {instances}",
            f"  Avg bbox:   {stats.avg_bbox_size:.0f}px",
        ]

        if stats.has_tracks:
            lines.append(f"  Tracks:     {stats.num_tracks}")

        return "\n".join(lines)

__init__(stats=None, **kwargs)

Initialize with optional stats.

Parameters:

Name Type Description Default
stats

DatasetStats object to display.

None
**kwargs

Additional arguments passed to parent.

{}
Source code in sleap_nn/config_generator/tui/widgets/recommendation.py
def __init__(self, stats=None, **kwargs):
    """Initialize with optional stats.

    Args:
        stats: DatasetStats object to display.
        **kwargs: Additional arguments passed to parent.
    """
    super().__init__(**kwargs)
    self._stats = stats

render()

Render the stats panel.

Source code in sleap_nn/config_generator/tui/widgets/recommendation.py
def render(self) -> str:
    """Render the stats panel."""
    if self._stats is None:
        return "Dataset Statistics\n" "──────────────────\n" "Loading..."

    stats = self._stats

    # Format channels
    channels = "Grayscale" if stats.is_grayscale else "RGB"

    # Format instances
    if stats.is_single_instance:
        instances = "Single instance"
    else:
        instances = f"Multi-instance (max {stats.max_instances_per_frame})"

    lines = [
        "Dataset Statistics",
        "──────────────────",
        "",
        f"  Frames:     {stats.num_labeled_frames}",
        f"  Videos:     {stats.num_videos}",
        f"  Size:       {stats.max_width} × {stats.max_height}",
        f"  Channels:   {channels}",
        "",
        f"  Skeleton:   {stats.num_nodes} nodes, {stats.num_edges} edges",
        f"  Instances:  {instances}",
        f"  Avg bbox:   {stats.avg_bbox_size:.0f}px",
    ]

    if stats.has_tracks:
        lines.append(f"  Tracks:     {stats.num_tracks}")

    return "\n".join(lines)

update_stats(stats)

Update the displayed statistics.

Parameters:

Name Type Description Default
stats

New DatasetStats object.

required
Source code in sleap_nn/config_generator/tui/widgets/recommendation.py
def update_stats(self, stats) -> None:
    """Update the displayed statistics.

    Args:
        stats: New DatasetStats object.
    """
    self._stats = stats
    self.refresh()

EffectiveSizeDisplay

Bases: Static

Compact effective size display showing key dimensions.

Shows a one-line summary of input and output sizes.

Methods:

Name Description
__init__

Initialize the effective size display.

render

Render the effective size display.

update

Update size values.

Source code in sleap_nn/config_generator/tui/widgets/size_display.py
class EffectiveSizeDisplay(Static):
    """Compact effective size display showing key dimensions.

    Shows a one-line summary of input and output sizes.
    """

    DEFAULT_CSS = """
    EffectiveSizeDisplay {
        height: auto;
        padding: 0;
    }

    EffectiveSizeDisplay .effective-label {
        color: $text-muted;
    }

    EffectiveSizeDisplay .effective-value {
        color: $primary;
        text-style: bold;
    }
    """

    def __init__(
        self,
        input_size: Tuple[int, int] = (0, 0),
        output_size: Tuple[int, int] = (0, 0),
        id: Optional[str] = None,
        classes: Optional[str] = None,
    ):
        """Initialize the effective size display.

        Args:
            input_size: Model input dimensions (width, height).
            output_size: Model output dimensions (width, height).
            id: Widget ID.
            classes: CSS classes.
        """
        super().__init__(id=id, classes=classes)
        self._input_size = input_size
        self._output_size = output_size

    def update(
        self,
        input_size: Optional[Tuple[int, int]] = None,
        output_size: Optional[Tuple[int, int]] = None,
    ) -> None:
        """Update size values.

        Args:
            input_size: New input dimensions.
            output_size: New output dimensions.
        """
        if input_size is not None:
            self._input_size = input_size
        if output_size is not None:
            self._output_size = output_size
        self.refresh()

    def render(self) -> str:
        """Render the effective size display."""
        in_w, in_h = self._input_size
        out_w, out_h = self._output_size
        return f"Input: {in_w}×{in_h}  →  Output: {out_w}×{out_h}"

__init__(input_size=(0, 0), output_size=(0, 0), id=None, classes=None)

Initialize the effective size display.

Parameters:

Name Type Description Default
input_size Tuple[int, int]

Model input dimensions (width, height).

(0, 0)
output_size Tuple[int, int]

Model output dimensions (width, height).

(0, 0)
id Optional[str]

Widget ID.

None
classes Optional[str]

CSS classes.

None
Source code in sleap_nn/config_generator/tui/widgets/size_display.py
def __init__(
    self,
    input_size: Tuple[int, int] = (0, 0),
    output_size: Tuple[int, int] = (0, 0),
    id: Optional[str] = None,
    classes: Optional[str] = None,
):
    """Initialize the effective size display.

    Args:
        input_size: Model input dimensions (width, height).
        output_size: Model output dimensions (width, height).
        id: Widget ID.
        classes: CSS classes.
    """
    super().__init__(id=id, classes=classes)
    self._input_size = input_size
    self._output_size = output_size

render()

Render the effective size display.

Source code in sleap_nn/config_generator/tui/widgets/size_display.py
def render(self) -> str:
    """Render the effective size display."""
    in_w, in_h = self._input_size
    out_w, out_h = self._output_size
    return f"Input: {in_w}×{in_h}  →  Output: {out_w}×{out_h}"

update(input_size=None, output_size=None)

Update size values.

Parameters:

Name Type Description Default
input_size Optional[Tuple[int, int]]

New input dimensions.

None
output_size Optional[Tuple[int, int]]

New output dimensions.

None
Source code in sleap_nn/config_generator/tui/widgets/size_display.py
def update(
    self,
    input_size: Optional[Tuple[int, int]] = None,
    output_size: Optional[Tuple[int, int]] = None,
) -> None:
    """Update size values.

    Args:
        input_size: New input dimensions.
        output_size: New output dimensions.
    """
    if input_size is not None:
        self._input_size = input_size
    if output_size is not None:
        self._output_size = output_size
    self.refresh()

ErrorBox

Bases: InfoBox

Convenience class for error-styled info box.

Methods:

Name Description
__init__

Initialize an error-styled info box.

Source code in sleap_nn/config_generator/tui/widgets/info_box.py
class ErrorBox(InfoBox):
    """Convenience class for error-styled info box."""

    def __init__(
        self,
        message: str,
        title: Optional[str] = "Error",
        id: Optional[str] = None,
        classes: Optional[str] = None,
    ):
        """Initialize an error-styled info box."""
        super().__init__(
            message=message,
            box_type=InfoBoxType.ERROR,
            title=title,
            id=id,
            classes=classes,
        )

__init__(message, title='Error', id=None, classes=None)

Initialize an error-styled info box.

Source code in sleap_nn/config_generator/tui/widgets/info_box.py
def __init__(
    self,
    message: str,
    title: Optional[str] = "Error",
    id: Optional[str] = None,
    classes: Optional[str] = None,
):
    """Initialize an error-styled info box."""
    super().__init__(
        message=message,
        box_type=InfoBoxType.ERROR,
        title=title,
        id=id,
        classes=classes,
    )

GuideBox

Bases: Static

Multi-section guide box for parameter explanations.

Displays structured guidance with multiple sections for explaining configuration parameters.

Methods:

Name Description
__init__

Initialize the guide box.

add_section

Add a section to the guide.

render

Render the guide box content.

update_sections

Replace all sections.

Source code in sleap_nn/config_generator/tui/widgets/info_box.py
class GuideBox(Static):
    """Multi-section guide box for parameter explanations.

    Displays structured guidance with multiple sections for
    explaining configuration parameters.
    """

    DEFAULT_CSS = """
    GuideBox {
        height: auto;
        padding: 1;
        margin: 1 0;
        border: solid $surface-lighten-2;
        background: $surface;
    }
    """

    def __init__(
        self,
        title: str = "Guide",
        sections: Optional[dict] = None,
        id: Optional[str] = None,
        classes: Optional[str] = None,
    ):
        """Initialize the guide box.

        Args:
            title: Main title.
            sections: Dict mapping section titles to content.
            id: Widget ID.
            classes: CSS classes.
        """
        super().__init__(id=id, classes=classes)
        self._title = title
        self._sections = sections or {}

    def render(self) -> str:
        """Render the guide box content."""
        lines = [self._title, "─" * len(self._title)]

        for section_title, content in self._sections.items():
            lines.append("")
            lines.append(f"â–¸ {section_title}")
            # Indent content
            for line in content.split("\n"):
                lines.append(f"  {line}")

        return "\n".join(lines)

    def add_section(self, title: str, content: str) -> None:
        """Add a section to the guide.

        Args:
            title: Section title.
            content: Section content.
        """
        self._sections[title] = content
        self.refresh()

    def update_sections(self, sections: dict) -> None:
        """Replace all sections.

        Args:
            sections: Dict mapping section titles to content.
        """
        self._sections = sections
        self.refresh()

__init__(title='Guide', sections=None, id=None, classes=None)

Initialize the guide box.

Parameters:

Name Type Description Default
title str

Main title.

'Guide'
sections Optional[dict]

Dict mapping section titles to content.

None
id Optional[str]

Widget ID.

None
classes Optional[str]

CSS classes.

None
Source code in sleap_nn/config_generator/tui/widgets/info_box.py
def __init__(
    self,
    title: str = "Guide",
    sections: Optional[dict] = None,
    id: Optional[str] = None,
    classes: Optional[str] = None,
):
    """Initialize the guide box.

    Args:
        title: Main title.
        sections: Dict mapping section titles to content.
        id: Widget ID.
        classes: CSS classes.
    """
    super().__init__(id=id, classes=classes)
    self._title = title
    self._sections = sections or {}

add_section(title, content)

Add a section to the guide.

Parameters:

Name Type Description Default
title str

Section title.

required
content str

Section content.

required
Source code in sleap_nn/config_generator/tui/widgets/info_box.py
def add_section(self, title: str, content: str) -> None:
    """Add a section to the guide.

    Args:
        title: Section title.
        content: Section content.
    """
    self._sections[title] = content
    self.refresh()

render()

Render the guide box content.

Source code in sleap_nn/config_generator/tui/widgets/info_box.py
def render(self) -> str:
    """Render the guide box content."""
    lines = [self._title, "─" * len(self._title)]

    for section_title, content in self._sections.items():
        lines.append("")
        lines.append(f"â–¸ {section_title}")
        # Indent content
        for line in content.split("\n"):
            lines.append(f"  {line}")

    return "\n".join(lines)

update_sections(sections)

Replace all sections.

Parameters:

Name Type Description Default
sections dict

Dict mapping section titles to content.

required
Source code in sleap_nn/config_generator/tui/widgets/info_box.py
def update_sections(self, sections: dict) -> None:
    """Replace all sections.

    Args:
        sections: Dict mapping section titles to content.
    """
    self._sections = sections
    self.refresh()

InfoBox

Bases: Static

Styled information box widget.

Displays informational content with visual styling to indicate the type of information (info, warning, success, error, tip).

Attributes:

Name Type Description
box_type

The type of info box (affects styling).

title

Optional title text.

message str

Main content text.

Methods:

Name Description
__init__

Initialize the info box.

render

Render the info box content.

update_message

Update the message content.

update_type

Update the box type.

Source code in sleap_nn/config_generator/tui/widgets/info_box.py
class InfoBox(Static):
    """Styled information box widget.

    Displays informational content with visual styling to indicate
    the type of information (info, warning, success, error, tip).

    Attributes:
        box_type: The type of info box (affects styling).
        title: Optional title text.
        message: Main content text.
    """

    DEFAULT_CSS = """
    InfoBox {
        height: auto;
        padding: 1;
        margin: 1 0;
        border-left: thick $accent;
        background: $surface;
    }

    InfoBox.info {
        border-left: thick #3b82f6;
    }

    InfoBox.warning {
        border-left: thick $warning;
    }

    InfoBox.success {
        border-left: thick $success;
    }

    InfoBox.error {
        border-left: thick $error;
    }

    InfoBox.tip {
        border-left: thick #a855f7;
    }
    """

    def __init__(
        self,
        message: str,
        box_type: InfoBoxType = InfoBoxType.INFO,
        title: Optional[str] = None,
        id: Optional[str] = None,
        classes: Optional[str] = None,
    ):
        """Initialize the info box.

        Args:
            message: Content text.
            box_type: Type of info box.
            title: Optional title.
            id: Widget ID.
            classes: CSS classes.
        """
        super().__init__(id=id, classes=classes)
        self._message = message
        self._box_type = box_type
        self._title = title

        # Add type class
        self.add_class(box_type.value)

    @property
    def message(self) -> str:
        """Get the message content."""
        return self._message

    @message.setter
    def message(self, value: str) -> None:
        """Set the message content."""
        self._message = value
        self.refresh()

    def render(self) -> str:
        """Render the info box content."""
        lines = []

        # Icons for different types
        icons = {
            InfoBoxType.INFO: "ℹ",
            InfoBoxType.WARNING: "âš ",
            InfoBoxType.SUCCESS: "✓",
            InfoBoxType.ERROR: "✗",
            InfoBoxType.TIP: "💡",
        }
        icon = icons.get(self._box_type, "")

        if self._title:
            lines.append(f"{icon} {self._title}")
            lines.append("")

        lines.append(self._message)

        return "\n".join(lines)

    def update_message(self, message: str) -> None:
        """Update the message content.

        Args:
            message: New message text.
        """
        self._message = message
        self.refresh()

    def update_type(self, box_type: InfoBoxType) -> None:
        """Update the box type.

        Args:
            box_type: New box type.
        """
        # Remove old type class
        self.remove_class(self._box_type.value)
        # Add new type class
        self._box_type = box_type
        self.add_class(box_type.value)
        self.refresh()

message property writable

Get the message content.

__init__(message, box_type=InfoBoxType.INFO, title=None, id=None, classes=None)

Initialize the info box.

Parameters:

Name Type Description Default
message str

Content text.

required
box_type InfoBoxType

Type of info box.

INFO
title Optional[str]

Optional title.

None
id Optional[str]

Widget ID.

None
classes Optional[str]

CSS classes.

None
Source code in sleap_nn/config_generator/tui/widgets/info_box.py
def __init__(
    self,
    message: str,
    box_type: InfoBoxType = InfoBoxType.INFO,
    title: Optional[str] = None,
    id: Optional[str] = None,
    classes: Optional[str] = None,
):
    """Initialize the info box.

    Args:
        message: Content text.
        box_type: Type of info box.
        title: Optional title.
        id: Widget ID.
        classes: CSS classes.
    """
    super().__init__(id=id, classes=classes)
    self._message = message
    self._box_type = box_type
    self._title = title

    # Add type class
    self.add_class(box_type.value)

render()

Render the info box content.

Source code in sleap_nn/config_generator/tui/widgets/info_box.py
def render(self) -> str:
    """Render the info box content."""
    lines = []

    # Icons for different types
    icons = {
        InfoBoxType.INFO: "ℹ",
        InfoBoxType.WARNING: "âš ",
        InfoBoxType.SUCCESS: "✓",
        InfoBoxType.ERROR: "✗",
        InfoBoxType.TIP: "💡",
    }
    icon = icons.get(self._box_type, "")

    if self._title:
        lines.append(f"{icon} {self._title}")
        lines.append("")

    lines.append(self._message)

    return "\n".join(lines)

update_message(message)

Update the message content.

Parameters:

Name Type Description Default
message str

New message text.

required
Source code in sleap_nn/config_generator/tui/widgets/info_box.py
def update_message(self, message: str) -> None:
    """Update the message content.

    Args:
        message: New message text.
    """
    self._message = message
    self.refresh()

update_type(box_type)

Update the box type.

Parameters:

Name Type Description Default
box_type InfoBoxType

New box type.

required
Source code in sleap_nn/config_generator/tui/widgets/info_box.py
def update_type(self, box_type: InfoBoxType) -> None:
    """Update the box type.

    Args:
        box_type: New box type.
    """
    # Remove old type class
    self.remove_class(self._box_type.value)
    # Add new type class
    self._box_type = box_type
    self.add_class(box_type.value)
    self.refresh()

InfoBoxType

Bases: str, Enum

Types of info boxes with different styling.

Source code in sleap_nn/config_generator/tui/widgets/info_box.py
class InfoBoxType(str, Enum):
    """Types of info boxes with different styling."""

    INFO = "info"
    WARNING = "warning"
    SUCCESS = "success"
    ERROR = "error"
    TIP = "tip"

LabeledSlider

Bases: Widget

Slider widget with label, progress bar, and numeric input.

Combines a visual progress bar with a numeric input field for both visual feedback and precise value entry.

Attributes:

Name Type Description
value reactive[float]

Current slider value.

min_value reactive[float]

Minimum allowed value.

max_value reactive[float]

Maximum allowed value.

step reactive[float]

Step increment for value changes.

label reactive[float]

Display label for the slider.

Classes:

Name Description
Changed

Posted when the slider value changes.

Methods:

Name Description
__init__

Initialize the labeled slider.

compose

Compose the slider layout.

decrease

Decrease value by one step.

handle_input_change

Handle direct input changes.

increase

Increase value by one step.

on_mount

Initialize progress bar on mount.

set_value

Programmatically set the slider value.

watch_value

React to value changes.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
class LabeledSlider(Widget):
    """Slider widget with label, progress bar, and numeric input.

    Combines a visual progress bar with a numeric input field
    for both visual feedback and precise value entry.

    Attributes:
        value: Current slider value.
        min_value: Minimum allowed value.
        max_value: Maximum allowed value.
        step: Step increment for value changes.
        label: Display label for the slider.
    """

    DEFAULT_CSS = """
    LabeledSlider {
        height: auto;
        padding: 0;
        margin: 1 0;
    }

    LabeledSlider .slider-header {
        height: auto;
        width: 100%;
    }

    LabeledSlider .slider-label {
        width: 1fr;
    }

    LabeledSlider .slider-value-display {
        width: auto;
        color: $primary;
        text-style: bold;
        text-align: right;
        min-width: 8;
    }

    LabeledSlider .slider-controls {
        height: auto;
        width: 100%;
        margin-top: 1;
    }

    LabeledSlider ProgressBar {
        width: 1fr;
        margin-right: 1;
    }

    LabeledSlider Input {
        width: 10;
    }

    LabeledSlider .slider-range {
        height: auto;
        width: 100%;
        margin-top: 0;
    }

    LabeledSlider .range-min {
        width: 1fr;
        color: $text-muted;
    }

    LabeledSlider .range-max {
        width: auto;
        color: $text-muted;
        text-align: right;
    }
    """

    value: reactive[float] = reactive(0.0)
    min_value: reactive[float] = reactive(0.0)
    max_value: reactive[float] = reactive(100.0)
    step: reactive[float] = reactive(1.0)

    class Changed(Message):
        """Posted when the slider value changes."""

        def __init__(self, slider: "LabeledSlider", value: float) -> None:
            """Initialize with slider widget and new value."""
            super().__init__()
            self.slider = slider
            self.value = value

        @property
        def control(self) -> "LabeledSlider":
            """Return the slider widget that sent this message."""
            return self.slider

    def __init__(
        self,
        label: str = "Value",
        value: float = 0.0,
        min_value: float = 0.0,
        max_value: float = 100.0,
        step: float = 1.0,
        format_str: str = "{:.1f}",
        unit: str = "",
        show_range: bool = True,
        id: Optional[str] = None,
        classes: Optional[str] = None,
    ):
        """Initialize the labeled slider.

        Args:
            label: Display label.
            value: Initial value.
            min_value: Minimum allowed value.
            max_value: Maximum allowed value.
            step: Step increment.
            format_str: Format string for value display.
            unit: Unit suffix for display (e.g., "px", "%").
            show_range: Whether to show min/max range labels.
            id: Widget ID.
            classes: CSS classes.
        """
        super().__init__(id=id, classes=classes)
        self._label = label
        self._format_str = format_str
        self._unit = unit
        self._show_range = show_range

        # Set initial values
        self.min_value = min_value
        self.max_value = max_value
        self.step = step
        self.value = max(min_value, min(max_value, value))

    def compose(self) -> ComposeResult:
        """Compose the slider layout."""
        with Horizontal(classes="slider-header"):
            yield Static(self._label, classes="slider-label")
            yield Static(
                self._format_value(self.value),
                id="value-display",
                classes="slider-value-display",
            )

        with Horizontal(classes="slider-controls"):
            yield ProgressBar(
                total=100,
                show_eta=False,
                show_percentage=False,
                id="progress",
            )
            yield Input(
                value=str(self.value),
                type="number",
                id="input",
            )

        if self._show_range:
            with Horizontal(classes="slider-range"):
                yield Static(
                    self._format_str.format(self.min_value),
                    classes="range-min",
                )
                yield Static(
                    self._format_str.format(self.max_value),
                    classes="range-max",
                )

    def on_mount(self) -> None:
        """Initialize progress bar on mount."""
        self._update_progress()

    def _format_value(self, value: float) -> str:
        """Format value for display."""
        formatted = self._format_str.format(value)
        if self._unit:
            formatted = f"{formatted}{self._unit}"
        return formatted

    def _update_progress(self) -> None:
        """Update the progress bar to match current value."""
        try:
            progress = self.query_one("#progress", ProgressBar)
            if self.max_value > self.min_value:
                percent = (
                    (self.value - self.min_value)
                    / (self.max_value - self.min_value)
                    * 100
                )
                progress.update(progress=percent)
        except Exception:
            pass

    def _update_display(self) -> None:
        """Update the value display."""
        try:
            display = self.query_one("#value-display", Static)
            display.update(self._format_value(self.value))
        except Exception:
            pass

    def watch_value(self, value: float) -> None:
        """React to value changes."""
        self._update_progress()
        self._update_display()

    @on(Input.Changed, "#input")
    def handle_input_change(self, event: Input.Changed) -> None:
        """Handle direct input changes."""
        try:
            new_value = float(event.value)
            # Clamp to range
            new_value = max(self.min_value, min(self.max_value, new_value))
            # Snap to step
            if self.step > 0:
                new_value = round(new_value / self.step) * self.step

            if new_value != self.value:
                self.value = new_value
                self.post_message(self.Changed(self, new_value))
        except ValueError:
            pass

    def increase(self) -> None:
        """Increase value by one step."""
        new_value = min(self.max_value, self.value + self.step)
        if new_value != self.value:
            self.value = new_value
            self._update_input()
            self.post_message(self.Changed(self, new_value))

    def decrease(self) -> None:
        """Decrease value by one step."""
        new_value = max(self.min_value, self.value - self.step)
        if new_value != self.value:
            self.value = new_value
            self._update_input()
            self.post_message(self.Changed(self, new_value))

    def _update_input(self) -> None:
        """Update the input field to match current value."""
        try:
            input_widget = self.query_one("#input", Input)
            input_widget.value = str(self.value)
        except Exception:
            pass

    def set_value(self, value: float) -> None:
        """Programmatically set the slider value.

        Args:
            value: New value to set.
        """
        clamped = max(self.min_value, min(self.max_value, value))
        if clamped != self.value:
            self.value = clamped
            self._update_input()

Changed

Bases: Message

Posted when the slider value changes.

Methods:

Name Description
__init__

Initialize with slider widget and new value.

Attributes:

Name Type Description
control LabeledSlider

Return the slider widget that sent this message.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
class Changed(Message):
    """Posted when the slider value changes."""

    def __init__(self, slider: "LabeledSlider", value: float) -> None:
        """Initialize with slider widget and new value."""
        super().__init__()
        self.slider = slider
        self.value = value

    @property
    def control(self) -> "LabeledSlider":
        """Return the slider widget that sent this message."""
        return self.slider
control property

Return the slider widget that sent this message.

__init__(slider, value)

Initialize with slider widget and new value.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
def __init__(self, slider: "LabeledSlider", value: float) -> None:
    """Initialize with slider widget and new value."""
    super().__init__()
    self.slider = slider
    self.value = value

__init__(label='Value', value=0.0, min_value=0.0, max_value=100.0, step=1.0, format_str='{:.1f}', unit='', show_range=True, id=None, classes=None)

Initialize the labeled slider.

Parameters:

Name Type Description Default
label str

Display label.

'Value'
value float

Initial value.

0.0
min_value float

Minimum allowed value.

0.0
max_value float

Maximum allowed value.

100.0
step float

Step increment.

1.0
format_str str

Format string for value display.

'{:.1f}'
unit str

Unit suffix for display (e.g., "px", "%").

''
show_range bool

Whether to show min/max range labels.

True
id Optional[str]

Widget ID.

None
classes Optional[str]

CSS classes.

None
Source code in sleap_nn/config_generator/tui/widgets/slider.py
def __init__(
    self,
    label: str = "Value",
    value: float = 0.0,
    min_value: float = 0.0,
    max_value: float = 100.0,
    step: float = 1.0,
    format_str: str = "{:.1f}",
    unit: str = "",
    show_range: bool = True,
    id: Optional[str] = None,
    classes: Optional[str] = None,
):
    """Initialize the labeled slider.

    Args:
        label: Display label.
        value: Initial value.
        min_value: Minimum allowed value.
        max_value: Maximum allowed value.
        step: Step increment.
        format_str: Format string for value display.
        unit: Unit suffix for display (e.g., "px", "%").
        show_range: Whether to show min/max range labels.
        id: Widget ID.
        classes: CSS classes.
    """
    super().__init__(id=id, classes=classes)
    self._label = label
    self._format_str = format_str
    self._unit = unit
    self._show_range = show_range

    # Set initial values
    self.min_value = min_value
    self.max_value = max_value
    self.step = step
    self.value = max(min_value, min(max_value, value))

compose()

Compose the slider layout.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
def compose(self) -> ComposeResult:
    """Compose the slider layout."""
    with Horizontal(classes="slider-header"):
        yield Static(self._label, classes="slider-label")
        yield Static(
            self._format_value(self.value),
            id="value-display",
            classes="slider-value-display",
        )

    with Horizontal(classes="slider-controls"):
        yield ProgressBar(
            total=100,
            show_eta=False,
            show_percentage=False,
            id="progress",
        )
        yield Input(
            value=str(self.value),
            type="number",
            id="input",
        )

    if self._show_range:
        with Horizontal(classes="slider-range"):
            yield Static(
                self._format_str.format(self.min_value),
                classes="range-min",
            )
            yield Static(
                self._format_str.format(self.max_value),
                classes="range-max",
            )

decrease()

Decrease value by one step.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
def decrease(self) -> None:
    """Decrease value by one step."""
    new_value = max(self.min_value, self.value - self.step)
    if new_value != self.value:
        self.value = new_value
        self._update_input()
        self.post_message(self.Changed(self, new_value))

handle_input_change(event)

Handle direct input changes.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
@on(Input.Changed, "#input")
def handle_input_change(self, event: Input.Changed) -> None:
    """Handle direct input changes."""
    try:
        new_value = float(event.value)
        # Clamp to range
        new_value = max(self.min_value, min(self.max_value, new_value))
        # Snap to step
        if self.step > 0:
            new_value = round(new_value / self.step) * self.step

        if new_value != self.value:
            self.value = new_value
            self.post_message(self.Changed(self, new_value))
    except ValueError:
        pass

increase()

Increase value by one step.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
def increase(self) -> None:
    """Increase value by one step."""
    new_value = min(self.max_value, self.value + self.step)
    if new_value != self.value:
        self.value = new_value
        self._update_input()
        self.post_message(self.Changed(self, new_value))

on_mount()

Initialize progress bar on mount.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
def on_mount(self) -> None:
    """Initialize progress bar on mount."""
    self._update_progress()

set_value(value)

Programmatically set the slider value.

Parameters:

Name Type Description Default
value float

New value to set.

required
Source code in sleap_nn/config_generator/tui/widgets/slider.py
def set_value(self, value: float) -> None:
    """Programmatically set the slider value.

    Args:
        value: New value to set.
    """
    clamped = max(self.min_value, min(self.max_value, value))
    if clamped != self.value:
        self.value = clamped
        self._update_input()

watch_value(value)

React to value changes.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
def watch_value(self, value: float) -> None:
    """React to value changes."""
    self._update_progress()
    self._update_display()

MemoryBreakdownCard

Bases: Static

Detailed memory breakdown card with component-level estimates.

Shows individual memory consumption for: - Model parameters/weights - Batch images - Feature maps/activations - Confidence maps/PAFs - Gradients

Methods:

Name Description
__init__

Initialize the breakdown card.

render

Render the breakdown card.

update_estimate

Update the displayed estimate.

Source code in sleap_nn/config_generator/tui/widgets/memory_gauge.py
class MemoryBreakdownCard(Static):
    """Detailed memory breakdown card with component-level estimates.

    Shows individual memory consumption for:
    - Model parameters/weights
    - Batch images
    - Feature maps/activations
    - Confidence maps/PAFs
    - Gradients
    """

    DEFAULT_CSS = """
    MemoryBreakdownCard {
        height: auto;
        padding: 1;
        border: solid $surface-lighten-2;
        margin: 1 0;
    }

    MemoryBreakdownCard .card-title {
        text-style: bold;
        margin-bottom: 1;
    }

    MemoryBreakdownCard .memory-row {
        height: 1;
    }

    MemoryBreakdownCard .memory-label {
        width: 1fr;
        color: $text-muted;
    }

    MemoryBreakdownCard .memory-value {
        width: auto;
        text-align: right;
    }
    """

    def __init__(
        self,
        estimate: Optional[MemoryEstimate] = None,
        model_type: str = "Single Instance",
        **kwargs,
    ):
        """Initialize the breakdown card.

        Args:
            estimate: Memory estimate to display.
            model_type: Name of model type for header display.
            **kwargs: Additional arguments passed to parent.
        """
        super().__init__(**kwargs)
        self._estimate = estimate
        self._model_type = model_type

    def update_estimate(
        self, estimate: MemoryEstimate, model_type: Optional[str] = None
    ) -> None:
        """Update the displayed estimate.

        Args:
            estimate: New memory estimate.
            model_type: Optional new model type name.
        """
        self._estimate = estimate
        if model_type:
            self._model_type = model_type
        self.refresh()

    def render(self) -> str:
        """Render the breakdown card."""
        if self._estimate is None:
            return "Memory Breakdown\n" "Calculating..."

        est = self._estimate
        lines = [
            f"GPU Memory ({self._model_type})",
            "─" * 30,
        ]

        # Format values
        params_mb = est.model_memory_gb * 1024
        batch_mb = (est.batch_size * est.image_bytes) / (1024 * 1024)
        activations_mb = est.activations_memory_gb * 1024
        gradients_mb = est.gradients_memory_gb * 1024

        rows = [
            ("Model Params", f"~{self._format_count(params_mb / 4 * 1e6)}"),
            ("Weights", f"~{params_mb:.0f} MB"),
            ("Batch Images", f"~{batch_mb:.0f} MB"),
            ("Feature Maps", f"~{activations_mb:.0f} MB"),
            ("Gradients", f"~{gradients_mb:.0f} MB"),
        ]

        for label, value in rows:
            lines.append(f"  {label:<16} {value:>10}")

        lines.append("")
        lines.append(f"  {'Total':<16} {est.total_gpu_gb:.1f} GB")

        # Status line
        status_tags = {
            "green": ("[green]", "[/green]"),
            "yellow": ("[yellow]", "[/yellow]"),
            "red": ("[red]", "[/red]"),
        }
        open_tag, close_tag = status_tags.get(est.gpu_status, ("", ""))
        lines.append("")
        lines.append(f"{open_tag}{est.gpu_message}{close_tag}")

        return "\n".join(lines)

    def _format_count(self, count: float) -> str:
        """Format parameter count with appropriate suffix."""
        if count >= 1e9:
            return f"{count/1e9:.1f}B"
        elif count >= 1e6:
            return f"{count/1e6:.1f}M"
        elif count >= 1e3:
            return f"{count/1e3:.1f}K"
        return f"{count:.0f}"

__init__(estimate=None, model_type='Single Instance', **kwargs)

Initialize the breakdown card.

Parameters:

Name Type Description Default
estimate Optional[MemoryEstimate]

Memory estimate to display.

None
model_type str

Name of model type for header display.

'Single Instance'
**kwargs

Additional arguments passed to parent.

{}
Source code in sleap_nn/config_generator/tui/widgets/memory_gauge.py
def __init__(
    self,
    estimate: Optional[MemoryEstimate] = None,
    model_type: str = "Single Instance",
    **kwargs,
):
    """Initialize the breakdown card.

    Args:
        estimate: Memory estimate to display.
        model_type: Name of model type for header display.
        **kwargs: Additional arguments passed to parent.
    """
    super().__init__(**kwargs)
    self._estimate = estimate
    self._model_type = model_type

render()

Render the breakdown card.

Source code in sleap_nn/config_generator/tui/widgets/memory_gauge.py
def render(self) -> str:
    """Render the breakdown card."""
    if self._estimate is None:
        return "Memory Breakdown\n" "Calculating..."

    est = self._estimate
    lines = [
        f"GPU Memory ({self._model_type})",
        "─" * 30,
    ]

    # Format values
    params_mb = est.model_memory_gb * 1024
    batch_mb = (est.batch_size * est.image_bytes) / (1024 * 1024)
    activations_mb = est.activations_memory_gb * 1024
    gradients_mb = est.gradients_memory_gb * 1024

    rows = [
        ("Model Params", f"~{self._format_count(params_mb / 4 * 1e6)}"),
        ("Weights", f"~{params_mb:.0f} MB"),
        ("Batch Images", f"~{batch_mb:.0f} MB"),
        ("Feature Maps", f"~{activations_mb:.0f} MB"),
        ("Gradients", f"~{gradients_mb:.0f} MB"),
    ]

    for label, value in rows:
        lines.append(f"  {label:<16} {value:>10}")

    lines.append("")
    lines.append(f"  {'Total':<16} {est.total_gpu_gb:.1f} GB")

    # Status line
    status_tags = {
        "green": ("[green]", "[/green]"),
        "yellow": ("[yellow]", "[/yellow]"),
        "red": ("[red]", "[/red]"),
    }
    open_tag, close_tag = status_tags.get(est.gpu_status, ("", ""))
    lines.append("")
    lines.append(f"{open_tag}{est.gpu_message}{close_tag}")

    return "\n".join(lines)

update_estimate(estimate, model_type=None)

Update the displayed estimate.

Parameters:

Name Type Description Default
estimate MemoryEstimate

New memory estimate.

required
model_type Optional[str]

Optional new model type name.

None
Source code in sleap_nn/config_generator/tui/widgets/memory_gauge.py
def update_estimate(
    self, estimate: MemoryEstimate, model_type: Optional[str] = None
) -> None:
    """Update the displayed estimate.

    Args:
        estimate: New memory estimate.
        model_type: Optional new model type name.
    """
    self._estimate = estimate
    if model_type:
        self._model_type = model_type
    self.refresh()

MemoryGauge

Bases: Static

Widget displaying memory estimation with color-coded status.

Shows estimated GPU and CPU memory usage with visual indicators for whether the configuration will fit on available hardware.

Methods:

Name Description
__init__

Initialize with optional memory estimate.

render

Render the memory gauge display.

toggle_breakdown

Toggle the detailed breakdown visibility.

update_estimate

Update the displayed memory estimate.

Attributes:

Name Type Description
estimate Optional[MemoryEstimate]

Get the current memory estimate.

Source code in sleap_nn/config_generator/tui/widgets/memory_gauge.py
class MemoryGauge(Static):
    """Widget displaying memory estimation with color-coded status.

    Shows estimated GPU and CPU memory usage with visual indicators
    for whether the configuration will fit on available hardware.
    """

    DEFAULT_CSS = """
    MemoryGauge {
        height: auto;
        padding: 1;
        border: solid $primary;
        margin-bottom: 1;
    }

    MemoryGauge .memory-title {
        text-style: bold;
    }

    MemoryGauge .status-green {
        color: $success;
    }

    MemoryGauge .status-yellow {
        color: $warning;
    }

    MemoryGauge .status-red {
        color: $error;
    }

    MemoryGauge .memory-breakdown {
        color: $text-muted;
        margin-top: 1;
    }
    """

    def __init__(
        self,
        estimate: Optional[MemoryEstimate] = None,
        show_breakdown: bool = True,
        **kwargs,
    ):
        """Initialize with optional memory estimate.

        Args:
            estimate: Initial memory estimate to display.
            show_breakdown: Whether to show detailed breakdown.
            **kwargs: Additional arguments passed to parent.
        """
        super().__init__(**kwargs)
        self._estimate = estimate
        self._show_breakdown = show_breakdown

    def update_estimate(self, estimate: MemoryEstimate) -> None:
        """Update the displayed memory estimate.

        Args:
            estimate: New memory estimate to display.
        """
        self._estimate = estimate
        self.refresh()

    def toggle_breakdown(self) -> None:
        """Toggle the detailed breakdown visibility."""
        self._show_breakdown = not self._show_breakdown
        self.refresh()

    @property
    def estimate(self) -> Optional[MemoryEstimate]:
        """Get the current memory estimate."""
        return self._estimate

    def render(self) -> str:
        """Render the memory gauge display."""
        if self._estimate is None:
            return "Memory Estimate\n" "──────────────\n" "Loading..."

        status_icons = {"green": "✓", "yellow": "⚠", "red": "✗"}
        icon = status_icons.get(self._estimate.gpu_status, "?")

        lines = [
            "Memory Estimate",
            "──────────────",
            f"GPU: {self._estimate.total_gpu_gb:.1f} GB {icon}",
            f"  {self._estimate.gpu_message}",
            "",
            f"CPU: {self._estimate.cache_memory_gb:.1f} GB",
            f"  {self._estimate.cpu_message}",
        ]

        if self._show_breakdown and hasattr(self._estimate, "breakdown"):
            lines.append("")
            lines.append("Breakdown:")
            breakdown = self._estimate.breakdown
            if breakdown:
                for key, value in breakdown.items():
                    lines.append(f"  {key}: {value:.1f} MB")

        return "\n".join(lines)

estimate property

Get the current memory estimate.

__init__(estimate=None, show_breakdown=True, **kwargs)

Initialize with optional memory estimate.

Parameters:

Name Type Description Default
estimate Optional[MemoryEstimate]

Initial memory estimate to display.

None
show_breakdown bool

Whether to show detailed breakdown.

True
**kwargs

Additional arguments passed to parent.

{}
Source code in sleap_nn/config_generator/tui/widgets/memory_gauge.py
def __init__(
    self,
    estimate: Optional[MemoryEstimate] = None,
    show_breakdown: bool = True,
    **kwargs,
):
    """Initialize with optional memory estimate.

    Args:
        estimate: Initial memory estimate to display.
        show_breakdown: Whether to show detailed breakdown.
        **kwargs: Additional arguments passed to parent.
    """
    super().__init__(**kwargs)
    self._estimate = estimate
    self._show_breakdown = show_breakdown

render()

Render the memory gauge display.

Source code in sleap_nn/config_generator/tui/widgets/memory_gauge.py
def render(self) -> str:
    """Render the memory gauge display."""
    if self._estimate is None:
        return "Memory Estimate\n" "──────────────\n" "Loading..."

    status_icons = {"green": "✓", "yellow": "⚠", "red": "✗"}
    icon = status_icons.get(self._estimate.gpu_status, "?")

    lines = [
        "Memory Estimate",
        "──────────────",
        f"GPU: {self._estimate.total_gpu_gb:.1f} GB {icon}",
        f"  {self._estimate.gpu_message}",
        "",
        f"CPU: {self._estimate.cache_memory_gb:.1f} GB",
        f"  {self._estimate.cpu_message}",
    ]

    if self._show_breakdown and hasattr(self._estimate, "breakdown"):
        lines.append("")
        lines.append("Breakdown:")
        breakdown = self._estimate.breakdown
        if breakdown:
            for key, value in breakdown.items():
                lines.append(f"  {key}: {value:.1f} MB")

    return "\n".join(lines)

toggle_breakdown()

Toggle the detailed breakdown visibility.

Source code in sleap_nn/config_generator/tui/widgets/memory_gauge.py
def toggle_breakdown(self) -> None:
    """Toggle the detailed breakdown visibility."""
    self._show_breakdown = not self._show_breakdown
    self.refresh()

update_estimate(estimate)

Update the displayed memory estimate.

Parameters:

Name Type Description Default
estimate MemoryEstimate

New memory estimate to display.

required
Source code in sleap_nn/config_generator/tui/widgets/memory_gauge.py
def update_estimate(self, estimate: MemoryEstimate) -> None:
    """Update the displayed memory estimate.

    Args:
        estimate: New memory estimate to display.
    """
    self._estimate = estimate
    self.refresh()

ModelInfoDisplay

Bases: Static

Display for model architecture information.

Shows key model metrics like parameter count, receptive field, and encoder/decoder block counts.

Methods:

Name Description
__init__

Initialize the model info display.

render

Render the model info display.

update_info

Update model info values.

Source code in sleap_nn/config_generator/tui/widgets/size_display.py
class ModelInfoDisplay(Static):
    """Display for model architecture information.

    Shows key model metrics like parameter count, receptive field,
    and encoder/decoder block counts.
    """

    DEFAULT_CSS = """
    ModelInfoDisplay {
        height: auto;
        padding: 1;
        margin: 1 0;
        border: solid $surface-lighten-2;
    }

    ModelInfoDisplay .model-info-title {
        text-style: bold;
        margin-bottom: 1;
    }

    ModelInfoDisplay .model-info-grid {
        height: auto;
    }

    ModelInfoDisplay .info-label {
        color: $text-muted;
    }

    ModelInfoDisplay .info-value {
        color: $primary;
        text-style: bold;
    }
    """

    def __init__(
        self,
        params: int = 0,
        receptive_field: int = 0,
        encoder_blocks: int = 0,
        decoder_blocks: int = 0,
        title: str = "Model Architecture",
        id: Optional[str] = None,
        classes: Optional[str] = None,
    ):
        """Initialize the model info display.

        Args:
            params: Total parameter count.
            receptive_field: Receptive field size in pixels.
            encoder_blocks: Number of encoder blocks.
            decoder_blocks: Number of decoder blocks.
            title: Display title.
            id: Widget ID.
            classes: CSS classes.
        """
        super().__init__(id=id, classes=classes)
        self._params = params
        self._rf = receptive_field
        self._enc = encoder_blocks
        self._dec = decoder_blocks
        self._title = title

    def update_info(
        self,
        params: Optional[int] = None,
        receptive_field: Optional[int] = None,
        encoder_blocks: Optional[int] = None,
        decoder_blocks: Optional[int] = None,
    ) -> None:
        """Update model info values.

        Args:
            params: New parameter count.
            receptive_field: New receptive field.
            encoder_blocks: New encoder block count.
            decoder_blocks: New decoder block count.
        """
        if params is not None:
            self._params = params
        if receptive_field is not None:
            self._rf = receptive_field
        if encoder_blocks is not None:
            self._enc = encoder_blocks
        if decoder_blocks is not None:
            self._dec = decoder_blocks
        self.refresh()

    def _format_params(self, count: int) -> str:
        """Format parameter count with suffix."""
        if count >= 1e9:
            return f"{count/1e9:.1f}B"
        elif count >= 1e6:
            return f"{count/1e6:.1f}M"
        elif count >= 1e3:
            return f"{count/1e3:.1f}K"
        return str(count)

    def render(self) -> str:
        """Render the model info display."""
        lines = [
            self._title,
            "─" * len(self._title),
            "",
            f"  Parameters:     {self._format_params(self._params)}",
            f"  Receptive Field: {self._rf}px",
            f"  Encoder Blocks:  {self._enc}",
            f"  Decoder Blocks:  {self._dec}",
        ]
        return "\n".join(lines)

__init__(params=0, receptive_field=0, encoder_blocks=0, decoder_blocks=0, title='Model Architecture', id=None, classes=None)

Initialize the model info display.

Parameters:

Name Type Description Default
params int

Total parameter count.

0
receptive_field int

Receptive field size in pixels.

0
encoder_blocks int

Number of encoder blocks.

0
decoder_blocks int

Number of decoder blocks.

0
title str

Display title.

'Model Architecture'
id Optional[str]

Widget ID.

None
classes Optional[str]

CSS classes.

None
Source code in sleap_nn/config_generator/tui/widgets/size_display.py
def __init__(
    self,
    params: int = 0,
    receptive_field: int = 0,
    encoder_blocks: int = 0,
    decoder_blocks: int = 0,
    title: str = "Model Architecture",
    id: Optional[str] = None,
    classes: Optional[str] = None,
):
    """Initialize the model info display.

    Args:
        params: Total parameter count.
        receptive_field: Receptive field size in pixels.
        encoder_blocks: Number of encoder blocks.
        decoder_blocks: Number of decoder blocks.
        title: Display title.
        id: Widget ID.
        classes: CSS classes.
    """
    super().__init__(id=id, classes=classes)
    self._params = params
    self._rf = receptive_field
    self._enc = encoder_blocks
    self._dec = decoder_blocks
    self._title = title

render()

Render the model info display.

Source code in sleap_nn/config_generator/tui/widgets/size_display.py
def render(self) -> str:
    """Render the model info display."""
    lines = [
        self._title,
        "─" * len(self._title),
        "",
        f"  Parameters:     {self._format_params(self._params)}",
        f"  Receptive Field: {self._rf}px",
        f"  Encoder Blocks:  {self._enc}",
        f"  Decoder Blocks:  {self._dec}",
    ]
    return "\n".join(lines)

update_info(params=None, receptive_field=None, encoder_blocks=None, decoder_blocks=None)

Update model info values.

Parameters:

Name Type Description Default
params Optional[int]

New parameter count.

None
receptive_field Optional[int]

New receptive field.

None
encoder_blocks Optional[int]

New encoder block count.

None
decoder_blocks Optional[int]

New decoder block count.

None
Source code in sleap_nn/config_generator/tui/widgets/size_display.py
def update_info(
    self,
    params: Optional[int] = None,
    receptive_field: Optional[int] = None,
    encoder_blocks: Optional[int] = None,
    decoder_blocks: Optional[int] = None,
) -> None:
    """Update model info values.

    Args:
        params: New parameter count.
        receptive_field: New receptive field.
        encoder_blocks: New encoder block count.
        decoder_blocks: New decoder block count.
    """
    if params is not None:
        self._params = params
    if receptive_field is not None:
        self._rf = receptive_field
    if encoder_blocks is not None:
        self._enc = encoder_blocks
    if decoder_blocks is not None:
        self._dec = decoder_blocks
    self.refresh()

QuickSettingsPanel

Bases: Static

Widget showing quick summary of current settings.

Provides at-a-glance view of key configuration parameters.

Methods:

Name Description
__init__

Initialize the panel.

render

Render the quick settings panel.

update_settings

Update displayed settings.

Source code in sleap_nn/config_generator/tui/widgets/recommendation.py
class QuickSettingsPanel(Static):
    """Widget showing quick summary of current settings.

    Provides at-a-glance view of key configuration parameters.
    """

    DEFAULT_CSS = """
    QuickSettingsPanel {
        height: auto;
        padding: 1;
        border: solid $surface-lighten-2;
        margin-bottom: 1;
    }

    QuickSettingsPanel .setting-highlight {
        color: $primary;
    }
    """

    def __init__(self, **kwargs):
        """Initialize the panel."""
        super().__init__(**kwargs)
        self._settings = {}

    def update_settings(
        self,
        pipeline: str = "",
        backbone: str = "",
        batch_size: int = 0,
        input_scale: float = 1.0,
        sigma: float = 5.0,
    ) -> None:
        """Update displayed settings.

        Args:
            pipeline: Pipeline type.
            backbone: Backbone architecture.
            batch_size: Batch size.
            input_scale: Input scaling factor.
            sigma: Confidence map sigma.
        """
        self._settings = {
            "pipeline": pipeline,
            "backbone": backbone,
            "batch_size": batch_size,
            "input_scale": input_scale,
            "sigma": sigma,
        }
        self.refresh()

    def render(self) -> str:
        """Render the quick settings panel."""
        if not self._settings:
            return "Current Settings\n" "────────────────\n" "Not configured"

        s = self._settings
        lines = [
            "Current Settings",
            "────────────────",
            f"  Pipeline:   {s['pipeline'] or '-'}",
            f"  Backbone:   {s['backbone'] or '-'}",
            f"  Batch size: {s['batch_size']}",
            f"  Scale:      {s['input_scale']:.2f}",
            f"  Sigma:      {s['sigma']:.1f}",
        ]

        return "\n".join(lines)

__init__(**kwargs)

Initialize the panel.

Source code in sleap_nn/config_generator/tui/widgets/recommendation.py
def __init__(self, **kwargs):
    """Initialize the panel."""
    super().__init__(**kwargs)
    self._settings = {}

render()

Render the quick settings panel.

Source code in sleap_nn/config_generator/tui/widgets/recommendation.py
def render(self) -> str:
    """Render the quick settings panel."""
    if not self._settings:
        return "Current Settings\n" "────────────────\n" "Not configured"

    s = self._settings
    lines = [
        "Current Settings",
        "────────────────",
        f"  Pipeline:   {s['pipeline'] or '-'}",
        f"  Backbone:   {s['backbone'] or '-'}",
        f"  Batch size: {s['batch_size']}",
        f"  Scale:      {s['input_scale']:.2f}",
        f"  Sigma:      {s['sigma']:.1f}",
    ]

    return "\n".join(lines)

update_settings(pipeline='', backbone='', batch_size=0, input_scale=1.0, sigma=5.0)

Update displayed settings.

Parameters:

Name Type Description Default
pipeline str

Pipeline type.

''
backbone str

Backbone architecture.

''
batch_size int

Batch size.

0
input_scale float

Input scaling factor.

1.0
sigma float

Confidence map sigma.

5.0
Source code in sleap_nn/config_generator/tui/widgets/recommendation.py
def update_settings(
    self,
    pipeline: str = "",
    backbone: str = "",
    batch_size: int = 0,
    input_scale: float = 1.0,
    sigma: float = 5.0,
) -> None:
    """Update displayed settings.

    Args:
        pipeline: Pipeline type.
        backbone: Backbone architecture.
        batch_size: Batch size.
        input_scale: Input scaling factor.
        sigma: Confidence map sigma.
    """
    self._settings = {
        "pipeline": pipeline,
        "backbone": backbone,
        "batch_size": batch_size,
        "input_scale": input_scale,
        "sigma": sigma,
    }
    self.refresh()

RangeSlider

Bases: Widget

Dual-handle range slider for min/max values.

Provides visual representation of a range with two adjustable bounds.

Classes:

Name Description
Changed

Posted when the range values change.

Methods:

Name Description
__init__

Initialize the range slider.

compose

Compose the range slider layout.

handle_max_change

Handle max input changes.

handle_min_change

Handle min input changes.

set_range

Programmatically set the range values.

watch_max_val

React to max value changes.

watch_min_val

React to min value changes.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
class RangeSlider(Widget):
    """Dual-handle range slider for min/max values.

    Provides visual representation of a range with two adjustable bounds.
    """

    DEFAULT_CSS = """
    RangeSlider {
        height: auto;
        padding: 0;
        margin: 1 0;
    }

    RangeSlider .range-header {
        height: auto;
        width: 100%;
    }

    RangeSlider .range-label {
        width: 1fr;
    }

    RangeSlider .range-value-display {
        width: auto;
        color: $primary;
        text-style: bold;
    }

    RangeSlider .range-inputs {
        height: auto;
        width: 100%;
        margin-top: 1;
    }

    RangeSlider Input {
        width: 1fr;
    }

    RangeSlider .range-separator {
        width: auto;
        padding: 0 1;
        color: $text-muted;
    }
    """

    min_val: reactive[float] = reactive(0.0)
    max_val: reactive[float] = reactive(100.0)

    class Changed(Message):
        """Posted when the range values change."""

        def __init__(
            self, slider: "RangeSlider", min_val: float, max_val: float
        ) -> None:
            """Initialize with slider widget and new range values."""
            super().__init__()
            self.slider = slider
            self.min_val = min_val
            self.max_val = max_val

        @property
        def control(self) -> "RangeSlider":
            """Return the slider widget that sent this message."""
            return self.slider

    def __init__(
        self,
        label: str = "Range",
        min_val: float = 0.0,
        max_val: float = 100.0,
        limit_min: float = -1000.0,
        limit_max: float = 1000.0,
        step: float = 1.0,
        format_str: str = "{:.1f}",
        unit: str = "",
        symmetric: bool = False,
        id: Optional[str] = None,
        classes: Optional[str] = None,
    ):
        """Initialize the range slider.

        Args:
            label: Display label.
            min_val: Initial minimum value.
            max_val: Initial maximum value.
            limit_min: Absolute minimum allowed.
            limit_max: Absolute maximum allowed.
            step: Step increment.
            format_str: Format string for values.
            unit: Unit suffix for display.
            symmetric: If True, min = -max when adjusting.
            id: Widget ID.
            classes: CSS classes.
        """
        super().__init__(id=id, classes=classes)
        self._label = label
        self._limit_min = limit_min
        self._limit_max = limit_max
        self._step = step
        self._format_str = format_str
        self._unit = unit
        self._symmetric = symmetric

        self.min_val = min_val
        self.max_val = max_val

    def compose(self) -> ComposeResult:
        """Compose the range slider layout."""
        with Horizontal(classes="range-header"):
            yield Static(self._label, classes="range-label")
            yield Static(
                self._format_range(),
                id="range-display",
                classes="range-value-display",
            )

        with Horizontal(classes="range-inputs"):
            yield Input(
                value=str(self.min_val),
                type="number",
                id="min-input",
            )
            yield Static("to", classes="range-separator")
            yield Input(
                value=str(self.max_val),
                type="number",
                id="max-input",
            )

    def _format_range(self) -> str:
        """Format range for display."""
        min_str = self._format_str.format(self.min_val)
        max_str = self._format_str.format(self.max_val)
        if self._unit:
            return f"{min_str} to {max_str}{self._unit}"
        return f"{min_str} to {max_str}"

    def _update_display(self) -> None:
        """Update the range display."""
        try:
            display = self.query_one("#range-display", Static)
            display.update(self._format_range())
        except Exception:
            pass

    def watch_min_val(self, value: float) -> None:
        """React to min value changes."""
        self._update_display()

    def watch_max_val(self, value: float) -> None:
        """React to max value changes."""
        self._update_display()

    @on(Input.Changed, "#min-input")
    def handle_min_change(self, event: Input.Changed) -> None:
        """Handle min input changes."""
        try:
            new_min = float(event.value)
            new_min = max(self._limit_min, min(self.max_val, new_min))
            if self._step > 0:
                new_min = round(new_min / self._step) * self._step

            if self._symmetric:
                self.max_val = -new_min

            if new_min != self.min_val:
                self.min_val = new_min
                self.post_message(self.Changed(self, self.min_val, self.max_val))
        except ValueError:
            pass

    @on(Input.Changed, "#max-input")
    def handle_max_change(self, event: Input.Changed) -> None:
        """Handle max input changes."""
        try:
            new_max = float(event.value)
            new_max = max(self.min_val, min(self._limit_max, new_max))
            if self._step > 0:
                new_max = round(new_max / self._step) * self._step

            if self._symmetric:
                self.min_val = -new_max

            if new_max != self.max_val:
                self.max_val = new_max
                self.post_message(self.Changed(self, self.min_val, self.max_val))
        except ValueError:
            pass

    def set_range(self, min_val: float, max_val: float) -> None:
        """Programmatically set the range values.

        Args:
            min_val: New minimum value.
            max_val: New maximum value.
        """
        self.min_val = max(self._limit_min, min(max_val, min_val))
        self.max_val = max(min_val, min(self._limit_max, max_val))
        try:
            self.query_one("#min-input", Input).value = str(self.min_val)
            self.query_one("#max-input", Input).value = str(self.max_val)
        except Exception:
            pass

Changed

Bases: Message

Posted when the range values change.

Methods:

Name Description
__init__

Initialize with slider widget and new range values.

Attributes:

Name Type Description
control RangeSlider

Return the slider widget that sent this message.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
class Changed(Message):
    """Posted when the range values change."""

    def __init__(
        self, slider: "RangeSlider", min_val: float, max_val: float
    ) -> None:
        """Initialize with slider widget and new range values."""
        super().__init__()
        self.slider = slider
        self.min_val = min_val
        self.max_val = max_val

    @property
    def control(self) -> "RangeSlider":
        """Return the slider widget that sent this message."""
        return self.slider
control property

Return the slider widget that sent this message.

__init__(slider, min_val, max_val)

Initialize with slider widget and new range values.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
def __init__(
    self, slider: "RangeSlider", min_val: float, max_val: float
) -> None:
    """Initialize with slider widget and new range values."""
    super().__init__()
    self.slider = slider
    self.min_val = min_val
    self.max_val = max_val

__init__(label='Range', min_val=0.0, max_val=100.0, limit_min=-1000.0, limit_max=1000.0, step=1.0, format_str='{:.1f}', unit='', symmetric=False, id=None, classes=None)

Initialize the range slider.

Parameters:

Name Type Description Default
label str

Display label.

'Range'
min_val float

Initial minimum value.

0.0
max_val float

Initial maximum value.

100.0
limit_min float

Absolute minimum allowed.

-1000.0
limit_max float

Absolute maximum allowed.

1000.0
step float

Step increment.

1.0
format_str str

Format string for values.

'{:.1f}'
unit str

Unit suffix for display.

''
symmetric bool

If True, min = -max when adjusting.

False
id Optional[str]

Widget ID.

None
classes Optional[str]

CSS classes.

None
Source code in sleap_nn/config_generator/tui/widgets/slider.py
def __init__(
    self,
    label: str = "Range",
    min_val: float = 0.0,
    max_val: float = 100.0,
    limit_min: float = -1000.0,
    limit_max: float = 1000.0,
    step: float = 1.0,
    format_str: str = "{:.1f}",
    unit: str = "",
    symmetric: bool = False,
    id: Optional[str] = None,
    classes: Optional[str] = None,
):
    """Initialize the range slider.

    Args:
        label: Display label.
        min_val: Initial minimum value.
        max_val: Initial maximum value.
        limit_min: Absolute minimum allowed.
        limit_max: Absolute maximum allowed.
        step: Step increment.
        format_str: Format string for values.
        unit: Unit suffix for display.
        symmetric: If True, min = -max when adjusting.
        id: Widget ID.
        classes: CSS classes.
    """
    super().__init__(id=id, classes=classes)
    self._label = label
    self._limit_min = limit_min
    self._limit_max = limit_max
    self._step = step
    self._format_str = format_str
    self._unit = unit
    self._symmetric = symmetric

    self.min_val = min_val
    self.max_val = max_val

compose()

Compose the range slider layout.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
def compose(self) -> ComposeResult:
    """Compose the range slider layout."""
    with Horizontal(classes="range-header"):
        yield Static(self._label, classes="range-label")
        yield Static(
            self._format_range(),
            id="range-display",
            classes="range-value-display",
        )

    with Horizontal(classes="range-inputs"):
        yield Input(
            value=str(self.min_val),
            type="number",
            id="min-input",
        )
        yield Static("to", classes="range-separator")
        yield Input(
            value=str(self.max_val),
            type="number",
            id="max-input",
        )

handle_max_change(event)

Handle max input changes.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
@on(Input.Changed, "#max-input")
def handle_max_change(self, event: Input.Changed) -> None:
    """Handle max input changes."""
    try:
        new_max = float(event.value)
        new_max = max(self.min_val, min(self._limit_max, new_max))
        if self._step > 0:
            new_max = round(new_max / self._step) * self._step

        if self._symmetric:
            self.min_val = -new_max

        if new_max != self.max_val:
            self.max_val = new_max
            self.post_message(self.Changed(self, self.min_val, self.max_val))
    except ValueError:
        pass

handle_min_change(event)

Handle min input changes.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
@on(Input.Changed, "#min-input")
def handle_min_change(self, event: Input.Changed) -> None:
    """Handle min input changes."""
    try:
        new_min = float(event.value)
        new_min = max(self._limit_min, min(self.max_val, new_min))
        if self._step > 0:
            new_min = round(new_min / self._step) * self._step

        if self._symmetric:
            self.max_val = -new_min

        if new_min != self.min_val:
            self.min_val = new_min
            self.post_message(self.Changed(self, self.min_val, self.max_val))
    except ValueError:
        pass

set_range(min_val, max_val)

Programmatically set the range values.

Parameters:

Name Type Description Default
min_val float

New minimum value.

required
max_val float

New maximum value.

required
Source code in sleap_nn/config_generator/tui/widgets/slider.py
def set_range(self, min_val: float, max_val: float) -> None:
    """Programmatically set the range values.

    Args:
        min_val: New minimum value.
        max_val: New maximum value.
    """
    self.min_val = max(self._limit_min, min(max_val, min_val))
    self.max_val = max(min_val, min(self._limit_max, max_val))
    try:
        self.query_one("#min-input", Input).value = str(self.min_val)
        self.query_one("#max-input", Input).value = str(self.max_val)
    except Exception:
        pass

watch_max_val(value)

React to max value changes.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
def watch_max_val(self, value: float) -> None:
    """React to max value changes."""
    self._update_display()

watch_min_val(value)

React to min value changes.

Source code in sleap_nn/config_generator/tui/widgets/slider.py
def watch_min_val(self, value: float) -> None:
    """React to min value changes."""
    self._update_display()

RecommendationPanel

Bases: Static

Widget displaying pipeline recommendation.

Shows the recommended pipeline type, backbone architecture, and any warnings or suggestions based on data analysis.

Methods:

Name Description
__init__

Initialize with optional recommendation.

render

Render the recommendation panel.

update_recommendation

Update the displayed recommendation.

Attributes:

Name Type Description
recommendation Optional[ConfigRecommendation]

Get the current recommendation.

Source code in sleap_nn/config_generator/tui/widgets/recommendation.py
class RecommendationPanel(Static):
    """Widget displaying pipeline recommendation.

    Shows the recommended pipeline type, backbone architecture,
    and any warnings or suggestions based on data analysis.
    """

    DEFAULT_CSS = """
    RecommendationPanel {
        height: auto;
        padding: 1;
        border: solid $secondary;
        margin-bottom: 1;
    }

    RecommendationPanel .rec-title {
        text-style: bold;
    }

    RecommendationPanel .rec-pipeline {
        color: $success;
        text-style: bold;
    }

    RecommendationPanel .rec-backbone {
        color: $primary;
    }

    RecommendationPanel .rec-warning {
        color: $warning;
    }
    """

    def __init__(self, recommendation: Optional[ConfigRecommendation] = None, **kwargs):
        """Initialize with optional recommendation.

        Args:
            recommendation: Initial recommendation to display.
            **kwargs: Additional arguments passed to parent.
        """
        super().__init__(**kwargs)
        self._recommendation = recommendation

    def update_recommendation(self, rec: ConfigRecommendation) -> None:
        """Update the displayed recommendation.

        Args:
            rec: New recommendation to display.
        """
        self._recommendation = rec
        self.refresh()

    @property
    def recommendation(self) -> Optional[ConfigRecommendation]:
        """Get the current recommendation."""
        return self._recommendation

    def render(self) -> str:
        """Render the recommendation panel."""
        if self._recommendation is None:
            return "Recommendation\n" "──────────────\n" "Analyzing..."

        rec = self._recommendation
        lines = [
            "Recommendation",
            "──────────────",
            f"Pipeline: {rec.pipeline.recommended}",
            f"  {rec.pipeline.reason}",
            "",
            f"Backbone: {rec.backbone}",
            f"  {rec.backbone_reason}",
        ]

        if rec.pipeline.alternatives:
            lines.append("")
            lines.append("Alternatives:")
            for alt in rec.pipeline.alternatives[:2]:  # Show top 2 alternatives
                lines.append(f"  • {alt}")

        if rec.pipeline.warnings:
            lines.append("")
            lines.append("Warnings:")
            for w in rec.pipeline.warnings:
                lines.append(f"  âš  {w}")

        return "\n".join(lines)

recommendation property

Get the current recommendation.

__init__(recommendation=None, **kwargs)

Initialize with optional recommendation.

Parameters:

Name Type Description Default
recommendation Optional[ConfigRecommendation]

Initial recommendation to display.

None
**kwargs

Additional arguments passed to parent.

{}
Source code in sleap_nn/config_generator/tui/widgets/recommendation.py
def __init__(self, recommendation: Optional[ConfigRecommendation] = None, **kwargs):
    """Initialize with optional recommendation.

    Args:
        recommendation: Initial recommendation to display.
        **kwargs: Additional arguments passed to parent.
    """
    super().__init__(**kwargs)
    self._recommendation = recommendation

render()

Render the recommendation panel.

Source code in sleap_nn/config_generator/tui/widgets/recommendation.py
def render(self) -> str:
    """Render the recommendation panel."""
    if self._recommendation is None:
        return "Recommendation\n" "──────────────\n" "Analyzing..."

    rec = self._recommendation
    lines = [
        "Recommendation",
        "──────────────",
        f"Pipeline: {rec.pipeline.recommended}",
        f"  {rec.pipeline.reason}",
        "",
        f"Backbone: {rec.backbone}",
        f"  {rec.backbone_reason}",
    ]

    if rec.pipeline.alternatives:
        lines.append("")
        lines.append("Alternatives:")
        for alt in rec.pipeline.alternatives[:2]:  # Show top 2 alternatives
            lines.append(f"  • {alt}")

    if rec.pipeline.warnings:
        lines.append("")
        lines.append("Warnings:")
        for w in rec.pipeline.warnings:
            lines.append(f"  âš  {w}")

    return "\n".join(lines)

update_recommendation(rec)

Update the displayed recommendation.

Parameters:

Name Type Description Default
rec ConfigRecommendation

New recommendation to display.

required
Source code in sleap_nn/config_generator/tui/widgets/recommendation.py
def update_recommendation(self, rec: ConfigRecommendation) -> None:
    """Update the displayed recommendation.

    Args:
        rec: New recommendation to display.
    """
    self._recommendation = rec
    self.refresh()

SigmaVisualization

Bases: Static

Visual representation of confidence map sigma.

Shows a text-based representation of the Gaussian spread for confidence maps at the current sigma setting.

Methods:

Name Description
__init__

Initialize the sigma visualization.

render

Render the sigma visualization.

update_sigma

Update sigma value.

Source code in sleap_nn/config_generator/tui/widgets/size_display.py
class SigmaVisualization(Static):
    """Visual representation of confidence map sigma.

    Shows a text-based representation of the Gaussian spread
    for confidence maps at the current sigma setting.
    """

    DEFAULT_CSS = """
    SigmaVisualization {
        height: auto;
        padding: 1;
        margin: 1 0;
        border: solid $surface-lighten-2;
    }

    SigmaVisualization .sigma-title {
        margin-bottom: 1;
    }

    SigmaVisualization .sigma-value {
        color: $primary;
        text-style: bold;
    }

    SigmaVisualization .sigma-viz {
        height: auto;
        margin-top: 1;
    }
    """

    def __init__(
        self,
        sigma: float = 5.0,
        output_stride: int = 1,
        id: Optional[str] = None,
        classes: Optional[str] = None,
    ):
        """Initialize the sigma visualization.

        Args:
            sigma: Sigma value in pixels.
            output_stride: Output stride for scaling.
            id: Widget ID.
            classes: CSS classes.
        """
        super().__init__(id=id, classes=classes)
        self._sigma = sigma
        self._output_stride = output_stride

    def update_sigma(self, sigma: float, output_stride: Optional[int] = None) -> None:
        """Update sigma value.

        Args:
            sigma: New sigma value.
            output_stride: New output stride.
        """
        self._sigma = sigma
        if output_stride is not None:
            self._output_stride = output_stride
        self.refresh()

    def render(self) -> str:
        """Render the sigma visualization."""
        # 2 sigma covers ~95% of the Gaussian
        spread = int(self._sigma * 2)

        # Create a simple text visualization
        lines = [
            f"Sigma: {self._sigma:.1f}px",
            f"2σ spread: {spread}px (covers 95%)",
            "",
        ]

        # Visual representation using characters
        # Create a simple 1D Gaussian profile
        width = min(31, spread * 2 + 1)
        center = width // 2

        # Build visual rows using shading characters
        profile = []
        for i in range(width):
            dist = abs(i - center)
            if dist == 0:
                profile.append("â–ˆ")
            elif dist <= self._sigma * 0.5:
                profile.append("â–“")
            elif dist <= self._sigma:
                profile.append("â–’")
            elif dist <= self._sigma * 2:
                profile.append("â–‘")
            else:
                profile.append(" ")

        lines.append("  " + "".join(profile))
        lines.append(f"  {'─' * width}")
        lines.append(f"  {' ' * (center - 1)}↑")
        lines.append(f"  {' ' * (center - 3)}peak")

        return "\n".join(lines)

__init__(sigma=5.0, output_stride=1, id=None, classes=None)

Initialize the sigma visualization.

Parameters:

Name Type Description Default
sigma float

Sigma value in pixels.

5.0
output_stride int

Output stride for scaling.

1
id Optional[str]

Widget ID.

None
classes Optional[str]

CSS classes.

None
Source code in sleap_nn/config_generator/tui/widgets/size_display.py
def __init__(
    self,
    sigma: float = 5.0,
    output_stride: int = 1,
    id: Optional[str] = None,
    classes: Optional[str] = None,
):
    """Initialize the sigma visualization.

    Args:
        sigma: Sigma value in pixels.
        output_stride: Output stride for scaling.
        id: Widget ID.
        classes: CSS classes.
    """
    super().__init__(id=id, classes=classes)
    self._sigma = sigma
    self._output_stride = output_stride

render()

Render the sigma visualization.

Source code in sleap_nn/config_generator/tui/widgets/size_display.py
def render(self) -> str:
    """Render the sigma visualization."""
    # 2 sigma covers ~95% of the Gaussian
    spread = int(self._sigma * 2)

    # Create a simple text visualization
    lines = [
        f"Sigma: {self._sigma:.1f}px",
        f"2σ spread: {spread}px (covers 95%)",
        "",
    ]

    # Visual representation using characters
    # Create a simple 1D Gaussian profile
    width = min(31, spread * 2 + 1)
    center = width // 2

    # Build visual rows using shading characters
    profile = []
    for i in range(width):
        dist = abs(i - center)
        if dist == 0:
            profile.append("â–ˆ")
        elif dist <= self._sigma * 0.5:
            profile.append("â–“")
        elif dist <= self._sigma:
            profile.append("â–’")
        elif dist <= self._sigma * 2:
            profile.append("â–‘")
        else:
            profile.append(" ")

    lines.append("  " + "".join(profile))
    lines.append(f"  {'─' * width}")
    lines.append(f"  {' ' * (center - 1)}↑")
    lines.append(f"  {' ' * (center - 3)}peak")

    return "\n".join(lines)

update_sigma(sigma, output_stride=None)

Update sigma value.

Parameters:

Name Type Description Default
sigma float

New sigma value.

required
output_stride Optional[int]

New output stride.

None
Source code in sleap_nn/config_generator/tui/widgets/size_display.py
def update_sigma(self, sigma: float, output_stride: Optional[int] = None) -> None:
    """Update sigma value.

    Args:
        sigma: New sigma value.
        output_stride: New output stride.
    """
    self._sigma = sigma
    if output_stride is not None:
        self._output_stride = output_stride
    self.refresh()

SizeDisplay

Bases: Static

Widget displaying image size transformation pipeline.

Shows how image dimensions change through preprocessing steps: Original → Scaled → Cropped → Output

Useful for visualizing the effect of scale and output stride settings.

Methods:

Name Description
__init__

Initialize the size display.

render

Render the size display.

update_sizes

Update size parameters.

Source code in sleap_nn/config_generator/tui/widgets/size_display.py
class SizeDisplay(Static):
    """Widget displaying image size transformation pipeline.

    Shows how image dimensions change through preprocessing steps:
    Original → Scaled → Cropped → Output

    Useful for visualizing the effect of scale and output stride settings.
    """

    DEFAULT_CSS = """
    SizeDisplay {
        height: auto;
        padding: 1;
        margin: 1 0;
        border: solid $surface-lighten-2;
    }

    SizeDisplay .size-title {
        text-style: bold;
        margin-bottom: 1;
    }

    SizeDisplay .size-flow {
        height: auto;
    }

    SizeDisplay .size-step {
        color: $text;
    }

    SizeDisplay .size-value {
        color: $primary;
        text-style: bold;
    }

    SizeDisplay .size-arrow {
        color: $text-muted;
    }

    SizeDisplay .size-label {
        color: $text-muted;
    }
    """

    def __init__(
        self,
        original: Tuple[int, int] = (0, 0),
        scale: float = 1.0,
        max_size: Optional[Tuple[int, int]] = None,
        crop_size: Optional[int] = None,
        output_stride: int = 1,
        title: str = "Image Size Pipeline",
        id: Optional[str] = None,
        classes: Optional[str] = None,
    ):
        """Initialize the size display.

        Args:
            original: Original image dimensions (width, height).
            scale: Input scaling factor.
            max_size: Optional maximum size constraint (width, height).
            crop_size: Optional crop size (for centered instance).
            output_stride: Output stride for final dimensions.
            title: Display title.
            id: Widget ID.
            classes: CSS classes.
        """
        super().__init__(id=id, classes=classes)
        self._original = original
        self._scale = scale
        self._max_size = max_size
        self._crop_size = crop_size
        self._output_stride = output_stride
        self._title = title

    def update_sizes(
        self,
        original: Optional[Tuple[int, int]] = None,
        scale: Optional[float] = None,
        max_size: Optional[Tuple[int, int]] = None,
        crop_size: Optional[int] = None,
        output_stride: Optional[int] = None,
    ) -> None:
        """Update size parameters.

        Args:
            original: New original dimensions.
            scale: New scale factor.
            max_size: New maximum size constraint.
            crop_size: New crop size.
            output_stride: New output stride.
        """
        if original is not None:
            self._original = original
        if scale is not None:
            self._scale = scale
        if max_size is not None:
            self._max_size = max_size
        if crop_size is not None:
            self._crop_size = crop_size
        if output_stride is not None:
            self._output_stride = output_stride
        self.refresh()

    def _compute_scaled(self) -> Tuple[int, int]:
        """Compute scaled dimensions."""
        w, h = self._original
        scaled_w = int(w * self._scale)
        scaled_h = int(h * self._scale)

        if self._max_size:
            max_w, max_h = self._max_size
            scaled_w = min(scaled_w, max_w)
            scaled_h = min(scaled_h, max_h)

        return scaled_w, scaled_h

    def _compute_model_input(self) -> Tuple[int, int]:
        """Compute model input dimensions."""
        if self._crop_size:
            return self._crop_size, self._crop_size
        return self._compute_scaled()

    def _compute_output(self) -> Tuple[int, int]:
        """Compute output dimensions."""
        input_w, input_h = self._compute_model_input()
        return input_w // self._output_stride, input_h // self._output_stride

    def render(self) -> str:
        """Render the size display."""
        orig_w, orig_h = self._original
        scaled_w, scaled_h = self._compute_scaled()
        input_w, input_h = self._compute_model_input()
        out_w, out_h = self._compute_output()

        lines = [
            self._title,
            "─" * len(self._title),
            "",
        ]

        # Original
        lines.append(f"Original:     {orig_w} × {orig_h}")

        # Scaled (if different)
        if self._scale != 1.0 or self._max_size:
            scale_text = f"×{self._scale:.2f}" if self._scale != 1.0 else ""
            max_text = ""
            if self._max_size:
                max_text = f" (max {self._max_size[0]}×{self._max_size[1]})"
            lines.append(f"    ↓ scale{scale_text}{max_text}")
            lines.append(f"Scaled:       {scaled_w} × {scaled_h}")

        # Cropped (if applicable)
        if self._crop_size:
            lines.append(f"    ↓ crop to {self._crop_size}px")
            lines.append(f"Model Input:  {input_w} × {input_h}")
        else:
            lines.append(f"Model Input:  {input_w} × {input_h}")

        # Output (if stride > 1)
        if self._output_stride > 1:
            lines.append(f"    ↓ stride {self._output_stride}")
        lines.append(f"Output:       {out_w} × {out_h}")

        # Summary
        lines.append("")
        total_reduction = (
            (orig_w * orig_h) / (out_w * out_h) if out_w * out_h > 0 else 0
        )
        lines.append(f"Reduction: {total_reduction:.1f}× fewer pixels")

        return "\n".join(lines)

__init__(original=(0, 0), scale=1.0, max_size=None, crop_size=None, output_stride=1, title='Image Size Pipeline', id=None, classes=None)

Initialize the size display.

Parameters:

Name Type Description Default
original Tuple[int, int]

Original image dimensions (width, height).

(0, 0)
scale float

Input scaling factor.

1.0
max_size Optional[Tuple[int, int]]

Optional maximum size constraint (width, height).

None
crop_size Optional[int]

Optional crop size (for centered instance).

None
output_stride int

Output stride for final dimensions.

1
title str

Display title.

'Image Size Pipeline'
id Optional[str]

Widget ID.

None
classes Optional[str]

CSS classes.

None
Source code in sleap_nn/config_generator/tui/widgets/size_display.py
def __init__(
    self,
    original: Tuple[int, int] = (0, 0),
    scale: float = 1.0,
    max_size: Optional[Tuple[int, int]] = None,
    crop_size: Optional[int] = None,
    output_stride: int = 1,
    title: str = "Image Size Pipeline",
    id: Optional[str] = None,
    classes: Optional[str] = None,
):
    """Initialize the size display.

    Args:
        original: Original image dimensions (width, height).
        scale: Input scaling factor.
        max_size: Optional maximum size constraint (width, height).
        crop_size: Optional crop size (for centered instance).
        output_stride: Output stride for final dimensions.
        title: Display title.
        id: Widget ID.
        classes: CSS classes.
    """
    super().__init__(id=id, classes=classes)
    self._original = original
    self._scale = scale
    self._max_size = max_size
    self._crop_size = crop_size
    self._output_stride = output_stride
    self._title = title

render()

Render the size display.

Source code in sleap_nn/config_generator/tui/widgets/size_display.py
def render(self) -> str:
    """Render the size display."""
    orig_w, orig_h = self._original
    scaled_w, scaled_h = self._compute_scaled()
    input_w, input_h = self._compute_model_input()
    out_w, out_h = self._compute_output()

    lines = [
        self._title,
        "─" * len(self._title),
        "",
    ]

    # Original
    lines.append(f"Original:     {orig_w} × {orig_h}")

    # Scaled (if different)
    if self._scale != 1.0 or self._max_size:
        scale_text = f"×{self._scale:.2f}" if self._scale != 1.0 else ""
        max_text = ""
        if self._max_size:
            max_text = f" (max {self._max_size[0]}×{self._max_size[1]})"
        lines.append(f"    ↓ scale{scale_text}{max_text}")
        lines.append(f"Scaled:       {scaled_w} × {scaled_h}")

    # Cropped (if applicable)
    if self._crop_size:
        lines.append(f"    ↓ crop to {self._crop_size}px")
        lines.append(f"Model Input:  {input_w} × {input_h}")
    else:
        lines.append(f"Model Input:  {input_w} × {input_h}")

    # Output (if stride > 1)
    if self._output_stride > 1:
        lines.append(f"    ↓ stride {self._output_stride}")
    lines.append(f"Output:       {out_w} × {out_h}")

    # Summary
    lines.append("")
    total_reduction = (
        (orig_w * orig_h) / (out_w * out_h) if out_w * out_h > 0 else 0
    )
    lines.append(f"Reduction: {total_reduction:.1f}× fewer pixels")

    return "\n".join(lines)

update_sizes(original=None, scale=None, max_size=None, crop_size=None, output_stride=None)

Update size parameters.

Parameters:

Name Type Description Default
original Optional[Tuple[int, int]]

New original dimensions.

None
scale Optional[float]

New scale factor.

None
max_size Optional[Tuple[int, int]]

New maximum size constraint.

None
crop_size Optional[int]

New crop size.

None
output_stride Optional[int]

New output stride.

None
Source code in sleap_nn/config_generator/tui/widgets/size_display.py
def update_sizes(
    self,
    original: Optional[Tuple[int, int]] = None,
    scale: Optional[float] = None,
    max_size: Optional[Tuple[int, int]] = None,
    crop_size: Optional[int] = None,
    output_stride: Optional[int] = None,
) -> None:
    """Update size parameters.

    Args:
        original: New original dimensions.
        scale: New scale factor.
        max_size: New maximum size constraint.
        crop_size: New crop size.
        output_stride: New output stride.
    """
    if original is not None:
        self._original = original
    if scale is not None:
        self._scale = scale
    if max_size is not None:
        self._max_size = max_size
    if crop_size is not None:
        self._crop_size = crop_size
    if output_stride is not None:
        self._output_stride = output_stride
    self.refresh()

SuccessBox

Bases: InfoBox

Convenience class for success-styled info box.

Methods:

Name Description
__init__

Initialize a success-styled info box.

Source code in sleap_nn/config_generator/tui/widgets/info_box.py
class SuccessBox(InfoBox):
    """Convenience class for success-styled info box."""

    def __init__(
        self,
        message: str,
        title: Optional[str] = "Success",
        id: Optional[str] = None,
        classes: Optional[str] = None,
    ):
        """Initialize a success-styled info box."""
        super().__init__(
            message=message,
            box_type=InfoBoxType.SUCCESS,
            title=title,
            id=id,
            classes=classes,
        )

__init__(message, title='Success', id=None, classes=None)

Initialize a success-styled info box.

Source code in sleap_nn/config_generator/tui/widgets/info_box.py
def __init__(
    self,
    message: str,
    title: Optional[str] = "Success",
    id: Optional[str] = None,
    classes: Optional[str] = None,
):
    """Initialize a success-styled info box."""
    super().__init__(
        message=message,
        box_type=InfoBoxType.SUCCESS,
        title=title,
        id=id,
        classes=classes,
    )

TipBox

Bases: InfoBox

Convenience class for tip-styled info box.

Methods:

Name Description
__init__

Initialize a tip-styled info box.

Source code in sleap_nn/config_generator/tui/widgets/info_box.py
class TipBox(InfoBox):
    """Convenience class for tip-styled info box."""

    def __init__(
        self,
        message: str,
        title: Optional[str] = "Tip",
        id: Optional[str] = None,
        classes: Optional[str] = None,
    ):
        """Initialize a tip-styled info box."""
        super().__init__(
            message=message,
            box_type=InfoBoxType.TIP,
            title=title,
            id=id,
            classes=classes,
        )

__init__(message, title='Tip', id=None, classes=None)

Initialize a tip-styled info box.

Source code in sleap_nn/config_generator/tui/widgets/info_box.py
def __init__(
    self,
    message: str,
    title: Optional[str] = "Tip",
    id: Optional[str] = None,
    classes: Optional[str] = None,
):
    """Initialize a tip-styled info box."""
    super().__init__(
        message=message,
        box_type=InfoBoxType.TIP,
        title=title,
        id=id,
        classes=classes,
    )

ToggleSection

Bases: Widget

Toggle-enabled section with switch control.

A section that can be enabled/disabled with a switch, with the content hidden when disabled.

Classes:

Name Description
Toggled

Posted when the section is enabled/disabled.

Methods:

Name Description
__init__

Initialize the toggle section.

add_content

Add widgets to the section content.

compose

Compose the toggle section layout.

handle_switch_change

Handle switch toggle.

set_enabled

Programmatically set enabled state.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
class ToggleSection(Widget):
    """Toggle-enabled section with switch control.

    A section that can be enabled/disabled with a switch,
    with the content hidden when disabled.
    """

    DEFAULT_CSS = """
    ToggleSection {
        height: auto;
        margin: 1 0;
    }

    ToggleSection .toggle-header {
        height: auto;
        padding: 1;
        background: $surface-lighten-1;
        border: solid $surface-lighten-2;
    }

    ToggleSection .toggle-row {
        height: auto;
        width: 100%;
    }

    ToggleSection .toggle-title {
        width: 1fr;
    }

    ToggleSection .toggle-content {
        padding: 1;
        border: solid $surface-lighten-2;
        border-top: none;
        background: $surface;
    }

    ToggleSection .toggle-content.disabled {
        display: none;
    }
    """

    enabled: reactive[bool] = reactive(False)

    class Toggled(Message):
        """Posted when the section is enabled/disabled."""

        def __init__(self, section: "ToggleSection", enabled: bool) -> None:
            """Initialize with toggle section widget and state."""
            super().__init__()
            self.section = section
            self.enabled = enabled

        @property
        def control(self) -> "ToggleSection":
            """Return the toggle section widget that sent this message."""
            return self.section

    def __init__(
        self,
        title: str = "Section",
        enabled: bool = False,
        description: str = "",
        id: Optional[str] = None,
        classes: Optional[str] = None,
    ):
        """Initialize the toggle section.

        Args:
            title: Header text.
            enabled: Initial enabled state.
            description: Optional description text.
            id: Widget ID.
            classes: CSS classes.
        """
        super().__init__(id=id, classes=classes)
        self._title = title
        self._description = description
        self.enabled = enabled
        self._content_widgets = []

    def compose(self) -> ComposeResult:
        """Compose the toggle section layout."""
        from textual.widgets import Switch

        with Container(classes="toggle-header"):
            with Container(classes="toggle-row"):
                yield Static(self._title, classes="toggle-title")
                yield Switch(value=self.enabled, id="toggle-switch")

            if self._description:
                yield Static(
                    self._description,
                    classes="description-text",
                )

        content_classes = (
            "toggle-content" if self.enabled else "toggle-content disabled"
        )
        yield Container(*self._content_widgets, classes=content_classes, id="content")

    def add_content(self, *widgets: Widget) -> None:
        """Add widgets to the section content.

        Args:
            widgets: Widgets to add.
        """
        self._content_widgets.extend(widgets)

    @on(Message)
    def handle_switch_change(self, event) -> None:
        """Handle switch toggle."""
        from textual.widgets import Switch

        if hasattr(event, "switch") and isinstance(event, Switch.Changed):
            self.enabled = event.value
            self._update_content()
            self.post_message(self.Toggled(self, self.enabled))

    def _update_content(self) -> None:
        """Update content visibility based on enabled state."""
        try:
            content = self.query_one("#content", Container)
            if self.enabled:
                content.remove_class("disabled")
            else:
                content.add_class("disabled")
        except Exception:
            pass

    def set_enabled(self, enabled: bool) -> None:
        """Programmatically set enabled state.

        Args:
            enabled: New enabled state.
        """
        from textual.widgets import Switch

        if enabled != self.enabled:
            self.enabled = enabled
            try:
                switch = self.query_one("#toggle-switch", Switch)
                switch.value = enabled
            except Exception:
                pass
            self._update_content()

Toggled

Bases: Message

Posted when the section is enabled/disabled.

Methods:

Name Description
__init__

Initialize with toggle section widget and state.

Attributes:

Name Type Description
control ToggleSection

Return the toggle section widget that sent this message.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
class Toggled(Message):
    """Posted when the section is enabled/disabled."""

    def __init__(self, section: "ToggleSection", enabled: bool) -> None:
        """Initialize with toggle section widget and state."""
        super().__init__()
        self.section = section
        self.enabled = enabled

    @property
    def control(self) -> "ToggleSection":
        """Return the toggle section widget that sent this message."""
        return self.section
control property

Return the toggle section widget that sent this message.

__init__(section, enabled)

Initialize with toggle section widget and state.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
def __init__(self, section: "ToggleSection", enabled: bool) -> None:
    """Initialize with toggle section widget and state."""
    super().__init__()
    self.section = section
    self.enabled = enabled

__init__(title='Section', enabled=False, description='', id=None, classes=None)

Initialize the toggle section.

Parameters:

Name Type Description Default
title str

Header text.

'Section'
enabled bool

Initial enabled state.

False
description str

Optional description text.

''
id Optional[str]

Widget ID.

None
classes Optional[str]

CSS classes.

None
Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
def __init__(
    self,
    title: str = "Section",
    enabled: bool = False,
    description: str = "",
    id: Optional[str] = None,
    classes: Optional[str] = None,
):
    """Initialize the toggle section.

    Args:
        title: Header text.
        enabled: Initial enabled state.
        description: Optional description text.
        id: Widget ID.
        classes: CSS classes.
    """
    super().__init__(id=id, classes=classes)
    self._title = title
    self._description = description
    self.enabled = enabled
    self._content_widgets = []

add_content(*widgets)

Add widgets to the section content.

Parameters:

Name Type Description Default
widgets Widget

Widgets to add.

()
Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
def add_content(self, *widgets: Widget) -> None:
    """Add widgets to the section content.

    Args:
        widgets: Widgets to add.
    """
    self._content_widgets.extend(widgets)

compose()

Compose the toggle section layout.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
def compose(self) -> ComposeResult:
    """Compose the toggle section layout."""
    from textual.widgets import Switch

    with Container(classes="toggle-header"):
        with Container(classes="toggle-row"):
            yield Static(self._title, classes="toggle-title")
            yield Switch(value=self.enabled, id="toggle-switch")

        if self._description:
            yield Static(
                self._description,
                classes="description-text",
            )

    content_classes = (
        "toggle-content" if self.enabled else "toggle-content disabled"
    )
    yield Container(*self._content_widgets, classes=content_classes, id="content")

handle_switch_change(event)

Handle switch toggle.

Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
@on(Message)
def handle_switch_change(self, event) -> None:
    """Handle switch toggle."""
    from textual.widgets import Switch

    if hasattr(event, "switch") and isinstance(event, Switch.Changed):
        self.enabled = event.value
        self._update_content()
        self.post_message(self.Toggled(self, self.enabled))

set_enabled(enabled)

Programmatically set enabled state.

Parameters:

Name Type Description Default
enabled bool

New enabled state.

required
Source code in sleap_nn/config_generator/tui/widgets/collapsible.py
def set_enabled(self, enabled: bool) -> None:
    """Programmatically set enabled state.

    Args:
        enabled: New enabled state.
    """
    from textual.widgets import Switch

    if enabled != self.enabled:
        self.enabled = enabled
        try:
            switch = self.query_one("#toggle-switch", Switch)
            switch.value = enabled
        except Exception:
            pass
        self._update_content()

WarningBox

Bases: InfoBox

Convenience class for warning-styled info box.

Methods:

Name Description
__init__

Initialize a warning-styled info box.

Source code in sleap_nn/config_generator/tui/widgets/info_box.py
class WarningBox(InfoBox):
    """Convenience class for warning-styled info box."""

    def __init__(
        self,
        message: str,
        title: Optional[str] = "Warning",
        id: Optional[str] = None,
        classes: Optional[str] = None,
    ):
        """Initialize a warning-styled info box."""
        super().__init__(
            message=message,
            box_type=InfoBoxType.WARNING,
            title=title,
            id=id,
            classes=classes,
        )

__init__(message, title='Warning', id=None, classes=None)

Initialize a warning-styled info box.

Source code in sleap_nn/config_generator/tui/widgets/info_box.py
def __init__(
    self,
    message: str,
    title: Optional[str] = "Warning",
    id: Optional[str] = None,
    classes: Optional[str] = None,
):
    """Initialize a warning-styled info box."""
    super().__init__(
        message=message,
        box_type=InfoBoxType.WARNING,
        title=title,
        id=id,
        classes=classes,
    )