Files

108 lines
3.1 KiB
Python

"""
Lines Tab — network interfaces overview.
Displays a DataTable of all network interfaces updated live via bus subscription
to the "network.interfaces" topic.
"""
from __future__ import annotations
from typing import Any, Dict
from textual.app import ComposeResult
from textual.reactive import reactive
from textual.widget import Widget
from textual.widgets import DataTable, Label, Static
def _fmt_bytes(n: int) -> str:
"""Format a byte count as a human-readable string."""
for unit in ("B", "KB", "MB", "GB", "TB"):
if n < 1024:
return f"{n:.1f} {unit}"
n /= 1024
return f"{n:.1f} PB"
class LinesTab(Widget):
"""
Network Interfaces tab.
Subscribes to ``network.interfaces`` bus events and refreshes
the DataTable in real time.
"""
DEFAULT_CSS = """
LinesTab {
layout: vertical;
height: 1fr;
padding: 1 2;
}
LinesTab .tab-title {
color: $primary;
text-style: bold;
height: 1;
margin-bottom: 1;
}
LinesTab DataTable {
height: 1fr;
border: round $primary;
}
"""
COLUMNS = [
("Interface", 12),
("Status", 8),
("IPv4", 16),
("IPv6", 24),
("MAC", 18),
("Speed", 9),
("RX", 12),
("TX", 12),
("RX Err", 8),
("TX Err", 8),
]
def compose(self) -> ComposeResult:
yield Static("// LINES — Network Interfaces", classes="tab-title")
yield DataTable(id="lines-table", zebra_stripes=True, cursor_type="row")
def on_mount(self) -> None:
table = self.query_one("#lines-table", DataTable)
for col_label, width in self.COLUMNS:
table.add_column(col_label, width=width, key=col_label.lower().replace(" ", "_"))
# Subscribe to the bus if app exposes one
app = self.app
if hasattr(app, "bus"):
app.bus.subscribe("network.interfaces", self._on_interfaces)
# Request immediate data
if hasattr(app, "state"):
interfaces = app.state.get("network.interfaces")
if interfaces:
self._refresh_table(interfaces)
def _on_interfaces(self, topic: str, data: Any) -> None:
"""Bus handler — schedule table refresh on the UI thread."""
self.call_from_thread(self._refresh_table, data)
def _refresh_table(self, interfaces: Dict[str, Dict]) -> None:
table = self.query_one("#lines-table", DataTable)
table.clear()
for iface, info in sorted(interfaces.items()):
status_text = info.get("status", "?")
speed = info.get("speed", 0)
speed_str = f"{speed} Mb" if speed else ""
table.add_row(
iface,
status_text,
info.get("ip4", "") or "",
info.get("ip6", "") or "",
info.get("mac", "") or "",
speed_str,
_fmt_bytes(info.get("rx_bytes", 0)),
_fmt_bytes(info.get("tx_bytes", 0)),
str(info.get("rx_errors", 0)),
str(info.get("tx_errors", 0)),
)