View Filters

It's time to look at the View Filters. There is quite a lot I want to show you about them, so it will be a long lesson. Grab a cup of tea or coffee and let's learn about View Filters in Revit API.

View Filters

It's time to look at the View Filters. There is quite a lot I want to show you about them, so it will be a long lesson. Grab a cup of tea or coffee and let's learn about View Filters in Revit API.

Download Files

Summary

In this lesson, you will learn a lot about View Filters.

I tried to cover as much as you might need, but in the future you might come back here and only reference the necessary step.

We will go through the following steps:

  • 1️⃣ View Filters Overview in Revit UI

  • 2️⃣ View Filters Overview in Revit Lookup

  • 3️⃣ How to Get all View Filters in the Project

  • 4️⃣ How to Sort View Filters used by Views

  • 5️⃣ How to add View Filter

  • 6️⃣ How to Copy View Filters

  • 7️⃣ Read View Filters properties

  • 8️⃣ Create a new View Filter

  • 9️⃣ Create View Filters based on a Type Name Parameter.

As you can see, we will cover a lot about View Filters in this lesson.
So get ready, and let's learn how to automate View Filters.

1️⃣ View Filters Overview in Revit UI

Let's start with the basics of View Filters in the Revit UI.

You know that Views and View Templates in Revit have a menu for View Filters.

Your view filters can have graphic overrides, that will use the same OverrideGraphicSettings as we learnt in the 09.03 - Graphic Overrides lesson.

Let's look inside the filter by clicking on Edit/New.

In general, to create a View Filter we need to:

  • Specify Categories

  • Create Rules by

    • Selecting Parameter

    • Evaluator

    • Rule Value

And these are the steps we need to replicate inside the code.

💡 We won't look into complicated nested rules, where we can have a rule within a rule... We will look at the standard use-case that will be used 99% of the time.

2️⃣ View Filters Overview in Revit Lookup

It's clear with Revit UI. Let's have a look behind the scenes to understand them a little better.

Go to Add-Ins tab and then in Revit Lookup choose Snoop Database.

This will open up a menu with all the objects inside your Revit project and we can inspect them.

This will open up a menu with all the objects inside your Revit project and we can inspect them.

Inside this Snoop Database, we can search for 'Filter'. And we are most interested in ParameterFilterElement Objects.

You won't find many properties and methods but there are a few that we need:

💡 We will look into these later in the lesson.

Obviously, you will see a list of categories of the ViewFilter when you click on GetCategories.

But keep in mind that once you start looking into GetElementFilter, you will be working with a lot of nested filters and rules. And that's where a lot of people get confused (myself included).

But since we won't be working with complicated examples of nested rules, we will be fine. For now, just keep in mind the order of the methods we can use:

👉 ElementParameterFilter -> GetElementFilter() -> GetFilters() -> GetRules() -> Read Rule Properties.

Prepare a Button

Let's prepare a button to write the code. Same as usual, we will use a few imports and define variables for this lesson.

Here is my code from the lesson so you can follow along:

# -*- coding: utf-8 -*-
__title__ = "9.06 - View Filters"
__doc__   = """Date    = 15.06.2024
_____________________________________________________________________
Description:
Learn how to work with View Filters:
_____________________________________________________________________
Author: Erik Frits"""

# ⬇️ IMPORTS
from Autodesk.Revit.DB import *
from pyrevit import script, forms

#.NET Import
import clr
clr.AddReference('System')
from System.Collections.Generic import List

#📦 VARIABLES
uidoc  = __revit__.ActiveUIDocument
doc    = __revit__.ActiveUIDocument.Document #type:Document
output = script.get_output()
3️⃣ Get All View Filters in the Project

First of all, let's get all ParameterFilterElements in the project so we can create a list of all filters we have.

This is very simple because we can use FilteredElementCollector + OfClass.

all_filters     = FilteredElementCollector(doc).OfClass(FilterElement).ToElements() # Params + Selection Filter
all_par_filters = FilteredElementCollector(doc).OfClass(ParameterFilterElement).ToElements()
all_sel_filters = FilteredElementCollector(doc).OfClass(SelectionFilterElement).ToElements()

