Creating View filter and setting up the rules

I have been trying to create the viewFilter that looks at a shared parameter and checks if it contains the word “New”. While I was writing up what I could not figure out I managed to solve it, so I’m leaving this post for any poor souls that are trying to do what I did and vent some frustration with the Revit API docs.

My code:

import clr
import sys
sys.path.append('C:\Program Files (x86)\IronPython 2.7\Lib')
import System
from System import Array
from System.Collections.Generic import *
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)
clr.ImportExtensions(Revit.GeometryConversion)
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager 
from RevitServices.Transactions import TransactionManager 

clr.AddReference("RevitAPI")
clr.AddReference("RevitAPIUI")

import Autodesk 
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication 
app = uiapp.Application 
uidoc = uiapp.ActiveUIDocument
categories = doc.Settings.Categories


# The inputs to this node will be stored as a list in the IN variables.
paraName=UnwrapElement(IN[0])
filterWord1=UnwrapElement(IN[1])
# Place your code below this line

#for testing purposes, remove later and read from input
paraName="BIMEXPERTS_Clash_Status"
filterWord1="New"
#-------------------------------------

filterParam=0
shared_params = FilteredElementCollector(doc).OfClass(SharedParameterElement)
for i in shared_params:
	if(i.Name==paraName):
		filterParam = i


#get category
category=Category.GetCategory(doc,BuiltInCategory.OST_GenericModel).Id
	
#cast categories
catlist=[]
catlist.append(category)
typedCatList=List[ElementId](catlist)
#make rule
rules=[]
rules.append(ParameterFilterRuleFactory.CreateContainsRule(filterParam.Id,filterWord1,False))


TransactionManager.Instance.EnsureInTransaction(doc)

#creating the filter
filter=ParameterFilterElement.Create(doc,"BIMEXPERTS_Clash_Status_New",typedCatList)

#Connecting them
rules2=ElementParameterFilter(rules[0])
filter.SetElementFilter(rules2)

TransactionManager.Instance.TransactionTaskDone()


OUT=0

i have been trying to get it to work by reading this doc:
https://www.revitapidocs.com/2020/b231dc85-516a-5e75-c634-c6cd81b43fc5.htm

but this function ( CreateElementFilterFromFilterRules(filterRules) ) does not exist in the API.

Then i found this post on the forums:

that says you should use LogicalAndFilter but I could not manage to get it to work.

You create a filter by calling the class ParameterFilterElement. This works fine, it creates the filter but it has no rules. The function that creates the filter WITH THE RULES is gone in Revit 2020. That’s why old posts won’t help you.

rules are created by ParameterFilterRuleFactory.

What you need now is to set these rules on the filter object. But you cant do that. The set function needs an ElementFilter class but you have a FilterRule class.
In order to combine these to you need to make an ElementParameterFilter class that can take the rules, and then you can pass this to the SetElementFilter function.

Now look at the names of these classes
ParameterFilterElement for creation and
ElementParameterFilter for connection. They are really similar. You could be reading one thinking its the other. :expressionless:
I have been going around in circles between these 2 clases trying to figure out what the duck is going on.

I hope this helps anybody that comes across it.

3 Likes

Hey,

Sorry for digging up that thread but it was really useful for me so I’d like to add my contribution.

I needed to create 2 view filters to highlight elements with missing parameters while turning all the others transparent. Most of the informations I needed where already in your script but I also needed a way to use the LogicalAndFilter and LogicalOrFilter methods so I dug in the API docs and came up with this solution :

import clr

clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Structure import *

clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *

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

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument
uidoc=DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument

map = {}

def createMap():
	bindingMap = doc.ParameterBindings
	it = bindingMap.ForwardIterator()
	it.Reset()
	while (it.MoveNext()):
		map[it.Key.Name]=it.Current.Categories

def hasParameters(category,params):
	match = 0
	for i in params:
		for j in map[i.Name]:
			if j.Name==category.Name:
				match +=1
	return match==len(params)
	
run = IN[1]

