Adding a scrollbar to VStack or VGrid w omni.ui and python but so the 'Add' button still adds new dropdown rows

Hi, I wrote a script that builds a window with dropdowns (in USD Composer 2023.2, scripting it as an extension in python) and would like to add a scrollbar to the middle section with the dropdown rows:

‘Add’ button adds a new row with dropdowns.

I’d like to script this:


(this is a result of using ui.ScrollingFrame but now the ‘Add’ button replaces the existing dropdown row continuously :D , see “I’ve tried” below). Any suggestions?

My code:

def on_startup(self, ext_id):
        '''Preset hstack id (same id for dropdowns in each preset hstack)'''
        self._hstack_id = 0

        '''Holding presets hstacks and their dropdowns'''
        self.hstacks_dict : Dict[ui.HStack] = {}

        '''Constructing main window'''
        self._window = ui.Window("Rendering Selector", width=900, height=270)
        with self._window.frame:
            with ui.VStack():
                with ui.HGrid(height=50): # I'm calling them hstacks everywhere in comments, ended up replacing ui.HStacks with ui.HGrid 
                    ui.Button("Add", clicked_fn=self.add_dropdowns_hstack, width=self.BUTTON_WIDTH, height=self.BUTTON_HEIGHT)
                    ui.Button("Remove", clicked_fn=self.remove_dropdowns_hstack, width=self.BUTTON_WIDTH, height=self.BUTTON_HEIGHT)
                    ui.Button("Duplicate", clicked_fn=self.duplicate_dropdowns_hstack, width=self.BUTTON_WIDTH, height=self.BUTTON_HEIGHT)

                '''Constructing frame with presets HStacks'''
                self.selections_vstack = ui.VGrid(spacing=20)

                '''Adds first dropdowns hstack to self.selections_vstack'''
                self.add_dropdowns_hstack()

                with ui.HGrid(height=50):
                    ui.Button("Select All", clicked_fn=self.select_all_checkboxes, width=self.BUTTON_WIDTH, height=self.BUTTON_HEIGHT)
                    ui.Button("Deselect All", clicked_fn=self.deselect_all_checkboxes, width=self.BUTTON_WIDTH, height=self.BUTTON_HEIGHT)
                    ui.Button("Render Selected", clicked_fn=self.duplicate_dropdowns_hstack, width=self.BUTTON_WIDTH, height=self.BUTTON_HEIGHT)


    def add_or_duplicate_dropdowns(self, save_state_id, interior_color_id, exterior_color_id, trim_id, camera_id, checkbox_bool):
        
        with self.selections_vstack: 
            self.hstacks_dict[self._hstack_id] = {}
            self.hstacks_dict[self._hstack_id]["hstack_object"] = ui.HStack(height=30) 
            self.hstacks_dict[self._hstack_id]["dropdowns_dict"] = {}
            with self.hstacks_dict[self._hstack_id]["hstack_object"]:                
                dropdowns_dict = self.hstacks_dict[self._hstack_id]["dropdowns_dict"]
                dropdowns_dict["radiobutton"] = ui.RadioButton(identifier=f"{self._hstack_id}", 
                                                               radio_collection=self.collection, 
                                                               width=30, 
                                                               height=30, 
                                                               clicked_fn=self.get_selected_radiobutton_int)
                dropdowns_dict["save_state"] = ui.ComboBox(save_state_id, 
                                                           *self.save_state_list, 
                                                           width=self.DROPDOWN_WIDTH) 
                dropdowns_dict["interior_color"] = ui.ComboBox(interior_color_id, 
                                                               *self.interior_color_list, 
                                                               width=self.DROPDOWN_WIDTH)
                dropdowns_dict["exterior_color"] = ui.ComboBox(exterior_color_id, 
                                                               *self.exterior_color_list, 
                                                               width=self.DROPDOWN_WIDTH)                                      
                dropdowns_dict["trim"] = ui.ComboBox(trim_id, 
                                                     *self.trim_list, 
                                                     width=self.DROPDOWN_WIDTH)                                  
                dropdowns_dict["camera"] = ui.ComboBox(camera_id, 
                                                       *self.camera_list, 
                                                       width=self.DROPDOWN_WIDTH)                              
                dropdowns_dict["checkbox"] = ui.CheckBox(identifier=f"{self._hstack_id}", 
                                                         alignment=ui.Alignment.BOTTOM)
                dropdowns_dict["checkbox"].model.set_value(checkbox_bool)


    def add_dropdowns_hstack(self):
        self.add_or_duplicate_dropdowns(save_state_id=0, 
                                        interior_color_id=0, 
                                        exterior_color_id=0, 
                                        trim_id=0, 
                                        camera_id=0,
                                        checkbox_bool=False)
        self._hstack_id += 1

I’ve tried:

  • adding “auto_resize=True” to self._window - it works auto-resizing the window, but that’s not the same as a scrollbar
  • using ui.ScrollingFrame: - replacing it as my self.selections_vstack instead of a ui.VGrid curiously only allows me to have 1 child/row with dropdowns. Ussing the “Add” button always replaces the 1st row with a new row with default settings in the dropdowns:
self.selections_vstack = ui.ScrollingFrame(height=200, 
                                                           width=800,
                                                            horizontal_scrollbar_policy=ui.ScrollBarPolicy.SCROLLBAR_ALWAYS_OFF,
                                                            vertical_scrollbar_policy=ui.ScrollBarPolicy.SCROLLBAR_ALWAYS_ON,
                                                            style={"ScrollingFrame": {
                                                                "background_color": cl.set_shade("default"),
                                                                "scrollbar_size": 15,
                                                            }})
  • using ui.TreeView but I haven’t been successful in implementing a model that would work with UI (ui.ComboBox, ui.RadioButton etc) objects.

Here’s a snippet on how you can do it with VStack and ScrollingFrame. Hope it gives you some insight on how to implement it on your end.

    def on_startup(self, ext_id):
        print("[company.hello.world] company hello world startup")

        self._window = ui.Window("My Window", width=300, height=300)
        with self._window.frame:
            with ui.VStack(spacing=0):
                with ui.HStack(height=0): 
                    ui.Button("Add", clicked_fn=self.add_dropdowns, height=0)
                with ui.ScrollingFrame():
                    self.dropdown_list = ui.VStack()

    def add_dropdowns(self):
        hstack = ui.HStack()
        with hstack:
            ui.ComboBox()
            ui.ComboBox()
            ui.ComboBox()
        self.dropdown_list.add_child(hstack)
1 Like

Thanks for your solution kit-nvidia-Jen, I used this approach of appending to ScrollingFrame’s only Vstack child with .add_child(hstack) and it works! I was using the .add_child differently before and that kept overwriting my Vstack with a new hstack every time.

In the meantime I solved this by rebuilding the ScrollingFrame on each change with existing hstacks and their selected options which is a whole thing, this is so much easier :D

1 Like

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