# Print Filter Names
for filter in all_filters:
    print(filter.Name)
4️⃣ Sort used View Filters by Views

Now, let's have a look at how to Get View Filters that are applied to a view or a view template.

Firstly, we need to get all views and view templates in the project. Then, we need to use the GetFilters method on each view. This will give you a list of ElementIds of the filters.

Then, as usual, we can convert ElementId into an Element, by using GetElement and print some results.

Here is the code:

all_views_and_vt = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Views).ToElements()
all_views        = [v for v in all_views_and_vt if not v.IsTemplate]
all_vt           = [v for v in all_views_and_vt if v.IsTemplate]

for view in all_views: #type: View
    filter_ids = view.GetFilters()
    print('View: {:.<50} has {} Filters'.format(view.Name, len(filter_ids) ))

    for filter_id in filter_ids:
        f = doc.GetElement(filter_id)
        print('-----{}'.format(f.Name))
5️⃣ How to add a View Filter?

Next, let's have a look at how to add view filters and apply some basic graphic overrides.

Firstly, get a list of all filters and decide which ones you want to add. I will get all filters that have the word 'Möbel' in their name.

Then, we can use the View.AddFilter method to add a filter to any view or view template. And if you want to apply filter graphic overrides, you can use the SetFilterOverrides method.

💡 Keep in mind that if you use SetFilterOverrides , then you don't even need to use the AddFilter method because it will also add the filter if necessary. But it won't hurt either.

Here is the code:

active_view = doc.ActiveView

all_par_filters = FilteredElementCollector(doc).OfClass(ParameterFilterElement).ToElements()
sel_filters     = [fil for fil in all_par_filters if 'Möbel' in fil.Name]

t = Transaction(doc, 'Add Filters')
t.Start()

for view_filter in sel_filters:
    # Add Filters
    active_view.AddFilter(view_filter.Id)

    # Create Random Color
    import random
    red   = random.randint(150,255)
    green = random.randint(150,255)
    blue  = random.randint(150,255)
    color = Color(red, green, blue)

    # Create Graphic Overides
    override_settings = OverrideGraphicSettings()
    override_settings.SetProjectionLineColor(color)
    override_settings.SetProjectionLineWeight(5)

    # Apply Overrides
    active_view.SetFilterOverrides(view_filter.Id, override_settings)

t.Commit()
6️⃣ How to Copy View Filters between Views?

Next, let's have a look how to copy View Filters between different views. And that's a very common thing to do to keep our standards up to date.

Let's actually make it like as a usable button, so you can actually start using it to control your filters.

1️⃣ Firstly, we need to get our views. I will create a simple function to get views by name, by simply iterating through all of them, until we get a match.

2️⃣ Then, we can get all the filters and prompt user to select Views Filters to copy, because we don't always need to copy everything.
💡 Make sure you get your filters with GetOrderedFilters to keep the same order.

3️⃣ Lastly, we can recreate these filters in a new view by applying the same overrides and Visibility. For that we need to get it with:

Then, we can apply the same settings by using

Here is the code:


#1️⃣ Get Views From/To
def get_view_by_name(view_name):
    """Get Views by exact Name."""
    views = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Views).WhereElementIsNotElementType().ToElements()
    for view in views:
        if view.Name == view_name:
            return view
    raise("Couldn't find view by name: {}".format(view_name))

view_from  = get_view_by_name('HG_UG 1_OK FFB')         #type: View
view_to    = get_view_by_name('HG_UG 1_OK FFB Copy 1')  #type: View

#2️⃣ Get and Select View Filters
filter_ids = view_from.GetOrderedFilters()
filters    = [doc.GetElement(f_id) for f_id in filter_ids]

sel_filters = forms.SelectFromList.show(filters,
                                 multiselect=True,
                                 name_attr='Name',
                                 button_name='Select View Filters')

