Export family types as text node?

Hi, is there anything to export family types as text?
image

Like a type catalog?

I think if thats the right terminology :sweat_smile: so when people import a family the dialog box comes up with types to choose from. I’ve made a ton of door families. Keen to not export the type catalog one by one :slight_smile:

1 Like

Cool Ill try background open stuff and use this :thinking: although need it to create a text file

Yes, type catalog. The problem is that Revit won’t export most custom shared parameters by default. That’s what I shared was a method to pick any parameters to use.

Thanks Sean, getting this error for some reason. Your right, typical Revit catalog isnt working with shared parameters. I’ve made 200 doors with like 2500 types. Might be a waste of time if I cant get working catalogs into the library with them :melting_face: although Ill try work on making an excel export or something that may work.
image

What version of Revit?

22, dynamo 2.10

Ok, that is after the SpecType change so yeah the DUT isn’t going to work. I may try to see if I can refactor, but wouldn’t be at least till tomorrow.

I don’t have python available, but here is my C# which should be easy enough to port.

namespace RDGRevit.Commands
{
	[Transaction(TransactionMode.Manual)]
	class FamilyTypeCatalogExport : IExternalCommand
	{
		public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements)
		{
			Document doc = commandData.Application.ActiveUIDocument.Document;
			List<FamilyParameter> usedParams = new List<FamilyParameter>();
			FamilyParameterSet famParams = doc.FamilyManager.Parameters;
			Units famUnits = doc.GetUnits();
			FormatOptions len = famUnits.GetFormatOptions(SpecTypeId.Length);
			List<string> output = new List<string>();
			SaveFileDialog fsd = new SaveFileDialog
			{
				Filter = "Text File | *.txt",
				DefaultExt = "txt",
				OverwritePrompt = true,
				FileName = doc.Title
			};
			if(fsd.ShowDialog() == DialogResult.OK)
			{
				string path = fsd.FileName;
				FamilyTypeSet famTypes = doc.FamilyManager.Types;

				foreach(FamilyType type in famTypes)
				{
					foreach(FamilyParameter param in famParams)
					{
						if(!param.IsDeterminedByFormula)
						{
							if(type.HasValue(param))
							{
								if(!usedParams.Contains(param))
								{
									usedParams.Add(param);
								}
							}
						}
					}
				}
				string variables = "";
				foreach(FamilyParameter param in usedParams)
				{
					ForgeTypeId pType = param.Definition.GetDataType();
					string pName = param.Definition.Name;

					if(pType == SpecTypeId.Length)
					{
						variables += "," + pName + "##LENGTH##FEET";
					}
					else if(pType == SpecTypeId.Area)
					{
						variables += "," + pName + "##AREA##SQUARE_FEET";
					}
					else if(pType == SpecTypeId.Volume)
					{
						variables += "," + pName + "##VOLUME##CUBIC_FEET";
					}
					else if(pType == SpecTypeId.Slope)
					{
						variables += "," + pName + "##SLOPE##SLOPE_DEGREES";
					}
					else if(pType == SpecTypeId.Angle)
					{
						variables += "," + pName + "##ANGLE##DECIMAL DEGREES";
					}
					else if(pType == SpecTypeId.Currency)
					{
						variables += "," + pName + "##CURRENCY##CURRENCY";
					}
					else
					{
						variables += "," + pName + "##OTHER##";
					}
				}
				output.Add(variables);
				foreach(FamilyType famType in famTypes)
				{
					string typeString = "";
					typeString += famType.Name;
					foreach(FamilyParameter param in usedParams)
					{
						ForgeTypeId pt = param.Definition.GetDataType();
						StorageType storeType = param.StorageType;
						string value = string.Empty;
						if(storeType == StorageType.Double)
						{
							if(pt == SpecTypeId.Angle && famType.AsDouble(param) is double angle)
							{
								value = UnitUtils.ConvertFromInternalUnits(angle, UnitTypeId.Degrees).ToString();
							}
							else if(pt == SpecTypeId.Slope && famType.AsDouble(param) is double slope)
							{
								value = UnitUtils.ConvertFromInternalUnits(slope, UnitTypeId.SlopeDegrees).ToString();
							}
							else
							{
								value = famType.AsDouble(param).ToString();
							}
						}
						else if(storeType == StorageType.Integer)
						{
							value = famType.AsInteger(param).ToString();
						}
						else if(storeType == StorageType.String)
						{
							value = famType.AsString(param);
						}
						else if(storeType == StorageType.ElementId)
						{
							ElementId eId = famType.AsElementId(param);
							if(doc.GetElement(eId) is Element elem)
							{
								value = elem.Name;
							}
						}
						typeString += "," + value;
					}
					output.Add(typeString);
				}
				using(StreamWriter sw = new StreamWriter(path))
				{
					foreach(var line in output)
					{
						sw.WriteLine(line);
					}
				}
			}
			return Result.Succeeded;
		}
	}
}
1 Like

