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.
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.
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.
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.
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!')