Category by name in Python

Thanks!
“class” should be changed to some other name.

I still would want to know why “OST_Rooms” cannot be used in Python.

1 Like

You’re right, I’ve changed it to “klass” (this happens when you code on a smartphone inside a forum without testing anything :sweat_smile:

Two untested options:

categoryString = "OST_StructuralColumns"
categoryId = BuiltInCategory(getattr(BuiltInCategory, categoryString))

Or maybe

from System import Enum
category = Enum.Parse(categoryString)

PythonNet 3 (in pyRevit WIP) should handle .net enums, IIRC

The following works:

import pyrevit
from pyrevit import DB
from Autodesk.Revit.DB import *
doc = pyrevit.revit.doc
categoryString = "OST_StructuralColumns"
bic = getattr(BuiltInCategory, categoryString)
elements = FilteredElementCollector(doc).OfCategory(bic).WhereElementIsNotElementType()
elements = sorted(elements, key=lambda elements:elements.Name)
for element in elements:
    print (element.Name + " " + str(element.Id))
print ("end")

If categoryString = “OST_Rooms”, then there is:
TypeError : property cannot be read
referring to elements.Name in:
elements = sorted(elements, key=lambda elements:elements.Name)

If bic = BuiltInCategory(getattr(BuiltInCategory, categoryString)), then this line gives
TypeError : property cannot be read

The following does not work:

import clr
clr.AddReference("System")
from System import Enum
categoryString = "OST_StructuralColumns"
category = Enum.Parse(categoryString)

It gives:
TypeError : No method matches given arguments for Parse: (<class ‘str’>)
Referring to:
category = Enum.Parse(categoryString)

Have not tried PythonNet.

My workaround to handle the special case where OST_Rooms cannot lead to element.Name:

import pyrevit
from pyrevit import revit, DB
from Autodesk.Revit.DB import *
doc = __revit__.ActiveUIDocument.Document # type: ignore
#categoryString = "OST_StructuralColumns"
# or
categoryString = "OST_Rooms"
bic = getattr(BuiltInCategory, categoryString)
elements = FilteredElementCollector(doc).OfCategory(bic).WhereElementIsNotElementType().ToElements()
print ("total : " + str(len(elements)))
for element in elements:
    try:
        name = element.Name
        elements = sorted(elements, key=lambda elements:elements.Name)
    except:
        name = element.LookupParameter("Name").AsString()
        elements = sorted(elements, key=lambda elements:elements.LookupParameter("Name").AsString())
    print (str(element.Id) + " " + name)
print ("end")

The sorting should be done first before listing each element.
The corrected codes are:

#from operator import attrgetter  ## if attrgetter used
import pyrevit
from pyrevit import revit, DB
from Autodesk.Revit.DB import *
doc = __revit__.ActiveUIDocument.Document # type: ignore
categoryString = "OST_StructuralColumns"
# or
#categoryString = "OST_Rooms"
bic = getattr(BuiltInCategory, categoryString)
elements = FilteredElementCollector(doc).OfCategory(bic).WhereElementIsNotElementType().ToElements()
hasName = True
try:
    elements = sorted(elements, key=lambda elements: elements.Name)
    # or
    #elements = sorted(elements, key=attrgetter('Name'))
except:
    elements = sorted(elements, key=lambda elements:elements.LookupParameter("Name").AsString())
    hasName = False
print ("total : " + str(len(elements)))
for element in elements:
    if hasName:
        name = element.Name
    else:
        name = element.LookupParameter("Name").AsString()
    print (str(element.Id) + " " + name)
print ("end")

You can get the name and id of the element, put them into a tuple and sort the list

def get_name(element):
    try:
        return element.Name
    except AttributeError:
        return element.LookupParameter("Name").AsString()

# ...

ids_and_names = ((e.Id, get_name(e) for e in elements)

for item in sorted(ids_and_names, key=lambda x: x[1]):
    print("{} {}".format(*item)
1 Like

category.BuiltInCategory is category.Id if using cpython (with #! python3) but is name in string if using ironpython (without #! python3)

#! python3
# List Categories
from Autodesk.Revit.DB import *
doc = __revit__.ActiveUIDocument.Document # type: ignore
result = "CATEGORIES"
for category in doc.Settings.Categories:
result += "\n" + category.Name + " " + str(category.Id) + " " + str(category.BuiltInCategory)
print (result)
print(("End"))

That is respectively:
Building Type Settings -2008120 -2008120
Building Type Settings -2008120 OST_HVAC_Load_Building_Types

How to get name in string when using cpython?

1 Like