Retrieving provider parameters for a category

Hi everyone,

I’m trying to retrieve the set of parameters associated to a categorie, like the list from a filterrule.
I suspect this is a combination of builtin-parameters and projectparameters assigned to the category.

In the Revit-Api i found the enumaration of builtin-parameters, but i don’t see how i can retrieve only those associated to the category.

For the project parameters the Revit-API provides the ParameterBindings of a document.
below i have some code for this, but it’s not yet perfect (WIP).

Big thanks in advance on any input or info on this issue :slight_smile: !

def list_project_parameters_for_category(doc, category_name):
    category_set = None
    # Find the target category
    for cat in doc.Settings.Categories:
        if cat.Name == category_name:
            category_set = CategorySet()
            category_set.Insert(cat)
            break
    
    if not category_set:
        print("Category '{}' not found.".format(category_name))
        return
    
    # List to store project parameter names
    project_parameters = []
    
    # Iterate through all parameter bindings
    for definitionBindingMapItem in doc.ParameterBindings:
        definition = definitionBindingMapItem.Key
        binding = definitionBindingMapItem.Value
        
        # Check if the parameter is bound to the target category
        if isinstance(binding, InstanceBinding):
            if binding.Categories.Contains(category_set.ItemAt(0)):
                project_parameters.append(definition.Name)
        elif isinstance(binding, TypeBinding):
            if binding.Categories.Contains(category_set.ItemAt(0)):
                project_parameters.append(definition.Name)
    
    return project_parameters
1 Like

I think this is what you are asking for??? Tested in 2024
Make sure to put something here >>>OST_{REPLACETHIS}<<<

from System.Collections.Generic import List
from pyrevit import revit, DB


def get_parameters_bound_to_category(doc, category):
    param_names = set()
    param_bindings = doc.ParameterBindings
    iterator = param_bindings.ForwardIterator()
    while iterator.MoveNext():
        definition = iterator.Key
        binding = iterator.Current
        if binding.Categories.Contains(doc.Settings.Categories.get_Item(category)):
            param_names.add(definition.Name)
    return sorted(list(param_names))
    
def main():
    bound_parameters = get_parameters_bound_to_category(revit.doc, DB.BuiltInCategory.OST_{REPLACETHIS})
    for param in bound_parameters:
        print(param)
        
if __name__ == '__main__':
    main()
1 Like

As far as I understand, the technique proposed by JP is only going to get the user-added shared parameters, but not user-added project parameters or built-in parameters for the chosen category.

I have done what I think you are trying to do by getting one element of the chosen category, then getting the ParameterSet object from that.

parameterset = single_cat_collector[0].Parameters

That gets all parameters (shared, project, builtin). There is probably a better way (?).

Ah ok I misunderstood you.

Just ask any element from a category for a list of its parameters.

parameter_names = [param.Definition.Name for param in element.Parameters]

Something that maybe and print the output or however you need the data.

1 Like

Yes, that is what I meant. Depending on whether the element is an instance or type, you get access to different parameters.

True,
but the trick is that one element from a category doesn’t necessarily have the same parameters (built-in and project) as the others.
Let’s say you make a door from a wall-based door family template vs. one created from a generic model and then changed to doors category.

This would result in different built-in parameters.
later this week i will have time to work further on the script and let you know of my progress.

I agree with you. Maybe you would need to loop through all your instances or types for that category, adding their parameters to a set.
There must be a more efficient way, though.

Hi all,

I went back to solving this issue fo finding the correct available parameters.
The correct way of stateing is that i want to find all the possible providers for a category.

Below i generated 3 lists for a category, i took generic models as this is one of the most mixed onces.
List 1: Project parameters
List 2: Buildin parameters on the element level (intersected so only the once that apply to all the elements remain)
List 3: Family parameters (also intersected)