#✅ Ensure Selected Filters
if not sel_filters:
    forms.alert("No View Filters were Selected.\n"
                "Please Try Again.", exitscript=True)

#3️⃣ Copy View Filters
with Transaction(doc, 'Copy ViewFilters') as t:
    t.Start()

    for view_filter in filters:

        #🅰️ Copy Filter Overrides
        overrides = view_from.GetFilterOverrides(view_filter.Id)
        view_to.SetFilterOverrides(view_filter.Id, overrides)

        #🅱️ Copy Enable/Visibility Controls
        visibility = view_from.GetFilterVisibility(view_filter.Id)
        enable     = view_from.GetIsFilterEnabled(view_filter.Id)

        view_to.SetFilterVisibility(view_filter.Id, visibility)
        view_to.SetIsFilterEnabled(view_filter.Id, enable)

    t.Commit()
7️⃣ Read View Filter Parameter, Categories, Rules

Now we are going to dive much deeper and look inside of View Filters. It sounds like a very simple step, but there are a few tricky things. So let me break it down for you.

We will copy the code we wrote earlier to get views and iterate through them.

I also want to make sure that I only work with views that have some filters applied. So let's add an if statement' to skip any view with less than 2 filters.

And while we are in development, we can use break to only make one iteration. That will be enough to look inside filters and figure out how to read them.

To do that, we need to do the following:

1. Get Filter Category Names

Firstly, we need to get the categories, and there is a little trick.

We can get a list of element ids, but we can't get categories by using doc.GetElement. You would get an error if you would use the following snippet:

#❌ Can't use GetElement
for c_id in cats_ids:
    print(c_id) #-20010000 <- Built-In Category
    cat = doc.GetElement(c_id)
    print(cat.Name)

Instead of that, we need to use Category.GetCategory method like this:

#✅ Category.GetCategory
for c_id in cats_ids:
    cat = Category.GetCategory(doc, c_id)
    print(cat.Name)
  1. Get Rule Value and Evaluator

Now let's try to get the Rule Value and Evaluator, and pay attention as it can get confusing with nested rules.

Firstly we need to the the main rule with GetElementFilter method. This will be the AND/OR Rule that combines everything else (see screenshot below).

Then we need to keep looking inside of this main rule by using GetFilters(). This will give you a list of all ElementParameterFilter inside of your main AND/OR Rule.

Then we can iterate through each of these filters and get their rules with GetRules. This can return different types of rules, depending on the parameter and the storage type of the value, such as:

💡Some of these rules are tricky to work with, but you will work with the first 4 in most cases.

Once you got your rules, you can get the value and evaluator. However, keep in mind that the value can be access with either RuleString for text or RuleValue for numeric values (valid for: Integer, Double, ElementId).

Here is a code example:

#2️⃣ Get Rule Value + Evaluator
main_rule = f.GetElementFilter()
# print(main_rule)

if main_rule:
    par_filters = main_rule.GetFilters()

    for par_filter in par_filters:
        # print(par_filter)

        rules = par_filter.GetRules()
        for rule in rules:
            # print(rule)
            print('-'*50)
            print('Rule:{}'.format(type(rule)))

            value, eval = None, None

            if isinstance(rule, FilterStringRule):
                value = rule.RuleString
                eval  = rule.GetEvaluator()

            elif isinstance(rule, FilterDoubleRule):
                value = rule.RuleValue
                eval  = rule.GetEvaluator()

            elif isinstance(rule, FilterIntegerRule):
                value = rule.RuleValue
                eval  = rule.GetEvaluator()

            elif isinstance(rule, HasNoValueFilterRule):
                pass

            elif isinstance(rule, FilterInverseRule):
                pass

            print('Rule Value: {}'.format(value))
            print('Rule Evaluator: {}'.format(eval))
  1. Read Rule Parameter

Lastly, we want to know what kind of parameter is being assigned to the rule.

For that, we will use GetRuleParameter. This will give you an ElementId of your parameter; however keep in mind that:

  • Built-In Parameters will have negative ElementId numbers (e.g. -300010)

  • Shared/Project Parameters will have positive ElementId numbers

