Hi all,
Issue: I have a pyRevit script that copies and loads families from a source folder into a project. The script works fine for the copying part, but Revit completely freezes after the family loading/upgrading process is complete. The freezing happens after the script has reached the last part which is execution completed. Is it due to the family being upgraded to newer versions which takes up a lot of memory or merely forms.alert causing the freeze.
Any help would be greatly appreciated!
from pyrevit import forms
from pyrevit import revit, DB
from dfl_matrix.folderpathforfamilywise import main as folderpath_main
import clr
import os
import shutil
import re
def is_backup_file(filename):
return bool(re.search(r'\.\d{4}\.rfa$', filename))
def find_family_files(root_folder):
family_files = []
for root, _, files in os.walk(root_folder):
for file in files:
if file.endswith(".rfa") and not is_backup_file(file):
family_files.append(os.path.relpath(os.path.join(root, file), root_folder))
return family_files
def remove_config():
CONFIG_FILE_NAME = "config.json"
CONFIG_DIR = os.path.join(os.environ['APPDATA'], "pyRevit")
CONFIG_FILE_PATH = os.path.join(CONFIG_DIR, CONFIG_FILE_NAME)
try:
if os.path.exists(CONFIG_FILE_PATH):
os.remove(CONFIG_FILE_PATH)
except:
pass
def is_family_loaded(doc, family_name):
try:
collector = DB.FilteredElementCollector(doc).OfClass(DB.Family)
for fam in collector:
if fam.Name == family_name:
return True
return False
except:
return False
def is_subdirectory(child, parent):
try:
child = os.path.abspath(os.path.normpath(child))
parent = os.path.abspath(os.path.normpath(parent))
return parent == child or child.startswith(parent + os.sep)
except:
return False
class AutoUpgradeFamilyLoadOptions(DB.IFamilyLoadOptions):
def OnFamilyFound(self, familyInUse, overwriteParameterValues):
return True
def OnSharedFamilyFound(self, sharedFamily, familyInUse, source, overwriteParameterValues):
return True
def load_family_wise():
try:
DIRECTORY, DFLFAMILYLIBRARY_FOLDER, DFL2020_FOLDER = folderpath_main()
SOURCE_FOLDER = DFL2020_FOLDER
USER_FOLDER = DIRECTORY
if not os.path.exists(SOURCE_FOLDER):
forms.alert("The source folder does not exist: " + SOURCE_FOLDER, exitscript=True)
return
project_folder = forms.pick_folder(title="Select Project > Revit Families Folder")
if not project_folder:
forms.alert("No project folder selected. Script will exit.", exitscript=True)
return
if is_subdirectory(project_folder, USER_FOLDER) or \
is_subdirectory(project_folder, DIRECTORY) or \
os.path.abspath(project_folder) == os.path.abspath(DIRECTORY):
forms.alert("The project folder cannot be Design for Leisure - Documents or any of its subdirectories.",
exitscript=True)
return
remove_config()
family_files = find_family_files(SOURCE_FOLDER)
if not family_files:
forms.alert("No family files found in source folder.", exitscript=True)
return
selected_families = forms.SelectFromList.show(
family_files,
multiselect=True,
title="Select Families to Copy"
)
if not selected_families:
forms.alert("No families selected. Script will exit.", exitscript=True)
return
if len(selected_families) > 30:
response = forms.alert(
"You've selected {} families, which may cause Revit to freeze. "
"We recommend processing 30 or fewer at a time. Continue anyway?".format(len(selected_families)),
yes=True, no=True
)
if not response:
forms.alert("Script cancelled. Please try again with fewer families.", exitscript=True)
return
# Copy families
copied_families = []
for family in selected_families:
source_path = os.path.join(SOURCE_FOLDER, family)
target_path = os.path.join(project_folder, os.path.basename(family))
try:
target_dir = os.path.dirname(target_path)
if not os.path.exists(target_dir):
os.makedirs(target_dir)
shutil.copy2(source_path, target_path)
copied_families.append(family)
except:
continue
if not copied_families:
forms.alert("Failed to copy any families. Script will exit.", exitscript=True)
return
# Load families in small batches
BATCH_SIZE = 5
family_batches = [copied_families[i:i + BATCH_SIZE] for i in range(0, len(copied_families), BATCH_SIZE)]
doc = revit.doc
load_options = AutoUpgradeFamilyLoadOptions()
# Track results
loaded_families = []
failed_families = []
already_loaded_families = []
# Insert this tracking code inside the family loading loop:
# Replace the existing family loading section with:
for family_batch in family_batches:
with revit.Transaction("Load Family Batch"):
for family in family_batch:
family_path = os.path.join(project_folder, os.path.basename(family))
family_name = os.path.splitext(os.path.basename(family))[0]
try:
if is_family_loaded(doc, family_name):
already_loaded_families.append(family_name)
else:
loaded_family = clr.Reference[DB.Family]()
success = doc.LoadFamily(family_path, load_options, loaded_family)
if success:
loaded_families.append(family_name)
else:
failed_families.append(family_name)
except:
failed_families.append(family_name)
# Replace the final alert with:
result_message = ""
if loaded_families:
result_message += "LOADED ({}):\n".format(len(loaded_families))
for family in loaded_families[:10]: # Show max 10 to prevent dialog overflow
result_message += "- {}\n".format(family)
if len(loaded_families) > 10:
result_message += "... and {} more\n\n".format(len(loaded_families) - 10)
else:
result_message += "\n"
if failed_families:
result_message += "FAILED ({}):\n".format(len(failed_families))
for family in failed_families[:10]: # Show max 10 failed
result_message += "- {}\n".format(family)
if len(failed_families) > 10:
result_message += "... and {} more\n\n".format(len(failed_families) - 10)
else:
result_message += "\n"
result_message += "Already loaded: {}".format(len(already_loaded_families))
forms.alert("Project Folder: {}\n\n{}".format(project_folder, result_message), title="Family Loading Results")
except:
pass
if __name__ == "__main__":
load_family_wise()