Bug with model references during window destruction?

The simple Extension + Window code below (just replace an extension.py with it) is reloaded fine by Kit, after the file is saved from an editor, and the window displays the two created controls.

However, if the line with #ATTN is commented-out, after saving the python file twice, the extension window remains blank until Kit is shutdown. Even restoring the commented-out line no longer works, which is quite annoying.

I believe this happens because the reference to the ui.FloatSlider’s model somehow leaves the Window hanging, and after an extension reload, MyWindow._build_fn is again never called.

Is this a Kit issue that you plan to fix or is it “by design” behavior?

Thanks

from functools import partial

import omni.ext
import omni.ui as ui


class MyWindow(ui.Window):
    def __init__(self, title: str = None, delegate=None, **kwargs):
        print("win.__init__")

        super().__init__(title, **kwargs)

        self.frame.set_build_fn(self._build_fn)


    def destroy(self):
        print("win.destroy")

        self.proj_scale_model = None   #ATTN: comment this line and save extension twice - window controls no longer appear

        super().destroy()


    def _build_fn(self):
        print(f"win._build_fn {self.visible}")

        # Called to build the UI once the window is visible
        with ui.ScrollingFrame():
            with ui.VStack(height=0):
                self._sel_button = ui.Label("My Label")

                self.proj_scale_model = ui.FloatSlider(min=0, max=100).model
                self.proj_scale_model.add_value_changed_fn( self.act )

    def act(self, m):
        print("act", m.as_float)





class MyExtension(omni.ext.IExt):
    def on_startup(self, ext_id):
        print("MyExtension startup")

        self._window = None

        ui.Workspace.set_show_window_fn("Name", partial(self.show_window, None))
        ui.Workspace.show_window("Name")


    def show_window(self, menu, value):
        print("ext.show_window", value, self._window)
        self._window = MyWindow("Name", width=300, height=300)


    def on_shutdown(self):
        print("MyExtension shutdown")
        ui.Workspace.set_show_window_fn("Name", None)

        if self._window:
            self._window.destroy()
            self._window = None

Hi @jordio. You need to clean up references to UI objects on extension shutdown. You’ll see examples in many bundled Kit extensions that unsubscribe to events, call destroy(), and set variables referring to UI objects to None. This may be the case for models too, but I’ll double-check with the dev team.

Turns out a reference to a UI object is being held:

# self.act is not removed from subscription. A MyWindow reference lives on because act() is a member function.
self.proj_scale_model.add_value_changed_fn( self.act )

You need to use remove_value_changed_fn when you destroy. Alternatively, you may find subscribe_value_changed_fn easier to use.

Hi @mati-nvidia, thanks!

Using add/remove_value_changed_fn() does work, but it adds complexity to the code. An easier solution is to just keep a reference to the control (which will probably be needed anyway) and always reference the model as control.model. Then in destroy(), just set control = None and no references are held.

Method subscribe_value_changed_fn has the advantage of just needing to set the returned handle to None in destroy (instead of having to call remove* as in add*), but just setting the control reference to None works and is simpler.

For fellow developers watching this thread, these held references can cause very weird problems like zombie empty windows that don’t go away when the extension is reloaded, the only way is to restart Kit. So always look for created controls that are not being set to None in destroy(), if such weirdness appears!

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