Dynamic Model Updaters

I am curious to hear opinions on how to best use the dynamic model updater.

I was looking at this (https://thebuildingcoder.typepad.com/blog/2020/12/simple-iupdater-and-other-tbc-updates.html) and it appears that the script is unregistering the updater and registering it every time the script is run. Is that necessary? or a best practice? or maybe I am interpreting it incorrectly.

I was playing around with an updater implementation in my startup file, and when I was done testing I commented it out to prevent it from loading. Now I get an error in every document I used it in saying it cannot be found. Should I have made it optional when it was first registered? Would that prevent the error? Should we always make them optional so when someone else opens the document without the updater, they don’t get an error?

Still playing around with it so I have these general questions about best practices. Appreciate any insight from those of you who have used the dynamic updater before.

Thanks!

1 Like

It really depends on the function of the updater, but typically you would probably want to register it at startup. If you needed to have control of when the updater is active you would probably want to create a button to enable/disable the updater. The biggest benefit of registering/unregistering each time is that you can more easily test/develop the updater. When you unregister and register each time it refreshes the updater with the changes you have made to your code. I say “refresh” because even if you unregister I believe it is still saved inside the updaterregistry until you re-register it.

Yes. I would recommend making the updater optional unless the updater is required to maintain document/data integrity. As a personal example, I created an updater to dynamically generate values for shared parameters that are used for one of our tags. If the updater is optional and not active those parameters may end up being incorrect and may cause big issues if you do not realize it. Although to be completely honest this updater is still set to optional (even though it really shouldn’t be optional), because of the errors that you mentioned.

2 Likes

This is very helpful information thank you!

I’ve built the updater to throw a dialog box when a revision cloud is created so a user can add info to it. It’s not critical, but more of a forceful reminder of best practices, so making it optional is perfectly fine in my case. And I want it to always be active, so I’m adding it to the startup file, which makes unregistering it a little messy. I’m thinking I use an app closing event to unregister it just to keep it clean and avoid the mistake I made before that now causes a registry error every time I open my model.

1 Like

I don’t think you need to unregister it if it is set to optional. I do not unregister on app/doc close and have never had issues when the updater is set to optional. But I’ve never done any testing to see if there may be side effects if it is not unregistered on close, so it may be best practice to unregister on close.

1 Like

What does “set to optional” mean when talking about the dynamicmodelupdater?

@Gerhard.P https://apidocs.co/apps/revit/2024/edffd44c-2511-c9ee-f330-5cd77414d0e9.htm
It refers to the optional boolean you can pass into the RegisterUpdater method of the UpdaterRegistry. If you set it to False, Revit will throw an error if you open a document without the updater running.

1 Like

Thanks @Nicholas.Miles !

My attempt at registering/unregistering, since debuging/changing the code is a pain if you don´t unregister, as Nicholas mentioned, I´m using taskdialoges to register/unregister:

from Autodesk.Revit.DB import IUpdater, UpdaterId, ElementId, UpdaterRegistry
from Autodesk.Revit.DB import Element, ElementCategoryFilter, BuiltInCategory, ChangePriority,Element, ElementId, ElementParameterFilter, ParameterValueProvider, FilterStringEquals, BuiltInParameter
from Autodesk.Revit.UI import TaskDialog,TaskDialogCommonButtons,TaskDialogResult
from Autodesk.Revit.DB import Transaction
from System import Guid

# Define the SimpleUpdater
class SimpleUpdater(IUpdater):
    def __init__(self, doc):
        self._doc = doc
    def Execute(self, data):
        print("Updater was triggered!")
        added_ids = data.GetAddedElementIds()
        for id in added_ids:
            viewport = self._doc.GetElement(id)
            view_id = viewport.ViewId
            view = self._doc.GetElement(view_id)

            # Fetch parameters
            try:
                sheet = self._doc.GetElement(viewport.SheetId)  # Get the sheet containing the viewport
                from Autodesk.Revit.DB import BuiltInParameter
                sheet_number_param = sheet.get_Parameter(BuiltInParameter.SHEET_NUMBER)  # Fetch sheet number
                detail_number_param = viewport.get_Parameter(BuiltInParameter.VIEWPORT_DETAIL_NUMBER)  # Fetch detail number
                suffix_param = view.LookupParameter('View Name Suffix (AFRY)')
                if sheet_number_param and detail_number_param:
                    sheet_number = sheet_number_param.AsString()
                    detail_number = detail_number_param.AsString()

                    # Construct the new view name
                    new_name = "{} - {} - {}".format(sheet_number, "Section", detail_number)

                    # Set the new name to the view
                    view.Name = new_name
                    print("New view name set to: " + new_name)

                else:
                    print("Failed to retrieve sheet or detail number for view: " + view.Name)

            except Exception as e:
                print("Error renaming the view: {}".format(e))
        
        # Handle modified elements
        modified_ids = data.GetModifiedElementIds()
        print("Number of modified element IDs: {}".format(len(modified_ids)))
        for id in modified_ids:
            viewport = self._doc.GetElement(id)
            view_id = viewport.ViewId
            view = self._doc.GetElement(view_id)

            # Fetch parameters
            try:
                sheet = self._doc.GetElement(viewport.SheetId)  # Get the sheet containing the viewport
                from Autodesk.Revit.DB import BuiltInParameter
                sheet_number_param = sheet.get_Parameter(BuiltInParameter.SHEET_NUMBER)  # Fetch sheet number
                detail_number_param = viewport.get_Parameter(BuiltInParameter.VIEWPORT_DETAIL_NUMBER)  # Fetch detail number
                
                if sheet_number_param and detail_number_param:
                    sheet_number = sheet_number_param.AsString()
                    detail_number = detail_number_param.AsString()

                    # Construct the new view name
                    new_name = "{} - {} - {}".format(sheet_number, "Section", detail_number)

                    # Set the new name to the view
                    view.Name = new_name
                    print("New view name set to: " + new_name)

                else:
                    print("Failed to retrieve sheet or detail number for view: " + view.Name)

            except Exception as e:
                print("Error renaming the view: {}".format(e))
    def GetUpdaterId(self):
        # Return the unique identifier for this updater
        return self.updater_id

    def GetUpdaterName(self):
        return 'SimpleUpdaterName'

    def GetAdditionalInformation(self):
        return 'A simple updater for testing purposes'

    def GetChangePriority(self):
        return ChangePriority.Annotations

    def Initialize(self):
        # This is where you can add trigger conditions for the updater
        pass

    def Uninitialize(self):
        pass

# Get the current document and application
doc = __revit__.ActiveUIDocument.Document
app = __revit__.Application

# Create an instance of the updater
updater = SimpleUpdater(doc)

# Create a unique Guid for the updater
guid_string = "676d4886-3106-4b53-b1ac-9183947d3cdf"
guid = Guid(guid_string)

# Create an UpdaterId using the AddInId of the current application and the unique Guid
updater_id = UpdaterId(app.ActiveAddInId, guid)

# Set the identifier in the updater instance
updater.updater_id = updater_id

# Create a filter for views
viewport_filter = ElementCategoryFilter(BuiltInCategory.OST_Viewports)

# Get the ElementId for the VIEWPORT_DETAIL_NUMBER parameter
param_id = ElementId(BuiltInParameter.VIEWPORT_DETAIL_NUMBER)

# If the updater is already registered, ask the user if they want to unregister it
if UpdaterRegistry.IsUpdaterRegistered(updater_id, doc):
    dialog_result = TaskDialog.Show("Updater Options", 
                                    "The updater is already registered. Do you want to unregister it?", 
                                    TaskDialogCommonButtons.Yes | TaskDialogCommonButtons.No)

    # Depending on the user's choice, unregister the updater
    if dialog_result == TaskDialogResult.Yes:
        # User chose to unregister the updater
        UpdaterRegistry.UnregisterUpdater(updater_id, doc)
        TaskDialog.Show('Success', 'Updater has been unregistered!')

else:
    # If the updater is not registered, then register it
    UpdaterRegistry.RegisterUpdater(updater, doc)
    # Add trigger for the modification of the VIEWPORT_DETAIL_NUMBER parameter
    UpdaterRegistry.AddTrigger(updater_id, viewport_filter, Element.GetChangeTypeParameter(param_id))
    # Add trigger for the addition of a new viewport to a sheet
    UpdaterRegistry.AddTrigger(updater_id, viewport_filter, Element.GetChangeTypeElementAddition())
    TaskDialog.Show('Success', 'Updater has been registered and trigger has been set!')
1 Like

Now I understand the initial question of the thread,it´s all about this window that I know too good from multiple projects:

So is there no way of “cleaning” the “necessary” third party updaters?

Is it only me or has the option “don´t warn again” absolutely no effect?

Edit:

OK, auditing the model will solve this problem! Also working for cloud models!

Hi @Gerhard.P , I have tried your code, and for make it work each time I open a model I need to reload pyRevit, is this the way it should work?, is possible to make it work with out reloading pyrevit each time I open a model, I mean automatically when opening a model?, Thanks!.