I have noticed that not all of the Parameters nor parameter values for some of my Families were being exported when using the default Export Family Types feature in Revit. This was mostly true for Family Type Shared Parameters that are used for Labels.
Based on wanting to remedy that, I have worked through and crated a graph that allows you to pick the Parameters to export as well as doing some pre filtering for you such as parameters that don’t have a value or ones that are driven by formula.
Here is the Python node that gathers the parameter information:
#Sean Page, 2020
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument
params = []
names = []
groups = []
#Iternate each parameter in the Family
for param in doc.FamilyManager.Parameters:
#If the Parameter if Formula Driven, no need to export it
if param.Formula == None:
for type in doc.FamilyManager.Types:
#Check each type to see if the Parameter has a value, if no Type has a value no need to export
if type.HasValue(param):
#Check to see if the list already contains the Parameter because you only need it once
if not params.Contains(param):
#Add the parameter and parameter name to the output lists
params.Add(param)
names.Add(param.Definition.Name)
groups.Add(param.Definition.ParameterGroup)
OUT = names,params,groups
Here is the Python for the node that gets the values based on ParameterType and Parameter StorageType.
#Sean Page, 2020
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
clr.AddReference('System')
from System.Collections.Generic import List
from System import *
from System.IO import StreamWriter
doc = DocumentManager.Instance.CurrentDBDocument
#Preparing input from dynamo to revit
path = IN[1]
#units = doc.GetUnits().GetFormatOptions(UnitType.UT_Length).DisplayUnits
famParams = IN[0]
output = []
famTypes = doc.FamilyManager.Types
#Set up the first line of the Type Catalog with the headers
variables = "";
for param in famParams:
pType = param.Definition.ParameterType
pName = param.Definition.Name
if pType == ParameterType.Length:
variables += "," + pName + "##LENGTH##FEET"
elif pType == ParameterType.Area:
variables += "," + pName + "##AREA##SQUARE_FEET"
elif pType == ParameterType.Volume:
variables += "," + pName + "##VOLUME##CUBIC_FEET"
elif pType == ParameterType.Slope:
variables += "," + pName + "##SLOPE##SLOPE_DEGREES"
elif pType == ParameterType.Angle:
variables += "," + pName + "##ANGLE##DECIMAL DEGREES"
elif pType == ParameterType.Currency:
variables += "," + pName + "##CURRENCY##CURRENCY"
else:
variables += "," + pName + "##OTHER##"
#Add the headers to the final output
output.Add(variables)
#Iterate each type and get the values for each parameter ased on its Storage Type and Parameter Type
for famType in famTypes:
typeString = String.Empty
typeString += famType.Name
for param in famParams:
pType = param.Definition.ParameterType
sType = param.StorageType
value = String.Empty
if sType == StorageType.Double:
if pType == ParameterType.Angle:
value = UnitUtils.ConvertFromInternalUnits(famType.AsDouble(param),DisplayUnitType.DUT_DECIMAL_DEGREES).ToString()
elif pType == ParameterType.Slope:
value = UnitUtils.ConvertFromInternalUnits(famType.AsDouble(param),DisplayUnitType.DUT_SLOPE_DEGREES).ToString()
else:
value = famType.AsDouble(param).ToString()
if sType == StorageType.Integer:
value = famType.AsInteger(param).ToString()
if sType == StorageType.String:
value = famType.AsString(param)
if sType == StorageType.ElementId:
eid = famType.AsElementId(param)
elem = doc.GetElement(eid)
try:
value = elem.Name
except:
value = elem.get_Parameter(BuiltInParameter.ALL_MODEL_TYPE_NAME).AsString()
#Add each value for each parameter
typeString += ","+value
#Add all values for each Family Type
output.Add(typeString)
try:
#Write each line to a txt file
sw = StreamWriter(path)
for line in output:
sw.WriteLine(line)
sw.Close()
OUT = output
except:
OUT = "Failed to Write File"
@SeanP This is awesome I have some families that have greatness to them but if I use the standard type export it goes crazy on import. So extremely Happy to see this! That being said I have been trying to poke into the code for a bit. I for some reason end up with a labeled _1 column which then gives me the error of to many columns on import of family by type catalog. I’m guessing it might have more to due with my family but just curious if you have seen this behavior? Im playing with the python at the moment but haven’t seen anything that could cause it yet.
@SeanP I appreciate the quick response but after a night off and a few beers it hit me this morning. Material Name… Something I don’t manage for my firm is the Material Library and someone had the Idea to name the Material “Aluminum, Anodized Black” which due to how Material parameter values are stored (can’t be formula based) the comma gets read as a comma not part of the Name string. Thus adding a column and pushing all parameter values over by on column putting ints in string positions thus causing the loads to fail. I swore after reviewing your code it couldn’t be it, but I was running out of Ideas and thought to reach out. This code your posted is a life saver!
Thanks for circling back around on this. It looks like the default Revit export approach encapsulates values with special characters in double quotes. I will look to refactor the python to include this.
Sean truly appreciate you working to update/upkeep your code. When you say special characters do you mean ° for example? New problem is the insert side the new warning is that the parameter doesn’t exist and ° is coming in as ° in the warning dialog. I was just going to start to look into it now.
Basically any characters that may cause issues like “,” commas or " ’ " apostrophes etc. When you use the standard export from Revit family you would get “something, like it’s tomorrow” where right now this graph would just give you something, like it’s tomorrow and as you know that break its. One way would be to just wrap every value in double quotes to help mitigate that, but probably needs a more complete approach to be scalable.
You can test this with a simple family, make a couple types and a couple parameter. You’ll see if say you have a material with a comma in it, it will now have quotes.
Gotcha that makes sense. Not that I recommend putting ",’ in most places to those in my firm just like ‘.’ in file names. (although I do always tell the new guy to name the file “Con” just for fun.) Type Catalogs are great but unforgiving in that import export process
side question is this saving as UTF-8 or ANSI when making the TXT? Looks like UTF-8 found if you have parameters named with specials characters a quick file save as with ANSI fixes the issue.
Ok, so here is what I have come up with, and what is still not working just right.
I exported a Type Catalog from Revit directly and it was actually encoded as UTF-16LE
This export maintained special characters / letter as I expected, but broke for Parameter Names / Values with commas.
I refactored to encapsulate either of those values when they had a comma
I refactored the write to use the codecs class rather than the stream writer.
This enables us to encode as we want to the UTF-16LE or any other as needed
Problem is that for some reason it is hiding an invisible character at the beginning of the text file that is breaking revit.
This can be solved in two ways:
Save As from notepad as ANSI
Delete and replace the very first comma on the first line of the file.
There would probably be a way to open and replace the comma, but my first attempts saved the file back as UTF-8 or something other than what it was so I stopped thinking it may need a completely different approach.
#Sean Page, 2021
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
clr.AddReference('System')
from System.Collections.Generic import List
from System import *
from System.IO import StreamWriter
from System.Text import Encoding
import codecs
doc = DocumentManager.Instance.CurrentDBDocument
#Preparing input from dynamo to revit
path = IN[1]
#units = doc.GetUnits().GetFormatOptions(UnitType.UT_Length).DisplayUnits
famParams = IN[0]
output = []
famTypes = doc.FamilyManager.Types
#Set up the first line of the Type Catalog with the headers
variables = ''
for param in famParams:
pType = param.Definition.ParameterType
pName = param.Definition.Name
#check to see if the parameter name has a comma and encapsulate with double quoates
if pName.Contains(','):
pName = '"'+pName+'"'
if pType == ParameterType.Length:
variables += "," + pName + "##LENGTH##FEET"
elif pType == ParameterType.Area:
variables += "," + pName + "##AREA##SQUARE_FEET"
elif pType == ParameterType.Volume:
variables += "," + pName + "##VOLUME##CUBIC_FEET"
elif pType == ParameterType.Slope:
variables += "," + pName + "##SLOPE##SLOPE_DEGREES"
elif pType == ParameterType.Angle:
variables += "," + pName + "##ANGLE##DECIMAL DEGREES"
elif pType == ParameterType.Currency:
variables += "," + pName + "##CURRENCY##CURRENCY"
else:
variables += "," + pName + "##OTHER##"
#Add the headers to the final output
output.Add(variables + '\r\n')
#Iterate each type and get the values for each parameter ased on its Storage Type and Parameter Type
for famType in famTypes:
typeString = String.Empty
typeString += famType.Name
for param in famParams:
pType = param.Definition.ParameterType
sType = param.StorageType
value = String.Empty
if sType == StorageType.Double:
if pType == ParameterType.Angle:
value = UnitUtils.ConvertFromInternalUnits(famType.AsDouble(param),DisplayUnitType.DUT_DECIMAL_DEGREES).ToString()
elif pType == ParameterType.Slope:
value = UnitUtils.ConvertFromInternalUnits(famType.AsDouble(param),DisplayUnitType.DUT_SLOPE_DEGREES).ToString()
else:
value = famType.AsDouble(param).ToString()
if sType == StorageType.Integer:
value = famType.AsInteger(param).ToString()
if sType == StorageType.String:
value = famType.AsString(param)
if sType == StorageType.ElementId:
eid = famType.AsElementId(param)
elem = doc.GetElement(eid)
try:
value = Element.Name.__get__(elem)
except:
#This catches materials specifically that are "<Default>" and don't have a name
value = String.Empty
#Check for commas in the value and encapsulate with double quotes as needed
if value.Contains(','):
value = '"'+value+'"'
#Add each value for each parameter
typeString += ","+value
#Add all values for each Family Type
output.Add(typeString + '\r\n')
outfile = codecs.open(path, 'w', encoding='utf-16-le')
try:
#Write each line to a txt file while encoding for special characters
for line in output:
outfile.write(line)
outfile.close()
OUT = output
except:
outfile.close()
OUT = "Failed to Write File"
@SeanP I’m really excited to use your routine. I belive it will resolve my problem with the Family Type parameter. But, when I ran it, some node warnings appear. (Check the image) Bellow are listed the error texts:
UI.Listview Data: Error: Custom node definition is not loaded!
UI.FilePath Data: Error: Custom node definition is not loaded!
UI.MultipleInputForm ++: Error: Custom node definition is not loaded!
Write parameter Values: Warning: IronPythonEvaluator.EvaluateIronPythonScript operation failed.
Traceback (most recent call last): File “string>”, line 24, in TypeError: iteration over non-sequence of type NoneType.
Could you help me solve them?
I didn’t find any packcage for the custom nodes.
Thank You!
As usual, nice work Sean! I am downloading these to give them a try. Why I didnt find these before is beyond me.
I still can’t believe that we do not have a proper function or addin to help do a selective and custom type catalog export. Joe Sferrarra (Sp?) from the Charlotte Revit Users Group use to have a tool that I used all the time and it worked great. However, he stopped developing it a few years ago. It would be sweet if he or someone who still has it could update it for use in the newer Revit versions. It was that awesome. =)
I appreciate the graph. I will reply if i run into any issues with it. =)
You’re welcome @jquarry. I actually did convert it to C# shortly after testing it in Dynamo and integrated it into our company’s toolbar. I believe that in R24 there are some improvements to this, but I have not tried it myself to know for sure.