Sort View Filters by input

Hi @c.poupin,
it’s possible to reorder naturally by name the existing filters in view ? . Thanks in advance

Cheers

[Edit Moderation]
related topic

Hi @paris

I add a method, you can try this

import clr
import sys
import re
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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

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

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
app = uiapp.Application
sdkNumber = int(app.VersionNumber)

class FilterUtils():
	def __init__(self, view):
		self._view = view
		
	@property
	def FilterIds(self):
		filterIds = self._view.GetFilters()
		return filterIds
		
	@property
	def FilterNames(self):
		return [doc.GetElement(x).Name for x in self.FilterIds]
		
	@property
	def FilterOverrides(self):
		return [self._view.GetFilterOverrides(x) for x in self.FilterIds]

	@property
	def FilterVisibity(self):
		return [self._view.GetFilterVisibility(x) for x in self.FilterIds]
		
	@property
	def FilterEnabled(self):
		if sdkNumber > 2020:
			return [self._view.GetIsFilterEnabled(x) for x in self.FilterIds]
		else:
			return [None] * len(self.FilterIds)
		
	def SetFilterOnTop(self, filterName):
		try:
			indexFilter = self.FilterNames.index(serachFilter)
		except ValueError:
			indexFilter = None
		#
		older = self.FilterNames[:]
		if indexFilter is not None and len(self.FilterIds) > 0:
			datafilters = zip(self.FilterIds, self.FilterOverrides, self.FilterVisibity, self.FilterEnabled)
			datafilters.insert(0, datafilters.pop(indexFilter))
			#
			for i in self.FilterIds:
				self._view.RemoveFilter(i)
			#
			for fId, overrideVG, visuVG, fiEnabled in datafilters:
				self._view.AddFilter(fId)
				self._view.SetFilterOverrides(fId, overrideVG)
				self._view.SetFilterVisibility(fId, visuVG)
				if fiEnabled is not None:
					self._view.SetIsFilterEnabled(fId, fiEnabled)
			return self.FilterNames
	
	def __natural_sort(self, l): 
		convert = lambda text: int(text) if text.isdigit() else text.lower()
		alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
		return sorted(l, key=alphanum_key)
		
	def SetNaturalOrder(self):
		lst_datafilters = zip(self.FilterNames, self.FilterIds, self.FilterOverrides, self.FilterVisibity, self.FilterEnabled)
		lst_datafilters.sort(key = lambda x : self.__natural_sort(x[0]))
		#
		for i in self.FilterIds:
			self._view.RemoveFilter(i)
		#
		for f_Name, fId, overrideVG, visuVG, fiEnabled in lst_datafilters:
			self._view.AddFilter(fId)
			self._view.SetFilterOverrides(fId, overrideVG)
			self._view.SetFilterVisibility(fId, visuVG)
			if fiEnabled is not None:
				self._view.SetIsFilterEnabled(fId, fiEnabled)
		return self.FilterNames

toList = lambda x : x if hasattr(x, '__iter__') else [x]

lstViews = toList(UnwrapElement(IN[0]))
try:
	serachFilter = IN[1]
except:
	serachFilter = ""
TransactionManager.Instance.EnsureInTransaction(doc)
for v in lstViews:
	objFi = FilterUtils(v)
	newOrderFilters = objFi.SetNaturalOrder()
TransactionManager.Instance.TransactionTaskDone()	

OUT = newOrderFilters
3 Likes

Hi @c.poupin thank-you very much for your answer, what I have to feed into IN[1] ? the sorted list name choised?
Thanks in advance
Cheers

Try this version

  • EDIT 29/04/2023 (code fixed for CPython3)
  • EDIT 29/04/2023 (code fixed for _alphanum_key)
import clr
import sys
import re
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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

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

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
app = uiapp.Application
sdkNumber = int(app.VersionNumber)