Thanks Sean! appreciate it. I can upgrade to R23 if that would solve the issue too. Will try your C sharp in the mean time :slight_smile:

1 Like

No, the code in my old graph is outdated after 2021, so it won’t work in anything newer than 2022 unless you modify it.

1 Like

I’m actually not sure what to do with C sharp and dynamo sorry… Not sure what port means too :sweat_smile:

#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"

See if this will work. I have not tested it, but it should.

#Sean Page, 2023
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.GetDataType()
	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 == SpecTypeId.Length:
		variables += "," + pName + "##LENGTH##FEET"
	elif pType == SpecTypeId.Area:
		variables += "," + pName + "##AREA##SQUARE_FEET"
	elif pType == SpecTypeId.Volume:
		variables += "," + pName + "##VOLUME##CUBIC_FEET"
	elif pType == SpecTypeId.Slope:
		variables += "," + pName + "##SLOPE##SLOPE_DEGREES"
	elif pType == SpecTypeId.Angle:
		variables += "," + pName + "##ANGLE##DECIMAL DEGREES"
	elif pType == SpecTypeId.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.GetDataType()
		sType = param.StorageType
		value = String.Empty
		if sType == StorageType.Double:
			if pType == SpecTypeId.Angle:
				value = UnitUtils.ConvertFromInternalUnits(famType.AsDouble(param),UnitTypeId.Degrees).ToString()
			elif pType == SpecTypeId.Slope:
				value = UnitUtils.ConvertFromInternalUnits(famType.AsDouble(param),UnitTypeId.SlopeDegrees).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"
1 Like

Thanks Sean! its throwing up the error below.
image

Try to change any of the contains locations like this I guess.

if pName.Contains(','):

to

if ',' in pName:

I think its this part

#Check for commas in the value and encapsulate with double quotes as needed
		if value.Contains(','):
			value = '"'+value+'"'

image

Smart fella at work helped me. Have to open and save the text file after making it or it dosnt work. Get cannot parse column header error. Which after making 200 text files isnt going to be great.

Other thing was changing units to metric and needed to input doc.

#Sean Page, 2023
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 = IN[2]
#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.GetDataType()
	pName = param.Definition.Name
	#check to see if the parameter name has a comma and encapsulate with double quoates
	if ',' in pName:
		pName = '"'+pName+'"'
	if pType == SpecTypeId.Length:
		variables += "," + pName + "##LENGTH##MILLIMETERS"
	elif pType == SpecTypeId.Area:
		variables += "," + pName + "##AREA##SQUARE_MILLIMETERS"
	elif pType == SpecTypeId.Volume:
		variables += "," + pName + "##VOLUME##CUBIC_MILLIMETERS"
	elif pType == SpecTypeId.Slope:
		variables += "," + pName + "##SLOPE##SLOPE_DEGREES"
	elif pType == SpecTypeId.Angle:
		variables += "," + pName + "##ANGLE##DEGREES"
	elif pType == SpecTypeId.Currency:
		variables += "," + pName + "##CURRENCY##CURRENCY"
	else:
		variables += "," + pName + "##OTHER##"

trimmedvar = variables[1:]
variables = trimmedvar
#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.GetDataType()
		sType = param.StorageType
		value = String.Empty
		if sType == StorageType.Double:
			if pType == SpecTypeId.Angle:
				value = UnitUtils.ConvertFromInternalUnits(famType.AsDouble(param),UnitTypeId.Degrees).ToString()
			elif pType == SpecTypeId.Slope:
				value = UnitUtils.ConvertFromInternalUnits(famType.AsDouble(param),UnitTypeId.SlopeDegrees).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
		try:
			if	',' in value:
				value = '"'+value+'"'
		except:
			#This catches materials specifically that are "<Default>" and don't have a name
			value = String.Empty
		#Add each value for each parameter
		typeString += ","+value.ToString()
	#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"

Here is the graph that is working for me on your Family.

Selected Family Type Catalog Metric.dyn (37.6 KB)

Selection of Parameters in the family.

Here is the catalog available for selection.

Looks like it throws warnings when it can’t find the materials.

3 Likes

This is great thanks Sean! Turned it into a node and exported 200 family catalogs :slight_smile: Just wondering if theres a way to get the types ordered when importing?

If you sorted the famParams list before iterating.