Working on some code to load current (shared) family into a list of background opened family documents

So I need to load my current family into a list of families opened in the background. Initially I was trying to use Orchid package for this, but that was throwing an error that it failed to overwrite shared family. I am very new to the API but I started poking around in the API docs and found FloadOptions- just what I seemed to need.

I got most of the base for this code from a Gavin Crump node to open files in the background, and from a Python Pyrevit tutorial. Here’s the code as it stands now-

#thanks to Gavin Crump for the base code here 

import clr

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference("RevitAPI")
import Autodesk 
from Autodesk.Revit.DB import *
from Autodesk.Revit import DB

clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *

clr.AddReference('System')
from System.Collections.Generic import List

currDoc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application


def tolist(input):
    result = input if isinstance(input, list) else [input]
    return result


paths = tolist(IN[0])
docs = []

#opening a list of docs in the background and sending it to docs

for path in paths:
	try:
		doc = app.OpenDocumentFile(path)
		docs.append(doc)
	except:
		docs.append(None)

#from a Pyrevit Workshop https://www.notion.so/Python-Revit-Coding-Sessions-3cdc3a58519140e2a0df8248e918aed1

class FamilyLoaderOptionsHandler(DB.IFamilyLoadOptions):
    def OnFamilyFound(self, familyInUse, overwriteParameterValues):
        overwriteParameterValues = True
        return True

    def OnSharedFamilyFound(self, sharedFamily, familyInUse, source, overwriteParameterValues):
        source = DB.FamilySource.Family
        overwriteParameterValues = True
        return True

#mycode, attempting to iterate the LoadFamily method and Hassa's Floader class over my list of background docs

for doc in docs:
	TransactionManager.Instance.EnsureInTransaction(doc)

	fload_handler = FamilyLoaderOptionsHandler()
	doc.LoadFamily(currDoc, fload_handler)
	
	TransactionManager.Instance.TransactionTaskDone()

it throws this error when run:

