Skip to content

11_Core_Nervous_System

BioPro CI Bot edited this page May 16, 2026 · 1 revision

🧠 architecture: The Core Nervous System (Event Bus)

BioPro is built on an Event-Driven Architecture (EDA). This means the different parts of the app (like the Plugin Store, the Workspace, and the Core Storage) don't need to know about each other's internals. They just "shout" events, and anyone interested "listens."


🏗 Why Decoupled?

Imagine if the Plugin Store had to manually tell the Workspace Window to refresh every time a plugin was installed. If we added a third window (like a Plugin Manager), we'd have to go back and update the Store's code again.

With the Event Bus, the Store just says PLUGIN_INSTALLED, and any window that exists can choose to react to it.

graph LR
    P[📦 Plugin Store] -->|emit: PLUGIN_INSTALLED| EB((🧠 Event Bus))
    EB -->|notify| W1[🪟 Workspace Window]
    EB -->|notify| W2[🏠 Hub Window]
    EB -->|notify| L[📝 Logger]
Loading

🛠 Using the Event Bus (biopro.core.event_bus)

The system-wide bus is a singleton instance available as event_bus.

1. The BioProEvent Registry

All events are defined in a central Enum. This prevents "Magic String" bugs and allows for autocompletion.

Event Triggered When... Typical Payloads
PLUGIN_INSTALLED A new module is successfully added to disk. plugin_id: str
PLUGIN_REMOVED A module is deleted. plugin_id: str
PROJECT_LOADED A .biopro file is successfully opened. path: str
THEME_CHANGED The global UI theme is swapped. theme_name: str

2. Subscribing to Events

UI components typically subscribe during their __init__.

from biopro.core.event_bus import event_bus, BioProEvent

class MyDashboard(QWidget):
    def __init__(self):
        super().__init__()
        # Listen for new installs
        event_bus.subscribe(BioProEvent.PLUGIN_INSTALLED, self._on_plugin_added)

    def _on_plugin_added(self, plugin_id: str):
        print(f"I see you added {plugin_id}! Refreshing my UI...")
        self.refresh()

3. Emitting Events

Emitting is safe from any thread. BioPro uses PyQt6's signal queuing to ensure callbacks are always executed on the Main UI Thread.

def install_plugin(id):
    # (Perform heavy download and move files...)
    # Now notify the world:
    event_bus.emit(BioProEvent.PLUGIN_INSTALLED, id)

⏺️ The Diagnostic Engine (The Black Box)

BioPro's "Nervous System" includes a specialized Diagnostic Engine (biopro.core.diagnostics). It functions like a flight recorder (Black Box) for the application.

1. In-Memory History

The engine maintains a memory-resident buffer of the last 100 system events (logs, network requests, UI transitions). This history is always captured, even if logging to disk is disabled.

2. Global Exception Hook

The core installs a global sys.excepthook. When an unhandled crash occurs:

  1. The Black Box stops recording.
  2. The crash state + 100 pre-crash events are bundled into a structured JSON report.
  3. The ERROR_OCCURRED event is emitted.
  4. The Premium Error UI is triggered to show the user exactly what happened.

3. Integrated Plugin Logging

All plugins using self.logger (via biopro.sdk.utils.logging) are automatically piped into the Black Box. This means developers can see the exact sequence of plugin-level actions that led to a system failure.


🔍 Technical Deep Dive: Thread-Safe Dispatch

The EventManager uses a special internal pyqtSignal.

When you call emit() from a background worker thread, the signal is queued by the Qt Event Loop. It is only dispatched once the Main Thread is free. This prevents "Access Violation" crashes that happen if a background thread tries to update a UI widget directly.

class EventManager(QObject):
    _internal_bus = pyqtSignal(BioProEvent, tuple, dict)

    def emit(self, event_type, *args, **kwargs):
        # Queues the work for the UI thread automatically
        self._internal_bus.emit(event_type, args, kwargs)

Tip

Memory Safety: Always unsubscribe if your component is destroyed before the app closes to prevent memory leaks!

Clone this wiki locally