Dynamic Model Updater and Revit Events

Hallo pyRevit friends :slight_smile:

Im struggling to understand what the Dynamic model updater does and how this relates to Revit Events.

I found this thread where @eirannejad also created a implementation for the iUpdater to the pyRevit hooks.

Could someone please take the time to explain how all this works?
Here is an example usecase:

I want to update parameters of wall openings.
Looking at the available RevitEvents and the corresponing available Hooks there is nothing that really meets my needs. These events are firing way too often or not often enough.
I would wish for something that fires if a wall/wallopening gets changed. Is this where the iUpdater comes in the game? Can i really target specific elements with this method?

Is there any news for the pyRevit implemantation i should know?

Happy about any help :slight_smile:
Kind regards!

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):
    def uuid(cls):
        import hashlib
        from System import Guid

        m = hashlib.md5()
        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)

    def register_triggers(self):

    def unregister_triggers(self):

    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(),
        filtered_category = category.elevation_tags.as_id
        if UpdaterRegistry.IsUpdaterRegistered(self._updater_id):
            UpdaterRegistry.AddTrigger(self._updater_id, ElementCategoryFilter(filtered_category), change_type)

    def Execute(self, data):
            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]
            for element in modified_elements:
                # this is very important to prevent recursion
                if element.UniqueId in self._updated_elements:
                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:]
                    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)


Hello @Nicholas.Miles :slight_smile:

That are good news, so it´s not a waste of time if i read up on IUpdater!
Uh, I´m afraid of any code that has a class definition. I will study you code, test and be back with questions.
Thanks for your help!