Nicholas Rawlings
Nicholas Rawlings

Reputation: 31

NameError raised by IUpdater registered through pyRevit

I'm attempting to implement a Dynamic Model Updater as part of a pyRevit extension. However, I am unable to instantiate classes, invoke functions, or reference constants from the updater's Execute method.

As an example, let's assume make a pushbutton that executes this script:

from pyrevit import HOST_APP, DB
from System import Guid
from Autodesk.Revit.UI import TaskDialog

def do_thing(wall):
    TaskDialog.Show('ExampleUpdater', 'Updating {}'.format(wall.Id.IntegerValue))

class ExampleUpdater(DB.IUpdater):

    def __init__(self, addin_id):
        self.id = DB.UpdaterId(addin_id, Guid("70f3be2d-b524-4798-8baf-5b249c2f31c4"))

    def GetUpdaterId(self):
        return self.id

    def GetUpdaterName(self):
        return "Example Updater"

    def GetAdditionalInformation(self):
        return "Just an example"

    def GetChangePriority(self):
        return DB.ChangePriority.Views

    def Execute(self, data):
        doc = data.GetDocument()
        
        for id in data.GetModifiedElementIds():
            wall = doc.GetElement(id)
            try:
                do_thing(wall)

            except Exception as err:
                wall.ParametersMap["Comments"].Set("{}: {}".format(err.__class__.__name__, err))


updater = ExampleUpdater(HOST_APP.addin_id)

if DB.UpdaterRegistry.IsUpdaterRegistered(updater.GetUpdaterId()):
    DB.UpdaterRegistry.UnregisterUpdater(updater.GetUpdaterId())
    
DB.UpdaterRegistry.RegisterUpdater(updater)
wall_filter = DB.ElementCategoryFilter(DB.BuiltInCategory.OST_Walls)
change_type = DB.Element.GetChangeTypeAny()
DB.UpdaterRegistry.AddTrigger(updater.GetUpdaterId(), wall_filter, change_type)

If I move a wall (after clicking the button to register the updater, of course), an exception is raised and stored in the wall's comments property: NameError: name 'do_thing' is not defined. Similar exceptions are also raised when I try to call functions like TaskDialog.Show that were not originally defined in Python.

However, if I register the same updater in the RevitPythonShell, it works as expected:

from System import Guid
from Autodesk.Revit.UI import TaskDialog

def do_thing(wall):
    TaskDialog.Show('ExampleUpdater', 'Updating {}'.format(wall.Id.IntegerValue))

class ExampleUpdater(IUpdater):

    def __init__(self, addin_id):
        self.id = UpdaterId(addin_id, Guid("c197ee15-47a9-4cf7-b12c-43b863497826"))

    def GetUpdaterId(self):
        return self.id

    def GetUpdaterName(self):
        return "Example Updater"

    def GetAdditionalInformation(self):
        return "Just an example"

    def GetChangePriority(self):
        return ChangePriority.Views

    def Execute(self, data):
        doc = data.GetDocument()
        
        for id in data.GetModifiedElementIds():
            wall = doc.GetElement(id)
            try:
                do_thing(wall)

            except Exception as err:
                wall.ParametersMap["Comments"].Set("{}: {}".format(err.__class__.__name__, err))


updater = ExampleUpdater(__revit__.Application.ActiveAddInId)

if UpdaterRegistry.IsUpdaterRegistered(updater.GetUpdaterId()):
    UpdaterRegistry.UnregisterUpdater(updater.GetUpdaterId())
    
UpdaterRegistry.RegisterUpdater(updater)
wall_filter = ElementCategoryFilter(BuiltInCategory.OST_Walls)
change_type = Element.GetChangeTypeAny()
UpdaterRegistry.AddTrigger(updater.GetUpdaterId(), wall_filter, change_type)

What is the difference between the two environments that allows the updater to work in RevitPythonShell but not in pyRevit?

Revit 2019, pyRevit 4.7.4, IronPython 2.7.7

Upvotes: 2

Views: 353

Answers (2)

Jean-Marc Couffin
Jean-Marc Couffin

Reputation: 1

Making the bundle with a persistent engine did the trick:

engine:
  persistent: true

Upvotes: 0

Nicholas Rawlings
Nicholas Rawlings

Reputation: 31

Building on @Callum's suggestion, I was able to work around the problem by saving references to the imported resources I needed in the updater's __init__ method. My example updater now looks like this:

from pyrevit import HOST_APP, DB
from System import Guid
from Autodesk.Revit.UI import TaskDialog

class ExampleUpdater(DB.IUpdater):

    def __init__(self, addin_id):
        self.id = DB.UpdaterId(addin_id, Guid("70f3be2d-b524-4798-8baf-5b249c2f31c4"))
        self.TaskDialog = TaskDialog

    def GetUpdaterId(self):
        return self.id

    def GetUpdaterName(self):
        return "Example Updater"

    def GetAdditionalInformation(self):
        return "Just an example"

    def GetChangePriority(self):
        return DB.ChangePriority.Views

    def Execute(self, data):
        doc = data.GetDocument()
        
        for id in data.GetModifiedElementIds():
            wall = doc.GetElement(id)
            try:
                self.do_thing(wall)

            except Exception as err:
                wall.ParametersMap["Comments"].Set("{}: {}".format(err.__class__.__name__, err))
    
    def do_thing(self, wall):
        self.TaskDialog.Show('ExampleUpdater', 'Updating {}'.format(wall.Id.IntegerValue))

Simply making do_thing a method of ExampleUpdater wasn't enough, and saving a reference to the do_thing function (i.e. by adding self.do_thing = do_thing in __init__) didn't work either. In both cases, the updater raised a NameError that said global name 'TaskDialog' is not defined.

Upvotes: 1

Related Questions