Update ETO form after user has changed input data using the ETO form in real time?

Hi everyone

so im looking to make my ETO form update when the user changes something about the input data

in this example, I would like mutiple things to update the eto form in real time:

  1. when i hit the purge button, I have that bound to a method that purges the layers, obviously. but i also have the number of rows on the dynamic eto layout conform to the number of layers i have. whats happening is the button is purging the layers but the eto form doesnt update

  2. I have a row for when the user is feeling lazy and doesnt want to asign objects to a layer but i also have a button that would ideally add another row of laziness. the way i have this in code is that all buttons and the number of rows is associated to a variable that is an interger and when the add select line is clicked, it adds 1 to that varible (it starts at 1 but when i manually chage it to 2 it works and there are 2 ‘select’ rows.

image

sorry about the essay, i hope this makes sense, im quite new to scripting and i only starting reading up about ETO forms 2 days ago so any help will be massivley apreciated.

is this possible? i would be happy with closing and opening the eto form everytime the eto form needs to be updated. my code is below. Thank you massivley in advance!

P.S. if you see anything in my code that is just bad in general, please let me know im still learning :slight_smile:

import rhinoscriptsyntax as rs
import Rhino.UI
import Eto.Drawing as drawing
import Eto.Forms as forms

class Material_assigner(forms.Dialog[bool]):
    
    def __init__(self, material_names):##setting up the eto form
        self.Title = "Material Assigner"
        self.Padding = drawing.Padding(10)
        self.Resizable = False
        self.layer_names = rs.LayerNames()
            
          
        ### normal layer controls
            
        self.checkboxes = []
        for i in self.layer_names:
            checkbox = forms.CheckBox( Text = "") ##check boxes (always true to assume u want to calculate every layer
            checkbox.Checked = True
            self.checkboxes.append(checkbox)
        
        self.dropdowns = []
        for i in self.layer_names:
            dropdown = forms.ComboBox() ##dropdown menu you can edit
            dropdown.DataStore = material_names
            dropdown.SelectedIndex = 0
            self.dropdowns.append(dropdown)
       
       
        
        self.DefaultButton = forms.Button(Text = 'OK')
        self.DefaultButton.Click += self.OnOKButtonClick
        self.AbortButton = forms.Button(Text = 'Cancel')
        self.AbortButton.Click += self.OnCloseButtonClick
        
        
        self.addselectbutton = forms.Button(Text = 'Add select line')
        self.addselectbutton.Click += self.addselectlevel
        
        self.purgelayersbutton = forms.Button(Text = 'Purge Layers')
        self.purgelayersbutton.Click += self.purgeclick
        
        
        ###changing number of selecting options controls
        
        self.number_select_button_rows = 1 ##to increase by one every time the user chooses 
        
        self.selectbuttons= []
        for i in range(self.number_select_button_rows):
            selectbutton = forms.Button(Text = "Select / Im Lazy")
            selectbutton.Click += self.OnSelectButtonClick
            self.selectbuttons.append(selectbutton)
            

        
        self.selectcheckboxes = []
        for i in range(self.number_select_button_rows):
            checkboxselect = forms.CheckBox( Text = "")
            checkboxselect.Checked = True
            self.selectcheckboxes.append(checkboxselect)
            
        self.selectdropdowns=[]
        for i in range(self.number_select_button_rows):
            dropdownselect = forms.ComboBox( Text ="")
            dropdownselect.DataStore = material_names
            dropdownselect.SelectedIndex = 0
            self.selectdropdowns.append(dropdownselect)
        
        ## Layout###
        
        
        layout = forms.DynamicLayout()
        layout.Spacing = drawing.Size(5,5)
        for index, layer in enumerate(self.layer_names):
            layout.AddRow(self.checkboxes[index], layer, self.dropdowns[index])#u can just chuck strings in layouts and it acts like a lable
            layout.AddRow(None)
        layout.AddRow(None)
        for i in range(self.number_select_button_rows):
            layout.AddRow(self.selectcheckboxes[i], self.selectbuttons[i], self.selectdropdowns[i])
        layout.AddRow(None)
        layout.AddRow(None, self.purgelayersbutton, self.addselectbutton)
        layout.AddRow(self.DefaultButton, self.AbortButton)
        
        self.Content = layout
    
    ###operating methods
       
       
    
    def getobjectids(self):
        rs.Command('SelAll')
        check_ids = rs.GetObjects( preselect = True)
        solids_or_surf = []
        for id in check_ids:
            if rs.IsCurve(id) == True:
                return None
            else:
                rs.Command('SelAll')
                rs.Command('Cap')
                rs.Command('SelNone')
                rs.Command('SelClosedPolysrf')
                object_ids = rs.GetObjects(filter = 16, preselect=True)
            return object_ids
    
    def layervolume(self, ids):
        layervolume = {}
        if ids == None:
            layervolume[ids] = 'unable to calculate volume'
            return layervolume
        else:
            for id in ids:
                if rs.SurfaceVolume(id) != None:
                    layervolume[rs.ObjectLayer(id)] = sum(rs.SurfaceVolume(id)) * 0.000000001
                else:
                    layervolume['something strange'] = 'unable to calculate volume'
        return layervolume
    
    
    def checkboxchecks(self):
        checkboxchecked_index =[]
        for index, checkbox in enumerate(self.checkboxes):
            if checkbox.Checked == True:
                checkboxchecked_index.append(index)
        return checkboxchecked_index
    
    
    def materials_selected(self):
        materials_selected_index = []
        for dropdown in self.dropdowns:
            materials_selected_index.append(dropdown.SelectedIndex)
        return materials_selected_index
    
    
    def activate(self):
        checked_boxes_index = self.checkboxchecks()
        materials_index = self.materials_selected()
        data = self.layervolume(self.getobjectids())
        return data
        
    
    
    ### Button methods
    
    def OnOKButtonClick(self, sender, e):
        self.Close(True)
        try:
            print(self.activate())
        except:
            print("error! no objects found")
    
    def purgeclick(self, sender, e):
        for layer in self.layer_names:
            if rs.IsLayerEmpty(layer) == True:
                rs.DeleteLayer(layer)
            
            
            
    def OnCloseButtonClick(self, sender, e):
        self.Close(False)
    
    def OnSelectButtonClick(self, sender, e):
        return True

    def addselectlevel(self, sender, e):
        self.number_select_button_rows = self.number_select_button_rows + 1
        
    def OnSelectButtonAddClick(self, sender, e):
        pass
    
        

##script


def test(materials):
    test = Material_assigner(materials);
    rhinowindow = test.ShowModal(Rhino.UI.RhinoEtoApp.MainWindow)
    print(test.number_select_button_rows)
test(["material 1", "material 2", "material 3"])

You should move your data population code out of __init__ into a function that does that. Then also have a function that sets up the Eto controls and one that clears the form out. Then in __init__ and at the end of each function that modifies the Rhino document call these in the correct order to populate your data, clear the form and rebuild it.

4 Likes

Hi Nathan, thank you sooo much for getting back to me.

i’ve just learnt so much about manipulating class varibles now with functions outside of __init__ i kind of feel stupid now.

i was a bit stuck until i realised i needed to rebuild the layout section of my code everytime, not just the controls.

heres the code, i feel like i may have gone about it a bit sloppily, i’d massivley appreciate it if you could let me know if this is how you meant i should do it or if you would do it another way?.

i’ve just got a problem with changing the size of the window and buttons when it updates but i’ll be able to work that out.

thanks again!!!

import rhinoscriptsyntax as rs
import Rhino.UI
import Eto.Drawing as drawing
import Eto.Forms as forms

material_names = ["material 1", "material 2", "material 3"]


def get_layer_names():
    return rs.LayerNames()


##Control Functions

def eto_checkboxes(layer_names):
    checkboxes = []
    for i in layer_names:
        checkbox = forms.CheckBox( Text = "") ##check boxes (always true to assume u want to calculate every layer
        checkbox.Checked = True
        checkboxes.append(checkbox)
    return checkboxes

def eto_dropdowns(layer_names):
    dropdowns = []
    for i in layer_names:
        dropdown = forms.ComboBox()
        dropdown.DataStore = material_names
        dropdown.SelectedIndex = 0
        dropdowns.append(dropdown)
    return dropdowns

def eto_select_button(rows):
    selectbuttons = []
    for i in range(rows):
        selectbutton = forms.Button(Text = "Select / Im Lazy")
        
        selectbuttons.append(selectbutton)
    return selectbuttons

def eto_select_checkboxes(rows):
    selectcheckboxes = []
    for i in range(rows):
        checkboxselect = forms.CheckBox( Text = "")
        checkboxselect.Checked = True
        selectcheckboxes.append(checkboxselect)
    return selectcheckboxes
 
def eto_select_dropdowns(rows):
    selectdropdowns = []
    for i in range(rows):
        dropdownselect = forms.ComboBox( Text ="")
        dropdownselect.DataStore = material_names
        dropdownselect.SelectedIndex = 0
        selectdropdowns.append(dropdownselect)
    return selectdropdowns
    
##layout functions

def setup_layout(layer_names, checkboxes, dropdowns, number_select_button_rows, selectcheckboxes, selectbuttons, selectdropdowns, purgelayersbutton, addselectbutton, DefaultButton, AbortButton):
    layout = forms.DynamicLayout()
    layout.Spacing = drawing.Size(5,5)
    for index, layer in enumerate(layer_names):
        layout.AddRow(checkboxes[index], layer, dropdowns[index])#u can just chuck strings in layouts and it acts like a lable
        layout.AddRow(None)
    layout.AddRow(None)
    for i in range(number_select_button_rows):
        layout.AddRow(selectcheckboxes[i], selectbuttons[i], selectdropdowns[i])
    layout.AddRow(None)
    layout.AddRow(None, purgelayersbutton, addselectbutton)
    layout.AddRow(DefaultButton, AbortButton)
    return layout

def clear_layout():
    global layout
    layout = None
    
    

class Material_assigner(forms.Dialog[bool]):
        
    def __init__(self):##setting up the eto form
        self.Title = "Material Assigner"
        self.Padding = drawing.Padding(10)
        self.Resizable = True
        self.layer_names = get_layer_names()
        self.number_select_button_rows = 1
          
        ##eto controls
        
        self.checkboxes = eto_checkboxes(self.layer_names)
        self.dropdowns = eto_dropdowns(self.layer_names)
        
        self.selectbuttons = eto_select_button(self.number_select_button_rows)
        self.selectcheckboxes = eto_select_checkboxes(self.number_select_button_rows)
        self.selectdropdowns = eto_select_dropdowns(self.number_select_button_rows)
        
        for button in self.selectbuttons:
            button.Click += self.OnSelectButtonClick
        
        
        self.DefaultButton = forms.Button(Text = 'OK')
        self.DefaultButton.Click += self.OnOKButtonClick
        self.AbortButton = forms.Button(Text = 'Cancel')
        self.AbortButton.Click += self.OnCloseButtonClick
        
        self.addselectbutton = forms.Button(Text = 'Add select line')
        self.addselectbutton.Click += self.addselectlevel
        
        self.purgelayersbutton = forms.Button(Text = 'Purge Layers')
        self.purgelayersbutton.Click += self.purgeclick
        
        
        
        ## Layout###
        
        layout = setup_layout(self.layer_names, self.checkboxes, self.dropdowns, self.number_select_button_rows, self.selectcheckboxes, self.selectbuttons, self.selectdropdowns, self.purgelayersbutton, self.addselectbutton, self.DefaultButton, self.AbortButton)
       
        
        self.Content = layout
    
    ###operating methods
       
    
    def getobjectids(self):
        rs.Command('SelAll')
        check_ids = rs.GetObjects( preselect = True)
        solids_or_surf = []
        for id in check_ids:
            if rs.IsCurve(id) == True:
                return None
            else:
                rs.Command('SelAll')
                rs.Command('Cap')
                rs.Command('SelNone')
                rs.Command('SelClosedPolysrf')
                object_ids = rs.GetObjects(filter = 16, preselect=True)
            return object_ids
    
    def layervolume(self, ids):
        layervolume = {}
        if ids == None:
            layervolume[ids] = 'unable to calculate volume'
            return layervolume
        else:
            for id in ids:
                if rs.SurfaceVolume(id) != None:
                    layervolume[rs.ObjectLayer(id)] = sum(rs.SurfaceVolume(id)) * 0.000000001
                else:
                    layervolume['something strange'] = 'unable to calculate volume'
        return layervolume
    
    
    def checkboxchecks(self):
        checkboxchecked_index =[]
        for index, checkbox in enumerate(self.checkboxes):
            if checkbox.Checked == True:
                checkboxchecked_index.append(index)
        return checkboxchecked_index
    
    
    def materials_selected(self):
        materials_selected_index = []
        for dropdown in self.dropdowns:
            materials_selected_index.append(dropdown.SelectedIndex)
        return materials_selected_index
    
    
    def activate(self):
        checked_boxes_index = self.checkboxchecks()
        materials_index = self.materials_selected()
        data = self.layervolume(self.getobjectids())
        return data
        
    
    
    ### Button methods
    
    def OnOKButtonClick(self, sender, e):
        self.Close(True)
        try:
            print(self.activate())
        except:
            print("error! no objects found")
    
    def purgeclick(self, sender, e):
        for layer in self.layer_names:
            if rs.IsLayerEmpty(layer) == True:
                rs.DeleteLayer(layer)
        self.layer_names = get_layer_names()
        clear_layout()
        self.Content = setup_layout(self.layer_names, self.checkboxes, self.dropdowns, self.number_select_button_rows, self.selectcheckboxes, self.selectbuttons, self.selectdropdowns, self.purgelayersbutton, self.addselectbutton, self.DefaultButton, self.AbortButton)
            
    def OnCloseButtonClick(self, sender, e):
        self.Close(False)
    
    def OnSelectButtonClick(self, sender, e):
        print ("select button works")
        return True

    def addselectlevel(self, sender, e):
        self.number_select_button_rows = self.number_select_button_rows + 1
        
        self.selectbuttons = eto_select_button(self.number_select_button_rows)
        self.selectcheckboxes = eto_select_checkboxes(self.number_select_button_rows)
        self.selectdropdowns = eto_select_dropdowns(self.number_select_button_rows)
        
        print(self.number_select_button_rows)
        
        clear_layout()
        self.Content = setup_layout(self.layer_names, self.checkboxes, self.dropdowns, self.number_select_button_rows, self.selectcheckboxes, self.selectbuttons, self.selectdropdowns, self.purgelayersbutton, self.addselectbutton, self.DefaultButton, self.AbortButton)
        


##script


def test():
    test = Material_assigner()
    rhinowindow = test.ShowModal(Rhino.UI.RhinoEtoApp.MainWindow)
    
test()

Much better code separation, which gives you much better control over the flow of your code and how you maintain your GUI elements. Keep it up with the learning process :slight_smile:

1 Like

Hi again nathan,

i’ve been continuing to add functionality to the eto form but i just cant seem to get the dialog to adjust its size automatically.

under __init__ i have the clientsize variable being asigned by a function (like you taught me) like this:

self.ClientSize = dialog_auto_size()

and the function looks like this:

def dialog_auto_size():
    size = drawing.Size(-1,-1)
    return size

it works on the initilaisation of the class, and the form is auto sized. but when i try and rebuild the function under the button methods, it adjusts the size to 0. as in its as small as it can be.

do you think you can help me with this?

thanks in advance