StatusBarPlugin¶
Status bar plugins add indicator widgets to the main window’s status bar.
Purpose¶
Use StatusBarPlugin when you want to:
Display real-time status information
Show connection states or system health
Provide a quick click-through to detail dialogs / external pages
Base Class¶
from lightfall.plugins.statusbar_plugin import StatusBarPlugin, StatusBarPluginMetadata
Class Attributes¶
Attribute |
Value |
Description |
|---|---|---|
|
|
Plugin type identifier |
|
|
One instance per plugin |
|
|
Plugin metadata (required) |
StatusBarPluginMetadata¶
Every status bar plugin must define class-level metadata:
@dataclass
class StatusBarPluginMetadata:
id: str # Unique identifier (e.g., "lightfall.statusbar.user")
name: str # Human-readable display name
description: str = "" # Description of what it shows
priority: int = 100 # Sort order (lower = further left)
position: str = "permanent" # "left", "right", or "permanent"
tooltip: str = "" # Default tooltip text
Default Widget¶
The base class provides a default widget: a flat QToolButton whose
clicked signal is wired to on_clicked(). The button shares a
single horizontal layout with neighbouring plugins (no Qt separator
notches between items), so every entry looks consistent and signals
clickability. Plugins typically just override update(),
connect_signals(), disconnect_signals(), and (optionally)
on_clicked().
For complex needs (anchoring a popup, embedding non-button widgets),
override create_widget(); you are then responsible for setting
self._widget and any click wiring.
Required Methods¶
name (property)¶
Unique identifier for this plugin.
@property
def name(self) -> str:
return "my_status"
update()¶
Update the displayed state. Use the inherited helpers to drive the default button:
def update(self) -> None:
self.set_text("OK")
self.set_tooltip("Everything is fine")
self.set_color(self._theme.colors.success)
connect_signals() / disconnect_signals()¶
Wire up (and tear down) the service signals that drive update().
def connect_signals(self) -> None:
self._service.state_changed.connect(self.update)
def disconnect_signals(self) -> None:
try:
self._service.state_changed.disconnect(self.update)
except RuntimeError:
pass
Optional Methods¶
on_clicked()¶
Override to react to a button click. Default is a no-op.
def on_clicked(self) -> None:
QDesktopServices.openUrl(QUrl("https://example.com"))
create_widget(parent)¶
Override only when the default button is not enough (e.g., custom popup
anchor). You must assign self._widget and self._button (if
applicable) yourself.
Helpers¶
Helper |
Purpose |
|---|---|
|
Set the button label text |
|
Set the tooltip |
|
Apply a CSS color string ( |
|
Show or hide the plugin’s widget in the status bar |
|
Current visibility flag |
|
Qt signal emitted with the new visibility |
When a plugin calls set_visible(False) the widget is hidden and the
side container’s layout collapses around it — no leftover gap, no
trailing separator.
Lifecycle¶
Plugin is instantiated on load
StatusBarManagercallscreate_widget()to get the widgetconnect_signals()is called to wire up state handlersupdate()is called to set initial stateDuring runtime, signals trigger
update()callsOn cleanup,
disconnect_signals()is called
Complete Example¶
"""Device connection status indicator."""
from typing import ClassVar
from lightfall.plugins.statusbar_plugin import StatusBarPlugin, StatusBarPluginMetadata
from lightfall.ui.theme import ThemeManager
class DeviceConnectionStatus(StatusBarPlugin):
"""Status bar indicator showing device connection state."""
metadata: ClassVar[StatusBarPluginMetadata] = StatusBarPluginMetadata(
id="my_package.statusbar.device_connection",
name="Device Connection",
description="Shows whether devices are connected",
priority=50,
position="permanent",
)
def __init__(self) -> None:
super().__init__()
self._device_manager = None
self._theme: ThemeManager | None = None
@property
def name(self) -> str:
return "device_connection"
def update(self) -> None:
if not self._device_manager:
return
if self._theme is None:
self._theme = ThemeManager.get_instance()
colors = self._theme.colors
connected = self._device_manager.connected_count
total = self._device_manager.total_count
if connected == total:
self.set_text(f"Devices: {total} connected")
self.set_color(colors.success)
elif connected == 0:
self.set_text(f"Devices: {total} disconnected")
self.set_color(colors.error)
else:
self.set_text(f"Devices: {connected}/{total}")
self.set_color(colors.warning)
self.set_tooltip(
f"Connected: {connected}\n"
f"Disconnected: {total - connected}"
)
def connect_signals(self) -> None:
from lightfall.devices.manager import DeviceManager
self._device_manager = DeviceManager.get_instance()
self._device_manager.connection_changed.connect(self.update)
def disconnect_signals(self) -> None:
if self._device_manager:
try:
self._device_manager.connection_changed.disconnect(self.update)
except RuntimeError:
pass
Clickable Indicator¶
from PySide6.QtCore import QUrl
from PySide6.QtGui import QDesktopServices
from lightfall.plugins.statusbar_plugin import StatusBarPlugin, StatusBarPluginMetadata
class ExternalLinkStatus(StatusBarPlugin):
metadata = StatusBarPluginMetadata(
id="my_package.statusbar.docs",
name="Docs",
priority=80,
)
@property
def name(self) -> str:
return "docs_link"
def update(self) -> None:
self.set_text("Docs")
self.set_tooltip("Open documentation")
def connect_signals(self) -> None:
pass
def disconnect_signals(self) -> None:
pass
def on_clicked(self) -> None:
QDesktopServices.openUrl(QUrl("https://example.com/docs"))
Custom Widget (icon-based, popup anchor, etc.)¶
Override create_widget only when the default button is not enough.
import qtawesome as qta
from PySide6.QtCore import QSize
from PySide6.QtWidgets import QToolButton, QWidget
from lightfall.plugins.statusbar_plugin import StatusBarPlugin, StatusBarPluginMetadata
class IconStatusPlugin(StatusBarPlugin):
metadata = StatusBarPluginMetadata(
id="my_package.statusbar.icon",
name="Network",
priority=30,
)
@property
def name(self) -> str:
return "icon_status"
def create_widget(self, parent: QWidget | None = None) -> QWidget:
button = QToolButton(parent)
button.setAutoRaise(True)
button.setIcon(qta.icon("mdi.wifi"))
button.setIconSize(QSize(14, 14))
button.clicked.connect(self.on_clicked)
self._button = button
self._widget = button
return button
def update(self) -> None:
...
def connect_signals(self) -> None:
...
def disconnect_signals(self) -> None:
...
Registration¶
PluginEntry(
type_name="statusbar",
name="device_connection",
import_path="my_package.statusbar:DeviceConnectionStatus",
),
Position Options¶
Position |
Description |
|---|---|
|
Routed to the left-side container ( |
|
Routed to the right-side container ( |
|
Same as |
Within each side, plugins are sorted by priority (lower = further
left). A small uniform gap separates adjacent items; there are no
inter-item separator notches and no trailing separator.
Priority Guidelines¶
Lower priority values appear further left:
Priority Range |
Typical Use |
|---|---|
0-25 |
Critical system status |
25-50 |
Connection/authentication |
50-75 |
Feature status |
75-100 |
Informational |
100+ |
Low priority |
Hiding a Plugin¶
Plugins can hide themselves when there is nothing to show:
def update(self) -> None:
if not self._has_anything_to_say():
self.set_visible(False)
return
self.set_visible(True)
self.set_text(self._render())
Hidden plugins are removed from the layout flow — the surrounding items close up cleanly, with no leftover gap or trailing separator.
Built-in Status Plugins¶
Lightfall includes these status bar plugins:
Plugin |
Description |
|---|---|
|
Background tasks and scan progress (hides when idle) |
|
Current user information |
|
Authentication state |
|
Network connection |
|
Tiled data service status |
|
ALS synchrotron beam current and availability |