Apply parameter value to family document

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.

Best regards.

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()
1 Like

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()
1 Like

Thank you, @aaronrumple!

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.

1 Like

Thank you, @Jean-Marc .

You script goes over my head in places, but I will keep it handy to learn from as I develop better python skills.