Pushbutton with Iselection and BuiltInParameter.UNIFORMAT_CODE

I’ve created a script behind a pushbutton with Iselection, but it won’t work.
I’m trying to select structural foundations with only Type Parameter “Assembly Code” contains “17.”
Does any body know what I’m doing wrong?

file: select-palen_script.py

# Import Revit and pyRevit modules
from pyrevit import revit, DB, UI
from pyrevit import script

from Autodesk.Revit.UI.Selection import ISelectionFilter, Selection, ObjectType

# Get the current document
doc = revit.doc
uidoc = __revit__.ActiveUIDocument

class StructuralFoundationFilterByCategory(ISelectionFilter):
    def AllowElement(self, elem):
        # Controleer of het element tot de categorie OST_StructuralFoundation behoort
        elem = self.linkedDoc.GetElement(reference.LinkedElementId)
        if elem is not None:
            if elem.Category.Id == ElementId(BuiltInCategory.OST_StructuralFoundation):               
            # Haal de parameter voor UNIFORMAT_CODE op
                uniformat_code_param = elem.get_Parameter(DB.BuiltInParameter.UNIFORMAT_CODE)
                if uniformat_code_param:
                    param_value = uniformat_code_param.AsString()
                    # Controleer of de waarde van de parameter "17" bevat
                    if param_value and "17" in param_value:
                        return True
                return True
        return False

# Laat de gebruiker Structural Foundations selecteren met het filter
ref_elems = uidoc.Selection.PickObjects(ObjectType.Element, StructuralFoundationFilterByCategory())

# Haal de geselecteerde elementen op
sel_elems = [doc.GetElement(ref) for ref in ref_elems]
 
print(sel_elems)

Above is my script but it’s not working. Does anybody know how to make this work?
But I have also a function available. (see below) This works insida a script. Can I use this in de code above?


def CollectPalen(document):
    """
    Verzamel alle palen in dit document waarbij parameter 'Assembly Code' de tekst "17" bevat.

    Parameters:
    doc (Autodesk.Revit.DB.Document): The Revit document from which to select piles.
        The document can be either a Document or an ActiveView.

    Returns:
    List[Autodesk.Revit.DB.Element]: A list of all piles in the model,
        filtered by the specified Uniformat code.
    """
    # Create a list of selected elements using the Param_filter function
    param_id = ElementId(BuiltInParameter.UNIFORMAT_CODE)
    pfrf = ParameterFilterRuleFactory.CreateContainsRule(param_id, "17")
    epf = ElementParameterFilter(pfrf)
    output = FilteredElementCollector(document)\
        .OfCategory(BuiltInCategory.OST_StructuralFoundation)\
        .WhereElementIsNotElementType()\
        .WherePasses(epf)\
        .ToElements()
    return output

pointer, your uniformat_code param is a type parameter
so you need to collect type element
if you select a type element, it won’t show in the UI except in the filter at the bottom right of the revit UI
so you need to get the dependent elements from the type, and from these dependent elems, the FamilyInstances


# Import Revit and pyRevit modules
from pyrevit import revit, DB
from Autodesk.Revit.DB import (
    FilteredElementCollector,
    BuiltInCategory,
    ParameterFilterRuleFactory,
    ElementParameterFilter,
    ElementId,
)
from System.Collections.Generic import List

# Get the current document
doc = revit.doc
uidoc = __revit__.ActiveUIDocument

LOOKED_FOR_CODE = "17"
param_id = ElementId(DB.BuiltInParameter.UNIFORMAT_CODE)
pfrf = ParameterFilterRuleFactory.CreateContainsRule(param_id, LOOKED_FOR_CODE)
epf = ElementParameterFilter(pfrf)

matching_elements = (
    FilteredElementCollector(doc)
    .OfCategory(BuiltInCategory.OST_StructuralFoundation)
    .WhereElementIsElementType()
    .WherePasses(epf)
    .ToElements()
)

dependent_family_instances = []
for elem in matching_elements:
    dependent_family_instances.extend(
        elem.GetDependentElements(DB.ElementClassFilter(DB.FamilyInstance))
    )


if matching_elements:
    # Convert to .NET collection for selection
    element_ids = List[ElementId](
        [
            elem
            for elem in dependent_family_instances
        ]
    )
    uidoc.Selection.SetElementIds(element_ids)
    print(
        "Selected {} structural foundations type with code containing '{}'".format(
            len(matching_elements), LOOKED_FOR_CODE
        )
    )
    print("Dependent family instances selected: {}".format(len(dependent_family_instances)))

else:
    print("No matching elements found")

Thanks @Jean-Marc.
Sorry, but with your code I can’t make a user-selection in my model.
May I wasn’t clear enough. I want to make a user-selection in my model to select some filtered elements and not select all filtered elements.

With your code it selects ALL filtered elements in my project. It could also be achieved with my function CollectPalen. (with the correct modules imported) That works too.

And… correct me if I’m wrong, but as fas as I know I can only do a user-selection with Iselection ?
But I don’t know how to write the code to make this work.

Do not worry, I understood you just right and pointed you in the direction to solve it yourself

ISelection and interfaces are not the easiest concept to grasp as a beginner.

Disclaimer ;)

I try to not provide with the solution but with pointers _ my day job is to provide solutions to my clients and taking care of the whole pyrevit ecosystem is not my day job

The solution would be like :


# encoding: utf-8
from pyrevit import revit, DB
from Autodesk.Revit.UI.Selection import ISelectionFilter, ObjectType

LOOKED_FOR_CODE = "17"
doc = revit.doc
uidoc = revit.uidoc

UNIFORMAT_CODE_PARAM = DB.BuiltInParameter.UNIFORMAT_CODE



class StructuralFoundationTypeUniformatFilter(ISelectionFilter):
    def AllowElement(self, elem):
        # Only allow Structural Foundations
        if elem.Category and elem.Category.Id.IntegerValue == int(
            DB.BuiltInCategory.OST_StructuralFoundation
        ):
            # Get the element type (FamilySymbol)
            elem_type = doc.GetElement(elem.GetTypeId())
            if elem_type:
                # Try to get the UNIFORMAT_CODE parameter robustly
                try:
                    uniformat_param = elem_type.get_Parameter(UNIFORMAT_CODE_PARAM)
                except Exception:
                    uniformat_param = None
                if uniformat_param:
                    value = uniformat_param.AsString()
                    if value and LOOKED_FOR_CODE in value:
                        return True
        return False

    def AllowReference(self, reference, point):
        return False  # Only element selection

# Let the user select elements with the filter
try:
    refs = uidoc.Selection.PickObjects(
        ObjectType.Element,
        StructuralFoundationTypeUniformatFilter(),
        "Select Structural Foundations with UNIFORMAT_CODE containing '{}'".format(
            LOOKED_FOR_CODE
        ),
    )
    selected_elems = [doc.GetElement(r.ElementId) for r in refs]
    print("Selected elements:", selected_elems)
except Exception as e:
    print("Selection cancelled or failed:", e)

Aha. Sorry, of course. I understand.