ToolBar dock to viewport when extension loads

I’m writing a Python extension (in v106.3) that creates a toolbar and docks it to the top of the viewport when the extension loads. Here is the relevant snippet of code:

class MyToolBarExtension(omni.ext.IExt):
    def on_startup(self, _ext_id):
        self._toolbar = omni.ui.ToolBar("My ToolBar")

        viewport = omni.ui.Workspace.get_window("Viewport")
        if viewport is not None:
            print("Viewport found, docking.")
            # I get a warning that `dock_in_window` is deprecated, but the `dock_in` function it recommends doesn't seem to work at all
            self._toolbar.dock_in_window("Viewport", omni.ui.DockPosition.TOP)

The problem is, even though the viewport does seem to exist by the time this on_startup function is called (i.e. “Viewport found, docking.” prints in the console), the toolbar still doesn’t dock. However, if I reload the extension, then it docks. How do I get it to dock on load?

I’ve tried the following variants:

class MyToolBarExtension(omni.ext.IExt):
    def on_startup(self, _ext_id):
        self._toolbar = omni.ui.ToolBar("My ToolBar")

        self._toolbar.deferred_dock_in("Viewport")

That code never results in the toolbar docking, even when reloading the extension. Same with the following:

class MyToolBarExtension(omni.ext.IExt):
    def on_startup(self, _ext_id):
        self._toolbar = omni.ui.ToolBar("My ToolBar")

        omni.ui.Workspace.set_window_created_callback(self.on_window_created)

    def on_window_created(self, window):
        if window.title == "Viewport":
            print("Viewport created, now docking.")
            self._toolbar.dock_in_window("Viewport", omni.ui.DockPosition.TOP)

The viewport seems to generally be created by the time this extension loads, so the if statement there never triggers.

Any thoughts? Thanks!

Thanks for your question. I understand. Let me ask the dev team and get back to you.

Hi there,

Try to make an async function

    async def _dock(self) -> None:
        """Docking the panel to the right of viewport"""
        await omni.kit.app.get_app().next_update_async()
        # self._window is just a panel in this case inherited from omni.ui.Window)
        self._window.dock_in(omni.ui.Workspace.get_window("Viewport"), omni.ui.DockPosition.RIGHT, 0.25)

Inside on_startup

asyncio.ensure_future(self._dock())

Hi, thanks for pointing me in this direction. This has gotten me somewhere, but it’s pretty odd behavior.

If I follow your code and wait for one update frame, it then looks like the top example here. I.e. sort of docked, but not quite nicely in place yet. (I expect it to look like the bottom image, i.e. slotted above the viewport and to the right of the main toolbar, instead of over the top of both of them.)

So I thought maybe it’d be worth trying to wait for more than one frame, in case the viewport window just isn’t ready yet. If I modify the above code a bit:

async def _dock(self):
    for i in range(2):
        await omni.kit.app.get_app().next_update_async()
    self._toolbar.dock_in(omni.ui.Workspace.get_window("Viewport"), omni.ui.DockPosition.TOP)

I start to get slightly different results. Waiting 2 frames puts it in the same place but smaller; waiting 3-4 frames leaves it undocked again; but waiting 5+ frames finally puts it where I expect it to go.

All this to say, while I can live with the solution of just waiting a bunch of frames, I would love to know if there’s a more concrete way to do this. I wonder what is happening in those frames that gives the docking system such odd behavior?

I will report this back and see, but it sounds like you are pretty close.