My script creates my “KeyplanAreaToggle” parameter in the titleblock family only whether it is set to False or True (line 73). I need this as a titleblock instance parameter. I get an error message: PyRevitLoader Error: Failed to save or reload the family: File path must already be set to be able to save the document. It needs to be first saved using the SaveAs method instead.
The script:
from Autodesk.Revit.DB import *
from pyrevit import script
from Autodesk.Revit.UI import TaskDialog
# Get the active Revit document
doc = __revit__.ActiveUIDocument.Document
# Get the currently selected elements
selection = __revit__.ActiveUIDocument.Selection.GetElementIds()
if not selection:
TaskDialog.Show("Error", "No elements selected.")
script.exit()
# Function to check if a parameter is already defined in the family
def is_parameter_defined(family_manager, param_name):
for param in family_manager.Parameters:
if param.Definition.Name == param_name:
return True
return False
# Loop through selected elements and process if it's a Title Block
for element_id in selection:
element = doc.GetElement(element_id)
# Check if the element is a Title Block (usually it has the Title Blocks category)
if element.Category and element.Category.Name == "Title Blocks":
titleblock = element
# Ensure the element is a FamilyInstance
if isinstance(titleblock, FamilyInstance):
# Get the FamilySymbol (the type of the family instance)
family_symbol = titleblock.Symbol
family = family_symbol.Family
if not family:
TaskDialog.Show("Error", "Unable to access the family object.")
script.exit()
# Open the family for editing
family_doc = doc.EditFamily(family)
if family_doc:
# Start a transaction to modify the family
with Transaction(family_doc, "Add KeyplanAreaToggle Parameter") as t:
t.Start()
# Check for shared parameter file
shared_params_file = doc.Application.SharedParametersFilename
if shared_params_file:
shared_params = doc.Application.OpenSharedParameterFile()
if not shared_params:
TaskDialog.Show("Error", "Unable to open the shared parameters file.")
script.exit()
# Search for the shared parameter "KeyplanAreaToggle"
param_def = None
for group in shared_params.Groups:
for definition in group.Definitions:
if definition.Name == "KeyplanAreaToggle":
param_def = definition
break
if param_def:
break
if param_def:
# Access FamilyManager to add the shared parameter
family_manager = family_doc.FamilyManager
# Check if the parameter already exists
if not is_parameter_defined(family_manager, "KeyplanAreaToggle"):
# Add the shared parameter to the family
family_manager.AddParameter(param_def, BuiltInParameterGroup.PG_GRAPHICS, True) # True for instance parameter
else:
TaskDialog.Show("Info", "Parameter 'KeyplanAreaToggle' already exists in the family.")
else:
TaskDialog.Show("Error", "Shared parameter 'KeyplanAreaToggle' not found.")
script.exit()
else:
TaskDialog.Show("Error", "No shared parameter file is set.")
script.exit()
t.Commit()
# Save and reload the family
try:
family_doc.Save()
family_doc.Close(False)
# Reload the family into the project
with Transaction(doc, "Reload Family") as reload_txn:
reload_txn.Start()
doc.LoadFamily(family_doc.PathName)
reload_txn.Commit()
TaskDialog.Show("Success", "Parameter 'KeyplanAreaToggle' added and family reloaded successfully.")
except Exception as ex:
TaskDialog.Show("Error", "Failed to save or reload the family: {0}".format(ex))
script.exit()
else:
TaskDialog.Show("Error", "Unable to open the family document for editing.")
script.exit()
else:
TaskDialog.Show("Error", "The selected element is not a FamilyInstance (Titleblock).")
script.exit()
else:
TaskDialog.Show("Error", "The selected element is not a titleblock.")
script.exit()
So, I changed line 87 from family_doc.Save() to family_doc.SaveAs(“C:\Temp\TemporaryFamily.rfa”) and try it again on a freshly reloaded model. Now I get this error message: PyRevitLoader – Error: Failed to save or reload the family: The referenced object is not valid, possible because it has been deleted from the database, or its creation was undone. and the parameter is not created at all.
I have not been able to find an example of a script that is doing something similar to what I am attempting here. If you could point me to information that would educate me on what I am doing wrong, give me an informative critique of my work, or direct me to an example of adding an instance parameter that clarifies the correct implementation of AddParameter I would be most appreciative.
Try this…
Reloading with options (set to overwrite - but you can change that).
Also, your family_doc.PathName was being lost on close.
Moved it up and collected the path before it was closed or operated on. Doing things to things ini Revit can change their handle.
Was running this in PythonShell, so think I’ve left the declarations in a working state for pyRevit. But maybe not.
Good conditional error checking - something I always get too lazy to be as thorough.
I’d probably roll some of the tasks into def’s to make it simpler to follow and change.
from Autodesk.Revit.DB import *
#from pyrevit import script
from Autodesk.Revit.UI import TaskDialog
from Autodesk.Revit import DB
# Get the active Revit document
doc = __revit__.ActiveUIDocument.Document
# Create family load options-----------------------------------------------------------------------------------------------
class FamilyLoadOptions(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
# Create family load options-----------------------------------------------------------------------------------------------
# Get the currently selected elements
selection = __revit__.ActiveUIDocument.Selection.GetElementIds()
if not selection:
TaskDialog.Show("Error", "No elements selected.")
script.exit()
# Function to check if a parameter is already defined in the family
def is_parameter_defined(family_manager, param_name):
for param in family_manager.Parameters:
if param.Definition.Name == param_name:
return True
return False
# Loop through selected elements and process if it's a Title Block
for element_id in selection:
element = doc.GetElement(element_id)
# Check if the element is a Title Block (usually it has the Title Blocks category)
if element.Category and element.Category.Name == "Title Blocks":
titleblock = element
# Ensure the element is a FamilyInstance
if isinstance(titleblock, FamilyInstance):
# Get the FamilySymbol (the type of the family instance)
family_symbol = titleblock.Symbol
family = family_symbol.Family
if not family:
TaskDialog.Show("Error", "Unable to access the family object.")
script.exit()
# Open the family for editing
family_doc = doc.EditFamily(family)
if family_doc:
# Start a transaction to modify the family
with Transaction(family_doc, "Add KeyplanAreaToggle Parameter") as t:
t.Start()
# Check for shared parameter file
shared_params_file = doc.Application.SharedParametersFilename
if shared_params_file:
shared_params = doc.Application.OpenSharedParameterFile()
if not shared_params:
TaskDialog.Show("Error", "Unable to open the shared parameters file.")
script.exit()
# Search for the shared parameter "KeyplanAreaToggle"
param_def = None
for group in shared_params.Groups:
for definition in group.Definitions:
if definition.Name == "KeyplanAreaToggle":
param_def = definition
break
if param_def:
break
if param_def:
# Access FamilyManager to add the shared parameter
family_manager = family_doc.FamilyManager
# Check if the parameter already exists
if not is_parameter_defined(family_manager, "KeyplanAreaToggle"):
# Add the shared parameter to the family
family_manager.AddParameter(param_def, BuiltInParameterGroup.PG_GRAPHICS, True) # True for instance parameter
else:
TaskDialog.Show("Info", "Parameter 'KeyplanAreaToggle' already exists in the family.")
else:
TaskDialog.Show("Error", "Shared parameter 'KeyplanAreaToggle' not found.")
script.exit()
else:
TaskDialog.Show("Error", "No shared parameter file is set.")
script.exit()
t.Commit()
# Save and reload the family
try:
path = family_doc.PathName
print(path)
family_doc.Save()
family_doc.Close(False)
# Reload the family into the project
with Transaction(doc, "Reload Family") as reload_txn:
reload_txn.Start()
#Added Options----------------------------------------------------------------------------------------------------------------------
options = FamilyLoadOptions()
#Added Options----------------------------------------------------------------------------------------------------------------------
doc.LoadFamily(path, options)
reload_txn.Commit()
TaskDialog.Show("Success", "Parameter 'KeyplanAreaToggle' added and family reloaded successfully.")
except Exception as ex:
TaskDialog.Show("Error", "Failed to save or reload the family: {0}".format(ex))
script.exit()
else:
TaskDialog.Show("Error", "Unable to open the family document for editing.")
script.exit()
else:
TaskDialog.Show("Error", "The selected element is not a FamilyInstance (Titleblock).")
script.exit()
else:
TaskDialog.Show("Error", "The selected element is not a titleblock.")
script.exit()
It’s Friday and iam too lazy for a review but I’ll be generous anyway, I did something similar but deleting Params:
from pyrevit import revit, DB, forms, script
import os
import tempfile
from hooks import set_hook
set_hook()
output = script.get_output()
output.close_others()
doc = revit.doc
family_dict = {}
# temporary path for saving families
temp_dir = os.path.join(tempfile.gettempdir(), "remove_parameter")
if not os.path.exists(temp_dir):
os.mkdir(temp_dir)
save_as_options = DB.SaveAsOptions()
save_as_options.OverwriteExistingFile = True
# fi = DB.ElementCategoryFilter(DB.BuiltInCategory.OST_GenericModel, True)
fams = DB.FilteredElementCollector(doc).OfClass(
DB.Family).ToElements() # .WherePasses(fi)
for family in fams:
if family.FamilyCategory and family.IsEditable:
family_dict["%s: %s" %
(family.FamilyCategory.Name, family.Name)] = family
# family selection
if family_dict:
selected_families = forms.SelectFromList.show(
sorted(family_dict.keys()),
title="Select Families to remove parameter from",
multiselect=True,
)
# name the parameter to remove
param = forms.ask_for_string(
prompt="Type the parameter name to remove:", title="Parameter remover")
results = []
# let's do it
with revit.TransactionGroup("Remove parameter from families"):
if selected_families and param:
for idx, family in enumerate([family_dict[x] for x in selected_families]):
# edit mode of the family
base_fam = doc.EditFamily(family)
with revit.Transaction('Delete Parameter', base_fam):
# get the named parameter
params = revit.query.get_family_parameter(param, base_fam)
if params:
# delete it
base_fam.FamilyManager.RemoveParameter(params)
results.append("Parameter %s removed from %s" %
(param, family.Name))
else:
results.append("x_x_x_x_x_x Parameter %s not found in family %s" %
(param, family.Name))
# save the family in the temp folder
fpath = os.path.join(temp_dir, base_fam.Title)
base_fam.SaveAs(fpath, save_as_options)
base_fam.Close()
# load the family from the temp folder
with revit.Transaction("Remove parameter from families", doc):
loaded_f = revit.db.create.load_family(fpath, doc)
doc.Regenerate()
print("\n".join(results))
set_hook()
I also had to import os, point to the C:\Temp folder, give the temporary .rfa file a name, and use SaveAs(pathVariable, options) to finally get KeyplanAreaToggle to appear in instance properties in a test.