Warning: IronPythonEvaluator.EvaluateIronPythonScript operation failed.
Traceback (most recent call last):
File “”, line 56, in
Exception: The input argument “document” of function `anonymous-namespace’::Transaction_constructor or one item in the collection is null at line 94 of file E:\Ship\2022_px64\Source\Revit\RevitDBAPI\gensrc\APIUndoTransactionProxy.cpp.
Parameter name: document

Hey,

So, a couple of thoughts…

This is unfortunately quite a hard one to get yourself into using the API.

The big big issue here, is what happens if the code doesn’t finish executing… As it stands, you are background opening Every family in a folder.

Unless you successfully shut them after you use them, then you can quickly end up making your Revit fall over.

So, the sequence by which you do things, and the error catching you have in place is going to be critical…

Personally, I would organise your lists so that you run through 1 family doc at a time, try to open it, try to load it and try to close it. At each step, if there is a fail you need to deal with the errors you get.

I would have an extremely simple test setup because you’re likely to have to be shutting and opening Revit while you get it right.

Into the actual code, I’m curious to know why you benefit from background opening the files? Can you just load directly from a directory? Which I think is a quite simple adjustment to your code? :slight_smile: Untested unfortunately…

# Load the Python Standard and DesignScript Libraries
import clr

clr.AddReference('RevitAPI')
from Autodesk.Revit import DB
from Autodesk.Revit.DB.Structure import *

clr.AddReference('RevitAPIUI')
from Autodesk.Revit.UI import *

clr.AddReference('System')
from System.Collections.Generic import List

clr.AddReference('RevitNodes')
import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument
uidoc=DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument

#Preparing input from dynamo to revit
filepath_List = IN[0]

class FamilyLoaderOptionsHandler(DB.IFamilyLoadOptions):
    def OnFamilyFound(self, familyInUse, overwriteParameterValues):
        overwriteParameterValues = True
        return True

    def OnSharedFamilyFound(self, sharedFamily, familyInUse, source, overwriteParameterValues):
        source = DB.FamilySource.Family
        overwriteParameterValues = True
        return True

#Do some action in a Transaction
TransactionManager.Instance.EnsureInTransaction(doc)
for filepath in filepath_List:
    fload_handler = FamilyLoaderOptionsHandler()
    OUT = doc.LoadFamily(filepath, fload_handler)

TransactionManager.Instance.TransactionTaskDone()

As to error catching, @c.poupin has posted an excellent piece which you might find interesting… [Dynamo =+ Python] Gestion des erreurs (context manager) ~ VoltaDynaBim

Personally, I look at the way that Archi-Lab is structured for most of mine, thanks Konrad :slight_smile: I think you might need to modify to nest a few Try statements…

if RunIt:
	try:
		errorReport = None
		# run checks
		checks = False
		if something:
			checks = True
		else:
			errorReport = "Some useful text for the user."
		TransactionManager.Instance.EnsureInTransaction(doc)
		if checks:
			output = something
		TransactionManager.Instance.TransactionTaskDone()
	except:
		# if error accurs anywhere in the process catch it
		import traceback
		errorReport = traceback.format_exc()
else:
	errorReport = "Please set RunIt to True."

#Assign your output to the OUT variable
if errorReport == None:
	OUT = output
else:
	OUT = errorReport #less useful for the user, but useful for you

Hope that is helpful :slight_smile:

Mark

2 Likes

Hi, @jedbjorn

Rather than opening each family, why not directly load the families into the (document) project with this method?

1 Like

the families in question are quite heavy. I am looking at a workflow for schematic and concept that would allow us to use import objects we create from their geometry and load those “dumb” families in with their scheduling paramters written prior to being converted and purged. but this would cause potential variations in the nested instances between families, so I am looking for a way to update their nested instances outside of a revit project

Hi,
here an example

import sys
import re
import clr
import System
from System.Collections.Generic import List
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
app = uiapp.Application

pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
sys.path.append(pyt_path)
import os
from os import walk


class FamilyOption(IFamilyLoadOptions):
	def __init__(self, booloverrideValue):
		self.booloverrideValue = booloverrideValue #boolean
		
	def OnFamilyFound(self,familyInUse,overwriteParameterValues):
		overwriteParameterValues.Value = self.booloverrideValue
		return True
		
	def OnSharedFamilyFound(self, sharedFamily, familyInUse, FamilySource, overwriteParameterValues):
		overwriteParameterValues.Value = self.booloverrideValue
		return True

directory = IN[0]
overrideParameters = IN[1]
list_rfa = []
for root, dirs, files in os.walk(directory):
	for file in files:
		if file.endswith(".rfa") and re.search(r"\.\d+\.rfa", file) is None :
			list_rfa.append(root + "\\" + file)


TransactionManager.Instance.ForceCloseTransaction()	
famOption =  FamilyOption(overrideParameters)
for fam_rfa_path in list_rfa:
	# open family in editor
	famdoc = app.OpenDocumentFile(fam_rfa_path)
	famManger = famdoc.FamilyManager
	# do some modifications in Families
	tfam = Transaction(famdoc, "Modify Family")
	tfam.Start()
	paraUniformatCode = famManger.get_Parameter(BuiltInParameter.UNIFORMAT_CODE)
	for fam_type in famManger.Types:
		famManger.CurrentType = fam_type
		famManger.Set(paraUniformatCode, "D5030902")
	tfam.Commit()
	tfam.Dispose()
	# load in Project
	result = famdoc.LoadFamily(doc, famOption)
	# close Family
	famdoc.Close()
	famdoc.Dispose()
4 Likes

So basically I have some really heavy famillies and I am using Orchid nodes to convert them to import instances and then writing their data to the “proxy” family. The proxy families end up at around 500KB down from 24 mb or so, and they are nested into other families. During concept and schematic we can work with these super lightwieght proxies, but because the nested families never end up in project, I need a workflow for updating all of the families they are nested into in the background. If I let my people do this without automation there will be a host of problems and they will make a mess of it, so I need to have a utility that can upgrade nested instances in bulk automatically. A lot of the questions I have been asking here recently are a part of this larger workflow. To let the cat out of the bag, we are putting whole units in families and have nested kitchens and bathrooms inside of the unit families. This is a part of a workflow that updates nested kitchens and baths in all of the units they are loaded into, but we proxy them first so they are super lightweight. then in DD we can load in and place just the single kitchen or single bath in our enlarged plan and document it without bogging the file size down. It allows for a multifamily project with 300-400 units only increase in file size by less that 10mb when only proxies are placed, which makes things WAY WAY faster for everything. Sync times, open times, load times. everything is speedy. I just need to automate as much of this as is possible within the API capabilities.

2 Likes