Search code examples
revit-apirevitrevitpythonshellpyrevit

RevitPythonShell - IronPython.Runtime.UnboundNameException: global name 'doc' is not defined


im writing my first button in RPS but it seems like the init file is not being read on startup or when the button is clicked. this is the error I'm getting when trying to follow the tutorials provided on youtube.

IronPython.Runtime.UnboundNameException: global name 'doc' is not defined

I also got a similar error for the Filtered Element Collector until I copy and pasted the imports from the init script into my button file.

Has anyone had any issues or found any solutions for this?

here's the portion of my code where it errors out:

import os
import csv
import rpw
from rpw.ui.forms import Console
from rpw.ui.forms import SelectFromList

desktop = os.path.join(os.path.join(os.environ['USERPROFILE']), 'Desktop')
filepath = os.path.join(desktop, 'RevisionClouds.csv')

cl = FilteredElementCollector(doc)
cl.OfCategory(BuiltInCategory.OST_RevisionClouds)
cl.WhereElementIsNotElementType()

I'm in Revit 2019 with RPS running python 2.7.7

Here's my init.py file. i've added a couple more imports to it and it works fine with RPS and its console. but when it comes to the pyRevit Button form, it errors out at doc when its already defined in the init script.

init.py

# these commands get executed in the current scope
# of each new shell (but not for canned commands)

import clr
import rpw
from rpw import revit, db, ui, DB, UI

clr.AddReference('RevitAPI')
clr.AddReference('RevitAPIUI')
clr.AddReferenceByPartialName('PresentationCore')
clr.AddReferenceByPartialName("PresentationFramework")
clr.AddReferenceByPartialName('System')
clr.AddReferenceByPartialName('System.Windows.Forms')

from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Architecture import *
from Autodesk.Revit.DB.Analysis import *
from Autodesk.Revit.UI import *

from Autodesk.Revit import DB
from Autodesk.Revit import UI


uidoc = __revit__.ActiveUIDocument
doc = __revit__.ActiveUIDocument.Document

from Autodesk.Revit.UI import TaskDialog
from Autodesk.Revit.UI import UIApplication


def alert(msg):
    TaskDialog.Show('RevitPythonShell', msg)


def quit():
    __window__.Close()
exit = quit


def get_selected_elements(doc):
    """API change in Revit 2016 makes old method throw an error"""
    try:
        # Revit 2016
        return [doc.GetElement(id)
                for id in __revit__.ActiveUIDocument.Selection.GetElementIds()]
    except:
        # old method
        return list(__revit__.ActiveUIDocument.Selection.Elements)
selection = get_selected_elements(doc)
# convenience variable for first element in selection
if len(selection):
    s0 = selection[0]

#------------------------------------------------------------------------------
import clr
from Autodesk.Revit.DB import ElementSet, ElementId

class RevitLookup(object):
    def __init__(self, uiApplication):
        '''
        for RevitSnoop to function properly, it needs to be instantiated
        with a reference to the Revit Application object.
        '''
        # find the RevitLookup plugin
        try:
            rlapp = [app for app in uiApplication.LoadedApplications
                     if app.GetType().Namespace == 'RevitLookup'
                     and app.GetType().Name == 'App'][0]
        except IndexError:
            self.RevitLookup = None
            return
        # tell IronPython about the assembly of the RevitLookup plugin
        clr.AddReference(rlapp.GetType().Assembly)
        import RevitLookup
        self.RevitLookup = RevitLookup
        # See note in CollectorExt.cs in the RevitLookup source:
        self.RevitLookup.Snoop.CollectorExts.CollectorExt.m_app = uiApplication
        self.revit = uiApplication

    def lookup(self, element):
        if not self.RevitLookup:
            print 'RevitLookup not installed. Visit https://github.com/jeremytammik/RevitLookup to install.'
            return
        if isinstance(element, int):
            element = self.revit.ActiveUIDocument.Document.GetElement(ElementId(element))
        if isinstance(element, ElementId):
            element = self.revit.ActiveUIDocument.Document.GetElement(element)
        if isinstance(element, list):
            elementSet = ElementSet()
            for e in element:
                elementSet.Insert(e)
            element = elementSet
        form = self.RevitLookup.Snoop.Forms.Objects(element)
        form.ShowDialog()
_revitlookup = RevitLookup(__revit__)
def lookup(element):
    _revitlookup.lookup(element)

#------------------------------------------------------------------------------

# a fix for the __window__.Close() bug introduced with the non-modal console
class WindowWrapper(object):
    def __init__(self, win):
        self.win = win

    def Close(self):
        self.win.Dispatcher.Invoke(lambda *_: self.win.Close())

    def __getattr__(self, name):
        return getattr(self.win, name)
__window__ = WindowWrapper(__window__)

any and all help is appreciated!


Solution

  • In my experience the 'init' file is only good for using the console on the fly. When you deploy a script to a button (also called 'canned command'), you need to include all the up-front definitions from the 'init' file.

    That would explain why doc works fine when you run your script from the console, but isnt defined when deployed to a Ribbon button.

    Thats also what the first lines of the init file is referring to:

    # these commands get executed in the current scope
    # of each new shell (but not for canned commands)
    

    Try adding all the import and clr.AddReference lines of code into your deployed button file and see what happens