Search code examples
c#pythonlistboxrevit-apirevitpythonshell

IronPython and Revit API - How to display item attributes in listbox (attribute name)?


I am an architect who grew to love coding through Revit. But unfortunately since still being an ultimate noob, I would need some help from anyone willing to jump in. I am also Stackoverflow noob, so I dont know if it is even OK and excepted in the community to post a questions like these which are more tutoring ones then problem solving. But anyway, here it goes: I am trying to create an app which will be able to delete multiple parameters from Revit family editor at the same time. I have succeeded with C#, but since I want to transfer to Python due to being an easier to get into as beginner I am doing a lot of browsing but with no help due to a limited OOP knowledge. How can I display Family parameter name in a Listbox or if I alredy have string names in listbox, how can I compare selected items with actual parameters from FamilyParameterSet?

I have a start code where I am able to collect all parameters from Family manager. I cast it to List. Then one options is to use Name attribute of the parameter to be listed in listbox, but I do not have knowledge to get back and check out list or to loop the list to compare names from the parameter set to ones being selected from listbox. So I went to another option of putting directly Family parameters in listbox, but I am then not able to display actual Name attribute. This C# code might help me, but I do not know how to recreate it in Python, since my OOP experience is really poor. Items on ListBox show up as a class name

doc = __revit__.ActiveUIDocument.Document               
mgr = doc.FamilyManager;                                
fps = mgr.Parameters;                                  

paramsList=list(fps)                                   
senderlist =[]                                          

class IForm(Form):

    def __init__(self):
        self.Text = "Remove multiple parameters"

        lb = ListBox()                                  
        lb.SelectionMode = SelectionMode.MultiSimple   

        for n in fps:                                   
        lb.Items.Add(n.Definition.Name)


        lb.Dock = DockStyle.Fill
        lb.SelectedIndexChanged += self.OnChanged       


        self.Size = Size(400, 400)                      
        self.CenterToScreen()                           

        button = Button()                               
        button.Text = "Delete Parameters"              
        button.Dock = DockStyle.Bottom                  
        button.Click += self.RemoveParameter                  
        self.Controls.Add(button)  

    def OnChanged(self, sender, event):
        senderlist.append(sender.SelectedItem)                        

    def RemoveParameter(self,sender,event):       
        for i in paramsList:
            if i.Definition.Name in senderlist:
            t = Transaction(doc, 'This is my new transaction')
            t.Start()  
            mgr.RemoveParameter(i.Id)
            t.Commit()
Application.Run(IForm())

I need for function RemoveParameter to have all .Id proportes of the Family parameters in order to remove them from the Family parameter set. In the start of the code (for guys which do not know Revit API) "fps" means FamilyParameterSet which is cast to Python list "paramsList".So I need to remove member of the FPS from items selected in listbox.


Solution

  • Your journey from Revit to code is a familiar one!

    First, looks like there are some hangovers in your code from C#. There are a couple of key shifts from C# to Python that you need to bear in mind - in your case there are two lines that need to be indented, as per the error when you run the code:

    Syntax Error: expected an indented block (line 17)
    Syntax Error: expected an indented block (line 39)
    

    The rule of thumb is that indents are needed after colons : which also makes code more readable. Also there are some semi-colons ; in the first few lines - no need for them!

    Otherwise the code was pretty much there, Ive added comments in your code below:

    # the Winforms library first needs to be referenced with clr
    import clr
    clr.AddReference("System.Windows.Forms")
    
    # Then all the Winforms components youre using need to be imported
    from System.Windows.Forms import Application, Form, ListBox, Label, Button, SelectionMode, DockStyle
    
    doc = __revit__.ActiveUIDocument.Document               
    mgr = doc.FamilyManager                         
    fps = mgr.Parameters                          
    
    paramsList=list(fps)                                   
    # senderlist = [] moved this into the Form object - welcome to OOP!                                        
    
    class IForm(Form):
    
        def __init__(self):
            self.Text = "Remove multiple parameters"
    
            self.senderList = []
    
            lb = ListBox()                                  
            lb.SelectionMode = SelectionMode.MultiSimple   
    
            lb.Parent = self # this essentially 'docks' the ListBox to the Form
    
            for n in fps:                                   
                lb.Items.Add(n.Definition.Name)
    
            lb.Dock = DockStyle.Fill
            lb.SelectedIndexChanged += self.OnChanged       
    
            self.Size = Size(400, 400)                      
            self.CenterToScreen()                           
    
            button = Button()                               
            button.Text = "Delete Parameters"              
            button.Dock = DockStyle.Bottom                  
            button.Click += self.RemoveParameter                  
            self.Controls.Add(button)  
    
        def OnChanged(self, sender, event):
            self.senderList.append(sender.SelectedItem)                        
    
        def RemoveParameter(self,sender,event):       
            for i in paramsList:
                if i.Definition.Name in self.senderList:
                    t = Transaction(doc, 'This is my new transaction')
                    t.Start()
    
                    # wrap everything inside Transactions in 'try-except' blocks
                    # to avoid code breaking without closing the transaction
                    # and feedback any errors
                    try:
                        name = str(i.Definition.Name) 
                        mgr.RemoveParameter(i) # only need to supply the Parameter (not the Id)
                        print 'Parameter deleted:',name # feedback to the user
                    except Exception as e:
                        print '!Failed to delete Parameter:',e
    
                    t.Commit()
                    self.senderList = [] # clear the list, so you can re-populate 
    
    Application.Run(IForm())
    

    From here, additional functionality is just polishing it up for users:

    • Add a popup dialog letting them know success/failure
    • Once a user has deleted a paramenter, refresh the ListBox
    • Filter out BuiltIn parameters that cant be deleted (try deleting one and see the error it throws)

    Let me know how this works!