class FilterUtils():
	def __init__(self, view):
		self._view = view
		
	@property
	def FilterIds(self):
		filterIds = self._view.GetFilters()
		return filterIds
		
	@property
	def FilterNames(self):
		return [doc.GetElement(x).Name for x in self.FilterIds]
		
	@property
	def FilterOverrides(self):
		return [self._view.GetFilterOverrides(x) for x in self.FilterIds]

	@property
	def FilterVisibity(self):
		return [self._view.GetFilterVisibility(x) for x in self.FilterIds]
		
	@property
	def FilterEnabled(self):
		if sdkNumber > 2020:
			return [self._view.GetIsFilterEnabled(x) for x in self.FilterIds]
		else:
			return [None] * len(self.FilterIds)
		
	
	def _alphanum_key(self): 
		convert = lambda text: int(text) if text.isdigit() else text.lower()
		# apply the function only at the 1st items
		alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key[0])]
		return alphanum_key
		
	def SetFilterOrder(self, lst_newOrder_Name = []):
		"""
		set order by input list filter Name -> lst_newOrder_Name
		if lst_newOrder_Name is empty -> set order by FilterName
		reurn new order filter name
		"""
		lst_datafilters = list(zip(self.FilterNames, self.FilterIds, self.FilterOverrides, self.FilterVisibity, self.FilterEnabled))
		if len(lst_newOrder_Name) == len(lst_datafilters):
			lst_datafilters.sort(key = lambda x : lst_newOrder_Name.index(x[0]))
		else:
			lst_datafilters.sort(key = self._alphanum_key())
		#
		# remove filters
		for i in self.FilterIds:
			self._view.RemoveFilter(i)
		#
		# set the new order
		for f_Name, fId, overrideVG, visuVG, fiEnabled in lst_datafilters:
			self._view.AddFilter(fId)
			self._view.SetFilterOverrides(fId, overrideVG)
			self._view.SetFilterVisibility(fId, visuVG)
			if fiEnabled is not None:
				self._view.SetIsFilterEnabled(fId, fiEnabled)
		
		return [x[0] for x in lst_datafilters]

toList = lambda x : x if hasattr(x, '__iter__') else [x]

lstViews = toList(UnwrapElement(IN[0]))
try:
	lst_newOrder_Name = toList(IN[1])
except:
	lst_newOrder_Name = []

TransactionManager.Instance.EnsureInTransaction(doc)
for v in lstViews:
	objFi = FilterUtils(v)
	newOrderFilters = objFi.SetFilterOrder(lst_newOrder_Name)
TransactionManager.Instance.TransactionTaskDone()	

OUT = newOrderFilters
3 Likes

Hi @c.poupin great, thanks for your answer it works as expected, the filters into views is now ordered but in the output of python node, the “newOrderFilters” is the old older


Thanks in advance

1 Like

Hi,
thanks, I updated the code here

2 Likes

The script I made with dynamo didn’t work. While it was fine in the Dynamo window, the order in the template did not change.
Current Template Names
![Dynamo Sort Names|690x176]
Template Name List Sort.dyn (16.3 KB)

Hi,
your dyn is not attached to your post

Hi,
Add script

Template Name List Sort.dyn (16.3 KB)

hi,
add this node

1 Like

hi,
Thank you. good working

1 Like

hi,
how can i use this script for section and 3d view?
Template Name List Sort.dyn (21.4 KB)
Script Kodu.txt (2.9 KB)

Hi @srknkklc

here a workaround

Python Script to get View Template and filters associated

import clr
import sys
import re
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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


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

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

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
app = uiapp.Application
sdkNumber = int(app.VersionNumber)

def get_FiltersName(v):
	lstFilterName = [doc.GetElement(fid).Name for fid in v.GetFilters()]
	return ueWrapper.Invoke(None, (v, False)), lstFilterName


toList = lambda x : x if hasattr(x, '__iter__') else [x]

lst3DViews = toList(UnwrapElement(IN[0]))

ueWrapper = next((m for m in clr.GetClrType(Revit.Elements.ElementWrapper)\
				.GetMethods() if m.Name == "Wrap"), None)

lst3dTemplate = [doc.GetElement(v.ViewTemplateId ) for v in lst3DViews]

OUT = [get_FiltersName(v) for v in lst3dTemplate]

hi,
Thank you for your interest. I tried what you did but it didn’t work for me. I want it to do the sorting in Section and 3d filter.
Is it possible to sort in plan section and 3d filter in one go. You can see the latest status in the attachment.
Template Name List Sort - 29.04.2023.dyn (24.9 KB)

If you want to sort by Name filters, put directly the list of view (there is an function to do that inside)