And you will also notice that you will have no issues getting your shared parameter with doc.GetElement(p_id), but that's not going to work for Built-In Parameter.

So to get your BuiltInParameters, it's easier to iterate through all instances of BuiltInParameter Enumeration, convert it into an ElementId, and then compare if we get a match.

Here is a code snippet to do all that:

#3️⃣ Get Parameter
param_id   = rule.GetRuleParameter()
param_name = ''

#🅰️ Built-In Parameter
bips = BuiltInParameter.GetValues(BuiltInParameter)
for bip in bips:
    if ElementId(bip) == param_id:
        param_name = bip
        break

#🅱️ Shared Parameter
if not param_name:
    p = doc.GetElement(param_id)
    param_name = p.Name

print('Parameter: {}'.format(param_name))
Complete Snippet

Here is the full code for reading View Filter properties.

all_views_and_vt = FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Views).ToElements()
all_views        = [v for v in all_views_and_vt if not v.IsTemplate]
all_vt           = [v for v in all_views_and_vt if v.IsTemplate]

for view in all_views: #type: View
    filter_ids = view.GetOrderedFilters()
    view_linkify = output.linkify(view.Id, view.Name)

    if len(filter_ids) <2:
        continue

    output.print_md('---')
    print('View: {:.<50} has {} Filters'.format(view_linkify, len(filter_ids) ))

    # Iterate Through Filters
    for filter_id in filter_ids:
        f = doc.GetElement(filter_id)
        print('\n🔹ViewFilter{}'.format(f.Name))


        # 1️⃣ Get Filter Category Names
        cats_ids  = f.GetCategories()
        cat_names = [Category.GetCategory(doc, c_id).Name for c_id in cats_ids]
        print('Categories: {}'.format(', '.join(cat_names)))

        # #✅ Category.GetCategory
        # for c_id in cats_ids:
        #     cat = Category.GetCategory(doc, c_id)
        #     print(cat.Name)

        #❌ Can't use GetElement
        # for c_id in cats_ids:
        #     print(c_id) #-20010000 <- Built-In Category
        #     cat = doc.GetElement(c_id)
        #     print(cat.Name)

        #2️⃣ Get Rule Value + Evaluator
        main_rule = f.GetElementFilter()
        # print(main_rule)

        if main_rule:
            par_filters = main_rule.GetFilters()

            for par_filter in par_filters:
                # print(par_filter)

                rules = par_filter.GetRules()
                for rule in rules:
                    # print(rule)
                    print('-'*50)
                    print('Rule:{}'.format(type(rule)))

                    value, eval = None, None

                    if isinstance(rule, FilterStringRule):
                        value = rule.RuleString
                        eval  = rule.GetEvaluator()

                    elif isinstance(rule, FilterDoubleRule):
                        value = rule.RuleValue
                        eval  = rule.GetEvaluator()

                    elif isinstance(rule, FilterIntegerRule):
                        value = rule.RuleValue
                        eval  = rule.GetEvaluator()

                    elif isinstance(rule, HasNoValueFilterRule):
                        pass

                    elif isinstance(rule, FilterInverseRule):
                        pass

                    print('Rule Value: {}'.format(value))
                    print('Rule Evaluator: {}'.format(eval))



                    #3️⃣ Get Parameter
                    param_id   = rule.GetRuleParameter()
                    param_name = ''

                    #🅰️ Built-In Parameter
                    bips = BuiltInParameter.GetValues(BuiltInParameter)
                    for bip in bips:
                        if ElementId(bip) == param_id:
                            param_name = bip
                            break

                    #🅱️ Shared Parameter
                    if not param_name:
                        p = doc.GetElement(param_id)
                        param_name = p.Name

                    print('Parameter: {}'.format(param_name))

    break #🚧 Leave for Development only.

EF-Tools Example: ViewFilters: Create Legend

By the way, I've realized that I already had an example where I was reading View Filter properties to create legends from them. It might be a good idea to have a look if you want to do something similar.

👀 You can see the source code here: EF-Tools/ViewFilterLegends

