An IUpdater sounds perfect for what you want to do. The IUpdater can realistically target anything you want, no matter how specifc. When you implement an IUpdater you specify what the updater will react to by specifying the “ChangeType” and giving it a filter or list of elements to define which elements it will accept.
If you wanted to create an IUpdater to modify the walls paremters when wall openings change, your trigger would look something like this.
UpdaterRegistry.AddTrigger(self._updater_id, ElementClassFilter(Wall), Element.GetChangeTypeGeometry())
Here an implementation of IUpdater that I use for reference. The purpose of this code is to set elevation tags prefix and suffix base on what it is referencing.
import re
from Autodesk.Revit.DB import (IUpdater, UpdaterId, UpdaterRegistry, ElementCategoryFilter, ChangePriority,
Element, ChangeType, SpotDimension, BuiltInParameter)
from revit_utils import category
class IUpdaterBase(IUpdater):
@classmethod
def uuid(cls):
import hashlib
from System import Guid
m = hashlib.md5()
m.update(cls.__name__.encode('utf-8'))
return Guid(m.hexdigest())
def __init__(self, addin_id, *args, **kwargs):
self._addin_id = addin_id
self._updater_id = UpdaterId(addin_id, self._uuid())
def GetUpdaterId(self):
return self._updater_id
def GetAdditionalInformation(self):
return 'Not Implemented'
def GetUpdaterName(self):
return self.__class__.__name__
def GetChangePriority(self):
return ChangePriority.Annotations
def register_updater(self):
UpdaterRegistry.RegisterUpdater(self, True)
self.register_triggers()
def register_triggers(self):
pass
def unregister_triggers(self):
UpdaterRegistry.RemoveAllTriggers(self._updater_id)
def Execute(self, data):
raise NotImplemented
class ElevationUpdater(IUpdaterBase):
def __init__(self, addin_id, *args, **kwargs):
super(ElevationUpdater, self).__init__(addin_id, args, kwargs)
self._updated_elements = []
def GetChangePriority(self):
return ChangePriority.Annotations
def register_triggers(self):
change_type = ChangeType.ConcatenateChangeTypes(Element.GetChangeTypeElementAddition(),
Element.GetChangeTypeAny()
)
filtered_category = category.elevation_tags.as_id
if UpdaterRegistry.IsUpdaterRegistered(self._updater_id):
UpdaterRegistry.RemoveAllTriggers(self._updater_id)
UpdaterRegistry.AddTrigger(self._updater_id, ElementCategoryFilter(filtered_category), change_type)
def Execute(self, data):
try:
doc = data.GetDocument()
added_ids = data.GetAddedElementIds()
modded_ids = data.GetModifiedElementIds()
if added_ids:
modified_elements = [doc.GetElement(eid) for eid in added_ids] # type: list[SpotDimension]
elif modded_ids:
modified_elements = [doc.GetElement(eid) for eid in modded_ids] # type: list[SpotDimension]
else:
return
for element in modified_elements:
# this is very important to prevent recursion
if element.UniqueId in self._updated_elements:
self._updated_elements.remove(element.UniqueId)
continue
self._updated_elements.append(element.UniqueId)
element_prefix = element.Prefix if element.Prefix else ""
re_rule = re.compile(r'(\d+ )?(\d+/\d+|\d+)" [A-Z]+(-[A-Z]+)?')
re_match = re_rule.search(element_prefix)
if re_match:
s, e = re_match.span()
pre_prefix, post_prefix = element_prefix[:s], element_prefix[e:]
else:
pre_prefix, post_prefix = '', element_prefix
for reference in list(element.References):
ref_element = doc.GetElement(reference)
ref_id = ref_element.Category.Id.IntegerValue
if ref_id == category.pipes:
size = ref_element.get_Parameter(BuiltInParameter.RBS_CALCULATED_SIZE).AsString().strip()
sys_abv = ref_element.get_Parameter(BuiltInParameter.RBS_DUCT_PIPE_SYSTEM_ABBREVIATION_PARAM).AsString()
element.Prefix = pre_prefix + '%s %s' % (size, sys_abv) + post_prefix
elif ref_id in (category.pipe_fittings, category.pipe_accessories):
if 'PIPE SLEEVE' in ref_element.Symbol.FamilyName:
element.Prefix = pre_prefix + ref_element.Name.rstrip('.') + post_prefix
except Exception as ex:
import sys
print('Error on line {}'.format(sys.exc_info()[-1].tb_lineno), type(ex).__name__, ex)