Friday, October 5, 2012

RPS script to print a list of floor types and their functions

Yesterday, I needed to print out all the floor types in a Revit document along with their function (Interior/Exterior). Obviously, I decided to write a quick RevitPythonShell script, but ran into some snags along the way that I would like to share.

This should really be as easy as:

collector = FilteredElementCollector(doc)
for floor_type in collector.OfClass(FloorType):
   print floor_type.Name, floor_type.Function

But, well, nothing is ever easy, is it? What I ended up doing was this:

from System import Enum
collector = FilteredElementCollector(doc)
collector.OfClass(FloorType)
for floor_type in collector:
   name = Element.Name.GetValue(floor_type)
   function_param = Element.get_Parameter(floor_type, BuiltInParameter.FUNCTION_PARAM)
   if function_param:
      function = Enum.ToObject(WallFunction, function_param.AsInteger()).ToString()
   else:
      function = 'None'
   print '%(name)-25s\t%(function)s' % locals()

Let's go through this line by line...

from System import Enum

Uh... I'll explain that later on...

collector = FilteredElementCollector(doc)
collector.OfClass(FloorType)
for floor_type in collector:

Nothing unusual here - this is how to get a list of elements from a Revit document using a FilteredElementCollector and iterating over that list.

name = Element.Name.GetValue(floor_type)

What?! Shouldn't that just have been floor_type.Name? I thought so too. But somehow, whenever I do that, I get an error like this:

>>> # note how ugly one-liners get ;)
>>> ft = list(FilteredElementCollector(doc).OfClass(FloorType))[0].Name
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: Name

This AttributeError is somehow related to IronPython not being able to figure out that it needs to use the base method inherited from Autodesk.Revit.DB.Element. So, I tell it to use that explicitly by referencing the property explicitly with Element.Name - and then retrieving its value: .GetValue. It needs to be told for which instance to retrieve the value, which is why I then plug in the floor_type object.

So... now we have the name of the FloorType. What about its function? That isn't a property, but rather a parameter, that can be found using the Revit Lookup tool: BuiltInParameter.FUNCTION_PARAM. Retrieving parameters is easy:

function_param = Element.get_Parameter(floor_type, BuiltInParameter.FUNCTION_PARAM)

Note again, how I use the base method Element.get_Parameter, as it doesn't seem to work on FloorType directly - my guess is that the definition of FloorType somehow isn't what IronPython is used to...

 if function_param:
       function = Enum.ToObject(WallFunction, function_param.AsInteger()).ToString()
else:
   function = 'None'

The parameter may be null or, in python None, which will evaluate to False in a condition. In that case, we just set the text of the variable function to 'None'`. But the other line is a lot more interesting! This is why we did the from System import Enum earlier on: The (integer) value of the FUNCTION_PARAM is actually a member of an enumeration. As far as I can tell, it is equivalent to the WallFunction enumeration. With Enum.ToObject(WallFunction, function_param.AsInteger()), we can an the appropriate instance of this enumeration, which we then convert to a string (ToString()).

The last line:

print '%(name)-25s\t%(function)s' % locals()

Prints out the name and function nicely, using pythons string formatting: Left-alined name with a minimum width of 25 characters, a tab and then the function.

Try the script!

3 comments:

  1. Daren,
    Thanks a bunch for this. Was really stuck on how to get the string values of the WallFunction enumerator. BTW,

    wall.WallType.get_Parameter( BuiltInParameter.FUNCTION_PARAM )

    works for me, instead of

    Element.get_Parameter( wall, BuiltInParameter.FUNCTION_PARAM )

    Thanks again!

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. Thanks a lot, it totally unstucked me in two cases :

    fs_name = fi.Symbol.Name #where fi is a FamilyInstance
    vft_name = vft.Name #where vft is a ViewFamilyType

    become

    fs_name = Element.Name.GetValue(fi.Symbol)
    vft_name = Element.Name.GetValue(vft)

    ReplyDelete