Here is the video of how the final tool works:

👇Now back to this lesson.

8️⃣ Create View Filter

Now, we are getting to a more interesting part. It's time to create a new View Filter.

It's much simpler that reading about existing View Filters, but still there are a few steps to go through.

In general, we have to:

Let's break down these steps:

Create Categories

This is simple, just create a List[ElementId]() and then add Ids of your Categories.

#🐈 Select ViewFilter Category
cats = List[ElementId]()
cats.Add(ElementId(BuiltInCategory.OST_Walls))
Create Rules

There are 2 ways to create your rules.

  • You can use the class of a specific rule like FilterIntegerRule, FilterStringRule

  • Or you can use ParameterFilterRuleFactory.

The result is the same, but you can choose whatever method is easier for you.

Here is a code snippet to create the same rule for WallType's parameter - Function.

#1️⃣
pvp    = ParameterValueProvider(ElementId(BuiltInParameter.FUNCTION_PARAM))
rule_1 = FilterIntegerRule(pvp, FilterNumericEquals(), int(WallFunction.Exterior))

#2️⃣
rule_1 = ParameterFilterRuleFactory.CreateEqualsRule(ElementId(BuiltInParameter.FUNCTION_PARAM),
                                                      int(WallFunction.Exterior))
Combine Rules

If you want to have multiple rules, you would need to combine them in a List[FilterRule] like this:

#1️⃣ Rule 1 - Wall Function
pvp    = ParameterValueProvider(ElementId(BuiltInParameter.FUNCTION_PARAM))
rule_1 = FilterIntegerRule(pvp, FilterNumericEquals(), int(WallFunction.Exterior))

#2️⃣ Rule 2 - Comments = 'xxx'
pvp = ParameterValueProvider(ElementId(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS))
rule_2 = FilterStringRule(pvp, FilterStringContains(), 'xxx') # Revit API 2021 needs 4th argument (bool)

#➕ Combine Rules into List[FilterRule]
rules = List[FilterRule]([rule_1, rule_2])
Create an Element Parameter Filter

Now you need to convert your single or multiple rules into ElementParameterFilter.

#💪 Create an Element Parameter Filter
wall_filter = ElementParameterFilter(rule_1)  # Single
# wall_filter = ElementParameterFilter(rules) # Multiple
Create View Filter (ParameterFilterElement)

Finally, we can put it all together and create a view filter in our project. It's a one-liner where we just need to provide all these arguments we created.

#🎯 Create View Filter
view_filter = ParameterFilterElement.Create(doc, new_filter_name, cats, wall_filter)

After that, we can add this filter to one of the views and apply graphic overrides if necessary.

Complete Snippet - Create View Filter:

Here is a full snippet to avoid any confusion about how it works.

all_par_filters      = FilteredElementCollector(doc).OfClass(ParameterFilterElement).ToElements()
all_par_filter_names = [f.Name for f in all_par_filters]

with Transaction(doc, 'Create VewFilter') as t:
    new_filter_name = 'EF New Filter'

    if new_filter_name not in all_par_filter_names:
        t.Start()

        #🐈 Select ViewFilter Category
        cats = List[ElementId]()
        cats.Add(ElementId(BuiltInCategory.OST_Walls))

        #1️⃣ Rule 1 - Wall Function
        pvp    = ParameterValueProvider(ElementId(BuiltInParameter.FUNCTION_PARAM))
        rule_1 = FilterIntegerRule(pvp, FilterNumericEquals(), int(WallFunction.Exterior))

        # rule_1 = ParameterFilterRuleFactory.CreateEqualsRule(ElementId(BuiltInParameter.FUNCTION_PARAM),
        #                                                      int(WallFunction.Exterior))

        #2️⃣ Rule 2 - Comments = 'xxx'
        pvp = ParameterValueProvider(ElementId(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS))
        rule_2 = FilterStringRule(pvp, FilterStringContains(), 'xxx') # Revit API 2021 needs 4th argument (bool)

        #➕ Combine Rules into List[FilterRule]
        rules = List[FilterRule]([rule_1, rule_2])

        #💪 Create an Element Parameter Filter
        # wall_filter = ElementParameterFilter(rule_1)
        wall_filter = ElementParameterFilter(rules)

        #🎯 Create View Filter
        view_filter = ParameterFilterElement.Create(doc, new_filter_name, cats, wall_filter)

        #🎨 Set up color override for the filter
        blue = Color(0,0,255)
        override_settings = OverrideGraphicSettings()
        override_settings.SetCutLineColor(blue)
        override_settings.SetCutLineWeight(5)
        override_settings.SetSurfaceTransparency(50) # 50% transparency

        #✅ Apply the override to a view (e.g., active view)
        active_view = doc.ActiveView
        active_view.AddFilter(view_filter.Id)
        active_view.SetFilterOverrides(view_filter.Id, override_settings)

        t.Commit()

