Hi everybody, is it possible to have some help for the following subject :
I’m trying to use the FamilyDocReplaceParameter node from the crumple Package from @GavinCrump to replace family parameters with shared parameters.
I don’t understand why I have three nulls at the end of this script and why it’s not changing anything in the current family doc document
( for more info of what’s coming next :
It’s a script that I would like to use with Bird Tools dynamo multiplayer
I would like to replace the Code block that gives parameters name to replace and to add by an excel import)
Error: PythonEvaluator.Evaluate operation failed. Traceback (most recent call last): File , line 54, in . Exception: Cannot replace a parameter of a different ParameterType. Parameter name: familyDefinition
Python Code:
# Made by Gavin Crump
# Free for use
# BIM Guru, www.bimguru.com.au
# Boilerplate text
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 *
#unwrap all elements to use with API
docs = IN[0]
param_lists = IN[1]
def_lists = IN[2]
param_lists_out = []
counter = 0
# Main body of code
for doc,plst,dlst in zip(docs,param_lists,def_lists):
param_list_out = []
for p,d in zip(plst,dlst):
# start a transaction
trans = Transaction(doc, 'Replace parameters')
trans.Start()
# Set the counter for naming parameters
counter += 1
strCount = str(counter)
tempName = "Temporary " + strCount
# Get family manager and parameter settings
fm = doc.FamilyManager
pg = p.Definition.GetGroupTypeId()
pi = p.IsInstance
# If parameter is shared, replace with family
# If not, then we rename it anyway
if p.IsShared:
p = fm.ReplaceParameter(p, tempName, pg, pi)
else:
fm.RenameParameter(p, tempName)
# Replace family parameter with shared parameter
shp = fm.ReplaceParameter(p, d, pg, pi)
param_list_out.append(shp)
# Close the transaction
trans.Commit()
TransactionManager.Instance.ForceCloseTransaction()
param_lists_out.append(param_list_out)
# Preparing output to Dynamo
OUT = [param_lists_out,docs]
Maybe I am doing a mistake somewhere.
Can @GavinCrump or other Dynamo Champions offer some help?
Welcome to the fun of API deprecation! I haven’t bothered updating my code in Crumple yet as I’m generally not in 2024 at this point that often and build against 2022 for now. Personally I’m not a fan of the spec/group type calling approach. I understand it, but really wish it had a clearly documented enum (I assume it will be coming on APS docs). If anyone knows of one, feel free to drop it in this thread.
If you search forgetype and spectype by name on the forums you will find some examples of how to feed in a human readable name and get an equivalent spec/group type. Here’s one example:
You’ll need to add some code into my previous script to get these forgetypeIds instead of my approach which uses phased out methods/groups etc. in 2024+
# Boilerplate text
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 *
# Unwrap all elements to use with the API
docs = IN[0]
param_lists = IN[1]
def_lists = IN[2]
param_lists_out = []
counter = 0
# Main body of code
for doc, plst, dlst in zip(docs, param_lists, def_lists):
param_list_out = []
for p, d in zip(plst, dlst):
# Start a transaction
trans = Transaction(doc, 'Replace parameters')
trans.Start()
# Set the counter for naming parameters
counter += 1
strCount = str(counter)
tempName = "Temporary " + strCount
# Get family manager and parameter settings
fm = doc.FamilyManager
pi = p.IsInstance
# Check if parameter types match
if p.Definition.GetDataType() == SpecTypeId.String.Text:
# Replace the parameter
shp = fm.ReplaceParameter(p, d, pi)
param_list_out.append(shp)
else:
# Handle the type mismatch (e.g., log a warning or skip the replacement)
# You can customize this part based on your requirements
print("Parameter type mismatch: {} ({}) and {} ({})".format(p.Definition.Name, p.Definition.GetDataType(), d.Definition.Name, d.Definition.GetDataType()))
# Close the transaction
trans.Commit()
TransactionManager.Instance.ForceCloseTransaction()
param_lists_out.append(param_list_out)
# Prepare the output for Dynamo
OUT = [param_lists_out, docs]
But still doesn’work .
Shows error at Line 47: PythonEvaluator.Evaluate operation failed. Traceback (most recent call last): File"", line 47, in Attribute Error: ‘ExternalDefinition’ object has no attribute ‘Definition’
I generally don’t work with GPT code as it’s typically nonsensical or doesn’t produce outcomes suitable for complex problems like this one - it just creates more problems to fix unless it’s a basic problem or the querier has some understanding of the code it returns. Hopefully someone gets time to write functioning code in future here, unfortunately due to time constraints it likely wont be me currently.
GPT stands for Generative Pre-trained Transformer, but as the datasets are almost all generic in nature they likely should stand for Generic Pre-Trained Transformer.
What this means is that any code you get from it will be a pre-determined result from: it’s lifespan history (minimal in a per user context), a small sampling of relevant data, and a PLETHORA of irrelevant data. This is doubly the case as most (nearly all) gpts are trained on data which predates the last 3 releases of Revit, and therefore all of those API changes are going to cause it to fail. And even for stuff which works it’s usually going to make stuff up as the Revit API isn’t as common as other languages which are out there. That small percentage of Revit API on the web has massive impacts on the outcomes.
As an example I once spent 30 minutes trying to get it to draw a wall as an experiment only to get sample which thought things like points existed in the API. After 30 minutes I gave up and said “would this be easier with a google search?” And so in an new incognito browser I tried the ‘I’m feeling lucky’ google result and found the functional code sample after scrolling down 3 pages. Sadly googles results are becoming SEO’d to death even for API calls now (thanks to GPT), so that’s becoming less reliable. And so everyone is best off learning the API.
A GPT can certainly help with learning (well suited for this actually), but don’t expect to get functional code and always test the results thoroughly.
in addition to what has been said above, it is preferable to manage the entire process of changing parameters in a single Python node and to open and close families successively, rather than opening all families and ending up with several dozen (or even a hundred) documents open.
I found a brief window to look into this further, I think you had the workflow right before when you replaced the group with GetGroupTypeId() instead of ParameterGroup.
Your warning seems to indicate that you’re trying to change a parameter’s type, which is not permitted in Revit API or Revit itself (e.g. replacing an integer type parameter with a number). Cross check that isn’t the case.
To add to this I can see forgetypeIds are going to be on my radar so I bit the bullet and figured out how to get specs/groups by name. Much easier than I thought, I quite like them now vs the old method - targeting the human friendly name is great.
# Boilerplate text
import clr
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *
# Current doc/app/ui
doc = DocumentManager.Instance.CurrentDBDocument
# Preparing input from dynamo to revit
specs_get = IN[0]
groups_get = IN[1]
# Get forgetypeids and names
spec_ids = SpecUtils.GetAllSpecs()
spec_names = [LabelUtils.GetLabelForSpec(s) for s in spec_ids]
# Get grouptypeIds and names
group_ids = ParameterUtils.GetAllBuiltInGroups()
group_names = [LabelUtils.GetLabelForGroup(g) for g in group_ids]
# Function: Return element by name match
def element_byNameMatch(n, elements, names):
if n in names:
ind = names.index(n)
return elements[ind]
else:
return None
# Return forgetypeIds
OUT = [ [element_byNameMatch(n, spec_ids, spec_names) for n in specs_get],\
[element_byNameMatch(n, group_ids, group_names) for n in groups_get] ]