Stage Preview Window

In order to display the Renderer’s output, we’ll need to have an omni.ui.window that will host our widget. We’re interested in this level primarily to demonstrate the ability to create a ViewportWidget that isn’t tied to the application’s main UsdContext; rather you can provide a usd_context_name string argument and the StagePreviewWindow will either reuse an existing UsdContext with that name, or create a new one.

Keep in mind a ViewportWidget is currently tied to exactly one UsdContext for its lifetime. If you wanted to change the UsdContext you are viewing, you would need to hide and show a new ViewportWidget based on which context you want to be displayed.

Context creation

The first step we take is to check whether the named UsdContext exists, and if not, create it.

# We may be given an already valid context, or we'll be creating and managing it ourselves
usd_context = omni.usd.get_context(usd_context_name)
if not usd_context:
    self.__usd_context_name = usd_context_name
    self.__usd_context = omni.usd.create_context(usd_context_name)
else:
    self.__usd_context_name = None
    self.__usd_context = None

Viewport creation

Once we know a valid context with usd_context_name exists, we create the omni.ui.Window passing along the window size and window flags. The omni.ui documentation has more in depth description of what an omni.ui.Window is and the arguments for it’s creation.

After the omni.ui.Window has been created, we’ll finally be able to create the StagePreviewWidget, passing along the usd_context_name. We do this within the context of the omni.ui.Window.frame property, and forward any additional arguments to the StagePreviewWidget constructor.

super().__init__(title, width=window_width, height=window_height, flags=flags)
with self.frame:
    self.__preview_viewport = StagePreviewWidget(usd_context_name, *vp_args, **vp_kw_args)

Full code

class StagePreviewWindow(ui.Window):
    def __init__(self, title: str, usd_context_name: str = '', window_width: int = 1280, window_height: int = 720 + 20, flags: int = ui.WINDOW_FLAGS_NO_SCROLLBAR, *vp_args, **vp_kw_args):
        """StagePreviewWindow contructor
        Args:
            title (str): The name of the Window.
            usd_context_name (str): The name of a UsdContext this Viewport will be viewing.
            window_width(int): The width of the Window.
            window_height(int): The height of the Window.
            flags(int): ui.WINDOW flags to use for the Window.
            *vp_args, **vp_kw_args: Additional arguments to pass to the StagePreviewWidget
        """
        # We may be given an already valid context, or we'll be creating and managing it ourselves
        usd_context = omni.usd.get_context(usd_context_name)
        if not usd_context:
            self.__usd_context_name = usd_context_name
            self.__usd_context = omni.usd.create_context(usd_context_name)
        else:
            self.__usd_context_name = None
            self.__usd_context = None

        super().__init__(title, width=window_width, height=window_height, flags=flags)
        with self.frame:
            self.__preview_viewport = StagePreviewWidget(usd_context_name, *vp_args, **vp_kw_args)

    def __del__(self):
        self.destroy()

    @property
    def preview_viewport(self):
        return self.__preview_viewport

    def open_stage(self, file_path: str):
        # Reach into the StagePreviewWidget and get the viewport where we can retrieve the usd_context or usd_context_name
        self.__preview_viewport.viewport_api.usd_context.open_stage(file_path)
        # the Viewport doesn't have any idea of omni.ui.scene so give the models a sync after open (camera may have changed)
        self.__preview_viewport.sync_models()

    def destroy(self):
        if self.__preview_viewport:
            self.__preview_viewport.destroy()
            self.__preview_viewport = None
        if self.__usd_context:
            # We can't fully tear down everything yet, so just clear out any active stage
            self.__usd_context.remove_all_hydra_engines()
            # self.__usd_context = None
            # omni.usd.destroy_context(self.__usd_context_name)
        super().destroy()