2

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.

darkhitect
  • 55
  • 1
  • 6

1 Answers1

2

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!

Callum
  • 578
  • 3
  • 8
  • 1
    Thank you @Callum very much. In essence script itself is still removing only single parameter while I wanted to remove multiple parameters but it helped me to get a grip on what needs to be done. As well it helped me a bit with understanding OOP a bit more. So in general it did help a tone. In the meantime I migrated with my solution to Listview with checkboxes and it works much more neat. However I came to another issue I am trying to solve. I want to catch Exception (InvalidOperationException) but am failing to do so... – darkhitect Aug 21 '19 at 20:12
  • 1
    This happens when I am trying to use my Ribbon app while in project (not in family editor mode). I have created an .addin after deploying XML through RevitPythonShell. So in eesence it works like a charm in family editor but I want to prompt user to go to family manager if app is being used inside project. Itried something like this: if doc.IsFamilyDocument==False: TaskDialog.Show("Not in family editor", "Please enter family editor mode"). I tryed try/else but without much succes. In C# I used upper settup and returned Result.Cancelled but here i dont know hot to solve this... – darkhitect Aug 21 '19 at 20:14
  • 1
    I will try to solve this in following day or two and then perhaps create new question with whole new script as an example. Your guidelines really helped a lot.. Thank you once again @Callum – darkhitect Aug 21 '19 at 20:20
  • 1
    Sounds like youre on the right path - note that you can use `exit()` to escape a python script, so after the TaskDialog you could `exit()` before running the rest of the script – Callum Aug 22 '19 at 21:26
  • 1
    Thank you @Callum. This is exactly what I was looking for, due to my inexperience. exit() was not working, but (after some googling) it seem quit() works like a charm. Thank you very much. – darkhitect Aug 23 '19 at 08:41