if(run):
	# Select parameters
	paraName=UnwrapElement(IN[0])
	filterParam=[0 for i in paraName]
	shared_params = FilteredElementCollector(doc).OfClass(SharedParameterElement)
	for i in shared_params:
		for j in range(len(paraName)):
			if i.Name==paraName[j]:
				filterParam[j] = i
	
	#Create Maping between categories and parameters
	createMap()
	
	# Select Categories
	cats = doc.Settings.Categories
	catsWithParam = [i for i in cats if hasParameters(i,filterParam)]
	catsId = [i.Id for i in catsWithParam]
	catsFilterable = ParameterFilterUtilities.RemoveUnfilterableCategories(List[ElementId](catsId))
	
	#Ensure in transaction
	TransactionManager.Instance.EnsureInTransaction(doc)
	
	
	#Create Filter and delete others with same name if already existing
	filterNameIN = "All Parameters Have a Value"
	filterNameOUT = "At Least One Parameter is Empty"
	
	filter = None
	allFilters = list(FilteredElementCollector(doc).OfClass(ParameterFilterElement).ToElements())
	names=[i.Name for i in allFilters]
	for i in range(len(allFilters)):
		if names[i]==filterNameIN or names[i]==filterNameOUT:
			doc.Delete(allFilters[i].Id)
	filterIN = ParameterFilterElement.Create(doc, filterNameIN, List[ElementId](catsFilterable))
	filterOUT = ParameterFilterElement.Create(doc, filterNameOUT, List[ElementId](catsFilterable))
				
	#Create Rules 
	rulesIN=[]
	rulesOUT=[]
	for i in filterParam:
		rulesIN.append(ParameterFilterRuleFactory.CreateHasValueParameterRule(i.Id))
		rulesOUT.append(ParameterFilterRuleFactory.CreateHasNoValueParameterRule(i.Id))
	elemFiltersIN = []
	elemFiltersOUT=[]
	for i,j in zip(rulesIN,rulesOUT):
		elemFiltersIN.append(ElementParameterFilter(i))
		elemFiltersOUT.append(ElementParameterFilter(j))
	INRule = LogicalAndFilter(elemFiltersIN)
	OUTRule = LogicalOrFilter(elemFiltersOUT)
	
	#Apply the Rules on the filters
	filterIN.SetElementFilter(INRule)
	filterOUT.SetElementFilter(OUTRule)
	
	#Add the filters to the duplicated view
	view = UnwrapElement(IN[2])
	dup = view.Duplicate(ViewDuplicateOption.Duplicate)
	dupView = doc.GetElement(dup)
	dupView.AddFilter(filterIN.Id)
	dupView.AddFilter(filterOUT.Id)
	
	#Get the solid fillpattern and red color:
	fpe = FilteredElementCollector(doc).OfClass(FillPatternElement)
	solidPatternId = 0
	for i in fpe:
		pattern = i.GetFillPattern()
		if pattern.IsSolidFill == True:
			solidPatternId = i.Id
	red = Color(255,0,0)
	
	#Apply graphic overrides to filters
	ogsIN = dupView.GetFilterOverrides(filterIN.Id)
	ogsIN.SetSurfaceTransparency(80.0)
	dupView.SetFilterOverrides(filterIN.Id,ogsIN)
	
	ogsOUT = dupView.GetFilterOverrides(filterOUT.Id)
	ogsOUT.SetCutForegroundPatternColor(red)
	ogsOUT.SetSurfaceForegroundPatternId(solidPatternId)
	ogsOUT.SetCutBackgroundPatternColor(red)
	ogsOUT.SetSurfaceBackgroundPatternId(solidPatternId)
	ogsOUT.SetSurfaceForegroundPatternColor(red)
	ogsOUT.SetSurfaceForegroundPatternId(solidPatternId)
	ogsOUT.SetSurfaceBackgroundPatternColor(red)
	ogsOUT.SetSurfaceBackgroundPatternId(solidPatternId)
	ogsOUT.SetSurfaceTransparency(0.0)
	dupView.SetFilterOverrides(filterOUT.Id,ogsOUT)
	
	#Finish the transaction
	TransactionManager.Instance.TransactionTaskDone()

Writing this script I came across some unexpected problems of filterable categories and parameters. As I wanted to apply the filter on every possible category I often came across the same error :

Exception: One of the given rules refers to a parameter that does not apply to this filter’s categories. Parameter name: elementFilter

To resolve this I first had to filter categories “not filterable” using the RemoveUnfilterableCategories method. Then I created a mapping table between the document’s categories and the parameters attributed to them using the ParameterBindings method to apply the filter only to the categories where all the parameters could apply.

Note that this script had the vocation to be used multiple times, reason for which I delete the filters if they already exists to create new ones for the new inputs.

I hope my contribution can help anyone that ends up in this thread.

4 Likes