9️⃣ Create View Filters based on Wall Type Names

Lastly, let's create many view filters based on parameter values.

In my case, I will get all WallTypes in the project and create a filter for each one based on their name. It will be very simple if you use the code sample for creating a single view filter.

Here is the final code:

all_par_filters      = FilteredElementCollector(doc).OfClass(ParameterFilterElement).ToElements()
all_par_filter_names = [f.Name for f in all_par_filters]

wall_types      = FilteredElementCollector(doc).OfClass(WallType).ToElements()
wall_type_names = [Element.Name.GetValue(typ) for typ in wall_types]


with Transaction(doc, 'Create VewFilter') as t:
    t.Start()

    for wall_type_name in wall_type_names:
            filter_name = 'WallType_{}'.format(wall_type_name)

            if not filter_name in all_par_filter_names:

                #🐈 Select ViewFilter Category
                cats = List[ElementId]()
                cats.Add(ElementId(BuiltInCategory.OST_Walls))

                #1️⃣ Rule 1 - Wall Function
                pvp    = ParameterValueProvider(ElementId(BuiltInParameter.SYMBOL_NAME_PARAM))
                rule_1 = FilterStringRule(pvp, FilterStringEquals(), wall_type_name) # 4 arguments in Revit 2021!

                #💪 Create an Element Parameter Filter
                wall_filter = ElementParameterFilter(rule_1)

                #🎯 Create View Filter
                view_filter = ParameterFilterElement.Create(doc, filter_name, cats, wall_filter)


                #🎨 Get Color and Solid Pattern
                import random
                color = Color(random.randint(200, 255),random.randint(200, 255),random.randint(200, 255))
                all_patterns = FilteredElementCollector(doc).OfClass(FillPatternElement).ToElements()
                solid_pattern = [i for i in all_patterns if i.GetFillPattern().IsSolidFill][0]

                #🖌️ Create Override Settings
                override_settings = OverrideGraphicSettings()
                override_settings.SetSurfaceForegroundPatternId(solid_pattern.Id)
                override_settings.SetSurfaceForegroundPatternColor(color)

                #✅ Apply the override to a view (e.g., active view)
                active_view = doc.ActiveView
                active_view.AddFilter(view_filter.Id)
                active_view.SetFilterOverrides(view_filter.Id, override_settings)


    t.Commit()

Summary

And now you know a lot more about View Filters. I hope this will inspire you to create some cool tools for Revit and feel free to share in the community.

Happy Coding!

HomeWork

There was a lot about View Filters in this lesson.

As your homework, I want you to have a look at this code to learn how to create legends from View Filters.

It will be useful to see how to work with many different Rule Classes when you need to read Filter's parameters.

⌨️ Happy Coding!

Discuss the lesson:

P.S. Sometimes this chat might experience connection issues.
Please be patient or join via Discord app so you can get the most out of this community and get access to even more chats.

P.S. Sometimes this chat might experience connection issues.
Please be patient or join via Discord app so you can get the most out of this community and get access to even more chats.

© 2023-2024 EF Learn Revit API

© 2023-2024 EF Learn Revit API

© 2023-2024 EF Learn Revit API