As you've already figured out, it's not possible to create a copy of a FilteredElementCollector
. However, you could create a functionally identical one by recording which methods are called on the original and duplicating those method calls when you need to make a copy. The class below does just that:
class CopyableFilteredElementCollector(FilteredElementCollector):
def __init__(self, doc):
# Initialize the underlying FilteredElementCollector
FilteredElementCollector.__init__(self, doc)
# Save the document
self._doc = doc
# Calls to methods that constrain the FilteredElementCollector will be recorded here
self._log = []
def copy(self):
# Create a brand new instance
copy = CopyableFilteredElementCollector(self._doc)
# Replay the log on the new instance
for method_name, args in self._log:
getattr(copy, method_name)(*args)
# The copy now references the same document as the original,
# and has had the same methods called on it
return copy
We need to override each method that restrict the elements returned by the FilteredElementCollector
to record its invocation in the log. The most straightforward way to do that would be by defining override methods in the class body like this:
def OfCategory(self, category):
# Add an entry to the log containing the name of the method that was called
# and a tuple of its arguments that can be expanded with the splat operator
self._log.append(('OfCategory', (category,)))
# Call the original method
FilteredElementCollector.OfCategory(self, category)
# Return self just like the original method does
return self
Defining an override for every single method gets repetitive, so let's employ some metaprogramming voodoo instead:
# Methods of FilteredElementCollector that constrain which elements it returns
constraint_methods = [
'OfCategory',
'OfClass',
'WhereElementIsElementType',
'WhereElementIsNotElementType',
'WherePasses',
# et cetera
]
# A factory function that produces override methods similar to the one defined above
def define_method(method_name):
def method(self, *args):
self._log.append((method_name, args))
getattr(FilteredElementCollector, method_name)(self, *args)
return self
return method
# Create an override for each method and add it to the CopyableFilteredElementCollector class
for method_name in constraint_methods:
setattr(CopyableFilteredElementCollector, method_name, define_method(method_name))
I won't go as far as to claim that my approach is a good idea, but it's an option if your use case requires making copies of a FilteredElementCollector
rather than creating new ones from scratch.