Renamed the notion of handler classes to plugin classes in the Studio Python API

This change refactors the Python API to use the term "plugins" instead of "handlers" for classes containing decorated handler methods. The term handler is still used for an annotated method of a plugin class that handles a specific message type. Also improved some documentation.

PiperOrigin-RevId: 962150099
Change-Id: I34a8cc410cd784b088605490cfa3baccea5c9e71
This commit is contained in:
Matija Kecman
2026-08-10 07:49:47 -07:00
committed by Copybara-Service
parent ab1af24911
commit f1c8d3a58f
10 changed files with 84 additions and 68 deletions
@@ -20,8 +20,8 @@ import enum
from typing import Any
import mujoco
from mujoco.experimental.studio import endpoints
from mujoco.experimental.studio import handler_registry
from mujoco.experimental.studio import messages
from mujoco.experimental.studio import plugin_registry
from mujoco.experimental.studio import ux
import numpy as np
@@ -88,7 +88,7 @@ class ViewToSim:
class ViewerInitEvent(messages.Event):
"""Lifecycle event dispatched once when the concrete Viewer is initialized.
Handlers that need access to the Viewer should handle this event
Plugins that need access to the Viewer should handle this event
and cache the reference.
"""
@@ -98,7 +98,7 @@ class ViewerInitEvent(messages.Event):
class Viewer(abc.ABC):
"""Base class for any viewer.
Owns the communication endpoint, handler registry and core visualization
Owns the communication endpoint, plugin registry and core visualization
objects. The application is rendered by calling ``sync()``.
"""
@@ -109,7 +109,7 @@ class Viewer(abc.ABC):
*,
model: mujoco.MjModel | None = None,
model_path: str = '',
handlers: list[Any] | None = None,
plugins: list[Any] | None = None,
camera: mujoco.MjvCamera | None = None,
vis_options: mujoco.MjvOption | None = None,
perturb: mujoco.MjvPerturb | None = None,
@@ -125,7 +125,7 @@ class Viewer(abc.ABC):
an empty MjSpec. The Viewer deep-copies this model and creates its own
MjData.
model_path: Optional path to the model file.
handlers: Optional list of handler instances for viewer-side processing.
plugins: Optional list of plugin instances for viewer-side processing.
camera: Camera parameters. Internal object is created if None.
vis_options: Visualization options. Internal object is created if None.
perturb: Perturbation parameters. Internal object is created if None.
@@ -158,9 +158,9 @@ class Viewer(abc.ABC):
# Initted to match mujoco/src/engine/engine_vis_init.c
self.render_flags.flags = [1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1]
# Handler infrastructure.
all_handlers = [self] + list(handlers or [])
self.handlers = handler_registry.HandlerRegistry(all_handlers)
# Plugin infrastructure.
all_plugins = [self] + list(plugins or [])
self.plugins = plugin_registry.PluginRegistry(all_plugins)
def close(self) -> None:
"""Closes the viewer, sends an exit event and shuts down the endpoint."""
@@ -198,7 +198,7 @@ class Viewer(abc.ABC):
def dispatch(self, message: messages.Message) -> None:
"""Dispatches a message to registered handlers in priority order."""
self.handlers.dispatch(message)
self.plugins.dispatch(message)
def load_model(self, model: mujoco.MjModel, model_path: str = '') -> None:
"""Deep-copies a model and creates fresh data for the viewer."""
@@ -257,7 +257,7 @@ def run_viewer_loop(viewer: Viewer) -> None:
On exit, closes the viewer (which sends an ExitEvent to the sim side).
Args:
viewer: A Viewer that owns the endpoint and handler registry.
viewer: A Viewer that owns the endpoint and plugin registry.
"""
while True:
# Get the next frame; this also gets the frame's mouse/keyboard events.