Troubles making Extension with New Viewport

Hi there.

I’m currently trying to get a separate viewport up and running in my extension, and I’ve set up the basic one described in the Omniverse Viewport Next tutorial that loads up with Code. I couldn’t find a link to the tutorial online, but it’s the one that looks like this:

Right now, I have an extension that creates an instance of the “StagePreviewWindow” class, which itself has an instance of the “StagePreviewWidget” class, as suggested in the code blocks provided. Essentially, all I’ve done is copy the tutorial code presented and resolve all the missing imports.

Right now I have a second viewport that I can manipulate mostly normally (scroll wheel doesn’t work for some reason; instead I have to press ALT + RMB), but I’m running into issues adding additional features.

  1. I’d like to add buttons on top of the viewport, but adding them to the widget’s Z-Stack doesn’t seem to be doing the trick. EDIT: Adding them to a ZStack attached to the Window (not the widget) seemed to work perfectly, so this one is solved.
  2. I’d like the resolution of the viewport to update with the window resizing, and for the scene view to remain centered in the display as in the standard viewport.
  3. Ideally I’d like to get scrolling to work as it does in the original viewport.
  4. Currently I have the extension set to auto-load, but the window doesn’t appear until I disable and re-enable the extension; ideally I’d like it to appear on start-up.

I apologize if these things have already been addressed in other forum posts; I did a quick search and couldn’t find anything relevant, but I may have missed something. Thanks!

EDIT: I wanted to add that I have tried to look through the standard Viewport extension, but because it pulls content from the legacy viewport, it is difficult to determine what exactly it’s doing to achieve the functionality described above.

Hi @jlw387. I would start with omni.kit.viewport.utility.create_viewport_window()

Here’s a snippet to get you started:

import omni.kit.viewport.utility as vp_utils
import omni.ui as ui

vp_window = vp_utils.create_viewport_window("My Viewport")
vp_window.viewport_api.fill_frame = True
with vp_window.get_frame(ext_id): # Use some unique id here. We typically use Extension ID.
    with ui.ZStack():
        with ui.HStack():
            ui.Spacer()
            ui.Button("Hello World")
            ui.Spacer()

I think that should answer most of your questions. For #4, if you create your window in on_startup() it should work. Let me know if that’s not the case.

Hi @mati-nvidia,

I switched my code to create the viewport the way you suggest above, and it does solve the camera controls/resolution size problems, but now it has the buttons that the standard viewport has for camera control/rendering control/etc, and these are visually on top of the buttons/menus I’m trying to add on top of the viewport (see attached pic).

If I recall correctly, each of these buttons is located in its own extension separate extensions, so unless I can call some method on the viewport to remove those buttons, it seems like it’ll be difficult to stop them from being added (this was the reason I had started with the StagePreview extension code originally). EDIT: I’ve also now noticed that I can’t change the slider’s position with this new viewport, though it’s possible I need to resolve some other things that broke when I switched to using create_viewport_window().

To address 4) I do create the window in on_startup(), and yet it doesn’t load the window until I refresh the extension (disable/re-enable). I’ve included the relevant code from a few of the files for your reference, but I can also include the rest of the code if that would be helpful.

extension.py

class SketchInContextExtension(omni.ext.IExt):
    def __init__(self):
        self.__viewport_window = None
    
    # ext_id is current extension id. It can be used with extension manager to query additional information, like where
    # this extension is located on filesystem.
    def on_startup(self, ext_id):

        self._context_name = "SketchContext"
        check_context = omni.usd.get_context(self._context_name)

        if not check_context:
            self._context = omni.usd.create_context(self._context_name)
        else:
            self._context = check_context

        self.__viewport_window = SketchViewportWindow("Sketch In Context", e_id = ext_id, width=1280, height=720 + 20, resolution='fill_frame' or (1280, 760))
        
        # Additional irrelevant startup code here

sketch_viewport_window.py

"""Modified from NVIDIA's StagePreviewWindow"""
class SketchViewportWindow(): 

    # Note, the constructor still has some remnants of the StagePreviewWindow code, but they are unused in the current extension
    def __init__(self, title: str, usd_context_name: str = '', e_id = None, window_width: int = 1280, window_height: int = 720 + 20, flags: int = ui.WINDOW_FLAGS_NO_SCROLLBAR, *vp_args, **vp_kw_args):
     
        # NVIDIA: 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)
        self.__usd_context_name = usd_context_name
        if not usd_context:
            self.__usd_context = omni.usd.create_context(usd_context_name)
        else:
            # This else statement seems off to me; if the context already exists, it seems odd to me that we set the instance variables to None and not the existing context
            self.__usd_context_name = None
            self.__usd_context = None

        labels_list = [ <LABELS REMOVED FOR BREVITY> ]

        # This is the slider shown in the image 
        self.__proj_slider_wrapper = SliderWrapper(labels_list)

        # These are the lines recommended in the reply above
        self.__vp_window = vp_utils.create_viewport_window("Sketch in Context")
        self.__vp_window.viewport_api.fill_frame = True

        with self.__vp_window.get_frame(e_id):
            with ui.ZStack():
                with ui.HStack():
                    # Here we call the function to set the slider up instead of the "Hello World" button suggested above, but the effect is the same
                    self.__proj_slider_wrapper.set_up_slider()

Looking back at my code, I noticed that I forgot to pass in the context name to the window constructor, but that shouldn’t be causing the problematic behavior. I’ll post an update if fixing it resolves anything.

Appreciate the help!

Update on this; I switched back to the original code based on the StagePreview extension, and I’ve now resolved 2. The issue was that I had some improperly named variables (width and height instead of window_width and window_height) that went unnoticed due to the inclusion of*vp_args and **vp_kw_args in the constructor for the window object. 3) and 4) are still unsolved.

HI @jlw387. I got some answers from dev team. In the future, ViewportWindow will allow for more configuration so you could hide the menubar, for example. For now, you’re headed down the right path creating your own ViewportWindow from the ground up like Stage Preview does. ViewportWindow is rather complex though so it’ll take some leg work to implement all or most of the features. For scroll wheel zoom, you can find an example here that it is handled differently than the other camera manipulator operations: omni.kit.viewport.window.events.delegate

In regards to the window not showing up on app startup. This doesn’t appear to be working anymore in Code-2202.2. I’ve logged OM-66153 and hopefully we can get it fixed for the next release of Code.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.