I still don’t get the right list if i would overlap these 3.
On the one hand i’m missing parameters like ‘Omniclass Number’ and ‘Omniclass Title’ or ‘Rebar cover’.
On the other hand i have to many parameters still in my list like ‘Area’, ‘Type Id’, ’ Edited by’,…

Altho the Family parameter list gets close its still not the exact list as in the providers when creating a filter.
the code is just working code and not to clean at all but here you go:

# -*- coding: utf-8 -*-

# imports
# ===================================================

# Autodesk + pyrevit imports
from Autodesk.Revit.DB import *
from pyrevit import revit

# Variables
# ===================================================
doc = revit.doc
uidoc = revit.uidoc

# Functions
# ===================================================
def get_elementtypes_by_category(category):
    return FilteredElementCollector(doc).OfCategory(category).WhereElementIsElementType()

def list_project_parameters_for_category(doc, category_name):
    target_category = None
    # Find the target category
    for cat in doc.Settings.Categories:
        if cat.Name == category_name:
            target_category = cat
            break
    
    if target_category is None:
        print("Category '{}' not found.".format(category_name))
        return []

    project_parameters = []

    # Correctly iterate through all parameter bindings
    iterator = doc.ParameterBindings.ForwardIterator()
    while iterator.MoveNext():
        definition = iterator.Key  # Access the definition
        binding = iterator.Current  # Access the binding
        
        # Check if the binding's categories contain the target category
        if hasattr(binding, 'Categories') and binding.Categories.Contains(target_category):
            project_parameters.append(definition.Name)

    return project_parameters

# Main
# ===================================================
if __name__ == '__main__':
    doortypes = get_elementtypes_by_category(BuiltInCategory.OST_GenericModel)
    doors = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_GenericModel).WhereElementIsNotElementType().ToElements()
    print('-'*50)
    print('PROJECT PARAMETERS')
    projectparameters = list_project_parameters_for_category(doc, 'Generic Models')
    for param in projectparameters:
        print(param)
    print('-'*50)
    print('BUILD-IN PARAMETERS')
    # Initialize a list to hold parameter sets for each door
    door_parameters_sets = []

    for door in doors:
        # Create a set to hold parameters for the current door
        current_door_parameters = set()
        door_params = door.Parameters
        for param in door_params:
            # Check if parameter is a BuiltInParameter (Not a direct check for FamilyParameter)
            if isinstance(param.Definition, InternalDefinition):
                if param.Definition.BuiltInParameter != BuiltInParameter.INVALID:
                    current_door_parameters.add(param.Definition.Name)
        # Add the set of parameters for the current door to the list
        door_parameters_sets.append(current_door_parameters)

    # Find the intersection of all parameter sets
    if door_parameters_sets:
        common_parameters = set.intersection(*door_parameters_sets)
        for param in sorted(common_parameters):
            print("{}".format(param))
    else:
        print("No common parameters found.")
    
    print('-'*50)
    print('FAMILY PARAMETERS')
    doors = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_GenericModel).WhereElementIsElementType().ToElements()
    door_familyparameters_sets = []
    for door in doors:
        current_familyparameters = set()
        doorfamilyparams = door.Parameters
        for param in doorfamilyparams:
            if param.Definition.BuiltInParameter != BuiltInParameter.INVALID:
                    current_familyparameters.add((param.Definition.Name))
        door_familyparameters_sets.append(current_familyparameters)
    
    common_familyparameters = set.intersection(*door_familyparameters_sets)
    
    for param in sorted(common_familyparameters):
        print("{}".format(param))
    print('-'*50)

@ErikFrits in the past i have seen code snippets from you where you are able to retrieve elements with a certain parameter in them. DO you maybe have the solution to this issue, because it seems to be kind of the reverse from that.

Below a short gif with the provider list next to my output lists:
providerlist

1 Like

I notice you are collecting
For project parameters: both instance and types
For BuiltInParameters: instances
For family parameters: types

Could this explain the differences you see in the sets?

1 Like