Python Script | ViewTemplate

import clr
import sys
import re
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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


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

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

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
app = uiapp.Application
sdkNumber = int(app.VersionNumber)


def toList(x):
    if isinstance(x, list):
        return x
    elif hasattr(x, "GetType") and x.GetType().GetInterface("IEnumerable") is not None:
        return x
    else :
        return [x]

lst3DViews = toList(UnwrapElement(IN[0]))


ueWrapper = next((m for m in clr.GetClrType(Revit.Elements.ElementWrapper)\
				.GetMethods() if m.Name == "Wrap"), None)

lst3dTemplate = [doc.GetElement(v.ViewTemplateId) for v in lst3DViews]

OUT = [ueWrapper.Invoke(None, (v, False)) for v in lst3dTemplate]

Python Script | sort Filters (code fixed)

import clr
import sys
import re
import System
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

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

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

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
app = uiapp.Application
sdkNumber = int(app.VersionNumber)

class FilterUtils():
	def __init__(self, view):
		self._view = view
		
	@property
	def FilterIds(self):
		filterIds = self._view.GetFilters()
		return filterIds
		
	@property
	def FilterNames(self):
		return [doc.GetElement(x).Name for x in self.FilterIds]
		
	@property
	def FilterOverrides(self):
		return [self._view.GetFilterOverrides(x) for x in self.FilterIds]

	@property
	def FilterVisibity(self):
		return [self._view.GetFilterVisibility(x) for x in self.FilterIds]
		
	@property
	def FilterEnabled(self):
		if sdkNumber > 2020:
			return [self._view.GetIsFilterEnabled(x) for x in self.FilterIds]
		else:
			return [None] * len(self.FilterIds)
		
	
	def _alphanum_key(self): 
		convert = lambda text: int(text) if text.isdigit() else text.lower()
		# apply the function only at the 1st items
		alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key[0])]
		return alphanum_key
		
	def SetFilterOrder(self, lst_newOrder_Name = []):
		"""
		set order by input list filter Name -> lst_newOrder_Name
		if lst_newOrder_Name is empty -> set order by FilterName
		reurn new order filter name
		"""
		lst_datafilters = list(zip(self.FilterNames, self.FilterIds, self.FilterOverrides, self.FilterVisibity, self.FilterEnabled))
		if len(lst_newOrder_Name) == len(lst_datafilters):
			lst_datafilters.sort(key = lambda x : lst_newOrder_Name.index(x[0]))
		else:
			lst_datafilters.sort(key = self._alphanum_key())
		#
		# remove filters
		for i in self.FilterIds:
			self._view.RemoveFilter(i)
		#
		# set the new order
		for f_Name, fId, overrideVG, visuVG, fiEnabled in lst_datafilters:
			self._view.AddFilter(fId)
			self._view.SetFilterOverrides(fId, overrideVG)
			self._view.SetFilterVisibility(fId, visuVG)
			if fiEnabled is not None:
				self._view.SetIsFilterEnabled(fId, fiEnabled)
		
		return [x[0] for x in lst_datafilters]

toList = lambda x : x if hasattr(x, '__iter__') else [x]

lstViews = toList(UnwrapElement(IN[0]))
try:
	lst_newOrder_Name = toList(IN[1])
except:
	lst_newOrder_Name = []

TransactionManager.Instance.EnsureInTransaction(doc)
for v in lstViews:
	objFi = FilterUtils(v)
	newOrderFilters = objFi.SetFilterOrder(lst_newOrder_Name)
TransactionManager.Instance.TransactionTaskDone()	

OUT = newOrderFilters
2 Likes

Thank you c.poupin,
good working Code

1 Like

Good day, I am wondering, is it ok that the old filtering ordering is changed. I tried this script but I should have the exact old filter ordering and new filter on the top of filter list, correct? but new filter goes on top but another filters set also maybe with sorting. But I would like to add new filter on top without changing previous filters ordering

Hi @viktornrozalite ,

here an example

create filter_v2.dyn (24.6 KB)

Note : this node does not return the ordered filters

image

1 Like

thank for response, Is it means I can get ordered filters if using view template?

GetOrderedFilters returns all ordered filters for view, thanks. Not sure about why use code inside code block node