Source code for isaaclab.app.settings_manager
# Copyright (c) 2022-2026, The Isaac Lab Project Developers (https://github.com/isaac-sim/IsaacLab/blob/main/CONTRIBUTORS.md).
# All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
"""Settings manager for Isaac Lab that works with or without Omniverse carb.settings.
This module provides a unified settings interface that can work in two modes:
1. Omniverse mode: Uses carb.settings when SimulationApp is launched
2. Standalone mode: Uses pure Python dictionary when running without Omniverse
This allows Isaac Lab to run visualizers like Rerun and Newton without requiring
the full Omniverse/SimulationApp stack.
"""
import sys
from typing import Any
# Key for storing singleton in sys.modules to survive module reloads (e.g., from Hydra)
_SINGLETON_KEY = "__isaaclab_settings_manager_singleton__"
[docs]
class SettingsManager:
"""A settings manager that provides a carb.settings-like interface without requiring Omniverse.
This class can work in two modes:
- Standalone mode: Uses a Python dictionary to store settings
- Omniverse mode: Delegates to carb.settings when available
The interface is designed to be compatible with the carb.settings API.
This is implemented as a singleton to ensure only one instance exists across the application,
even when used in different execution contexts (e.g., Hydra). The singleton is stored in
sys.modules to survive module reloads.
"""
[docs]
def __new__(cls):
"""Singleton pattern - always return the same instance, stored in sys.modules to survive reloads."""
# Check if instance exists in sys.modules (survives module reloads)
instance = sys.modules.get(_SINGLETON_KEY)
if instance is None:
instance = super().__new__(cls)
sys.modules[_SINGLETON_KEY] = instance
# Mark that this instance needs initialization
instance._needs_init = True
return instance
[docs]
def __init__(self):
"""Initialize the settings manager (only runs once due to singleton pattern)."""
# Check if this instance needs initialization
needs_init = getattr(self, "_needs_init", False)
if not needs_init:
return
self._standalone_settings: dict[str, Any] = {}
self._carb_settings = None
self._use_carb = False
self._needs_init = False
@classmethod
def instance(cls) -> "SettingsManager":
"""Get the singleton instance of the settings manager.
Returns:
The singleton SettingsManager instance
"""
# Get instance from sys.modules (survives module reloads)
instance = sys.modules.get(_SINGLETON_KEY)
if instance is None:
instance = cls()
return instance
def initialize_carb_settings(self):
"""Initialize carb.settings if SimulationApp has been launched.
This should be called after SimulationApp is created to enable
Omniverse mode. If not called, the manager operates in standalone mode.
"""
try:
import carb
self._carb_settings = carb.settings.get_settings()
self._use_carb = True
except (ImportError, AttributeError):
# carb not available or SimulationApp not launched - use standalone mode
self._use_carb = False
def set(self, path: str, value: Any) -> None:
"""Set a setting value at the given path.
Args:
path: The settings path (e.g., "/isaaclab/render/offscreen")
value: The value to set
"""
if self._use_carb and self._carb_settings is not None:
# Delegate to carb.settings
if isinstance(value, bool):
self._carb_settings.set_bool(path, value)
elif isinstance(value, int):
self._carb_settings.set_int(path, value)
elif isinstance(value, float):
self._carb_settings.set_float(path, value)
elif isinstance(value, str):
self._carb_settings.set_string(path, value)
else:
# For other types, try generic set
self._carb_settings.set(path, value)
else:
# Standalone mode - use dictionary
self._standalone_settings[path] = value
def get(self, path: str, default: Any = None) -> Any:
"""Get a setting value at the given path.
Args:
path: The settings path (e.g., "/isaaclab/render/offscreen")
default: Default value to return if path doesn't exist
Returns:
The value at the path, or default if not found
"""
if self._use_carb and self._carb_settings is not None:
# Delegate to carb.settings
value = self._carb_settings.get(path)
return value if value is not None else default
else:
# Standalone mode - use dictionary
return self._standalone_settings.get(path, default)
def set_bool(self, path: str, value: bool) -> None:
"""Set a boolean setting value.
Args:
path: The settings path
value: The boolean value to set
"""
self.set(path, value)
def set_int(self, path: str, value: int) -> None:
"""Set an integer setting value.
Args:
path: The settings path
value: The integer value to set
"""
self.set(path, value)
def set_float(self, path: str, value: float) -> None:
"""Set a float setting value.
Args:
path: The settings path
value: The float value to set
"""
self.set(path, value)
def set_string(self, path: str, value: str) -> None:
"""Set a string setting value.
Args:
path: The settings path
value: The string value to set
"""
self.set(path, value)
@property
def is_omniverse_mode(self) -> bool:
"""Check if the settings manager is using carb.settings (Omniverse mode).
Returns:
True if using carb.settings, False if using standalone mode
"""
return self._use_carb
def get_settings_manager() -> SettingsManager:
"""Get the global settings manager instance.
The SettingsManager is implemented as a singleton, so this function
always returns the same instance. The singleton is stored in sys.modules
to survive module reloads (e.g., from Hydra).
Returns:
The global SettingsManager instance
"""
# Get instance from sys.modules (survives module reloads)
instance = sys.modules.get(_SINGLETON_KEY)
if instance is None:
instance = SettingsManager()
return instance
def initialize_carb_settings():
"""Initialize carb.settings integration for the global settings manager.
This should be called after SimulationApp is created to enable
Omniverse mode for the global settings manager.
"""
manager = get_settings_manager()
manager.initialize_carb_settings()