Associate family parameter in a family

So, while editting a family,

I’d like to assign -through dynamo- an instance value of a list of elements.
not like the ‘setparametervalue’ but ‘using the tiny grey button on the right’ … the associate family parameter.

Is this possible?

1 Like

I actually came across the need for this today for linking a Generic Annotation to a Titleblock for a complicated Key Plan. We’ll need to use Python to do it, and I’m thinking this is the API method: http://www.revitapidocs.com/2018/a047ea58-0351-b419-d856-85ed23734ee8.htm I’m not sure, though, if this method only works for element parameters in the family i.e. an extrusion’s “Visible” parameter

In any case, I have hit a wall with my test case and get the warnings “TypeError: expected Parameter, got Parameter” when feeding my Python script an Autodesk.Revit.DB.FamilyParameter element, and “TypeError: expected Parameter, got UnknownElement” when feeding it a ParameterElement :thinking:

I know the first error I’m getting is because I’m giving it the wrong type of “Parameter” (either Dynamo type or Revit type), and the second error makes me think Dynamo doesn’t recognize Family Parameter elements because thats typically what “UnknownElement” means, but I’m not sure how to get it sorted.

Here is the Python script I started:

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference('RevitNodes')

import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument

elemParam = IN[0]
famParam = IN[1]


TransactionManager.Instance.EnsureInTransaction(doc)

doc.FamilyManager.AssociateElementParameterToFamilyParameter(elemParam, famParam)
	
TransactionManager.Instance.TransactionTaskDone()

OUT = elemParam

Hopefully someone else might know more to completing this (perhaps @erfajo if he is not too busy :slight_smile: )

2 Likes

@awilliams I too was fiddling with AssociateElementParameterToFamilyParameter. Same result which made me kinda laugh “got Parameter, expected Parameter”.

My approach (deleted, wasn’t working) was
at family editor
IN[0]: a list with elements in need of associated parameter.
in python
get the parameter (either hardcoded or an IN[1] of those elements and
get the family parameter (either hardcoded or an IN[2])
for a in in[0]
associate
done.

Anybody else have a clue?

1 Like

Ok I got it sorted ! It needed an Autodesk.Revit.DB.Parameter for the element parameter and Autodesk.Revit.DB.FamilyParameter for the family parameter. The below is a bit messy and can use some improvement but it worked for my purposes. It will only process one element but it will associate multiple parameters; give the Python script node a list of parameter names (strings) and the family parameter names to associate them with like so:

Script:

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference('RevitNodes')

import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument

elem = UnwrapElement(IN[0])
paramNames = IN[1]
famNames = IN[2]

if not isinstance(paramNames, list):
	paramNames = [paramNames]
if not isinstance(famNames, list):
	famNames = [famNames]
	
elemParams = elem.Parameters
famParams = doc.FamilyManager.Parameters


elemAssoc = []
famAssoc = []

for param in elemParams:
	for name in paramNames:
		if param.Definition.Name == name:
			elemAssoc.append(param)

for fparam in famParams:
	for fname in famNames:
		if fparam.Definition.Name == fname:
			famAssoc.append(fparam)

TransactionManager.Instance.EnsureInTransaction(doc)

for i,j in zip(elemAssoc, famAssoc):
	doc.FamilyManager.AssociateElementParameterToFamilyParameter(i, j)
	
TransactionManager.Instance.TransactionTaskDone()

OUT = elem

Let me know if this works for you as well & if you improve upon it please share :slight_smile:

7 Likes

I agree @erfajo another stunner from @awilliams :+1: +1

2 Likes

@awilliams,
Sorry for my belated response, but kudos! awesome script.

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference('RevitNodes')

import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument

elem = UnwrapElement(IN[0])
paramNames = IN[1]
famNames = IN[2]

if not isinstance(paramNames, list):
	paramNames = [paramNames]
if not isinstance(famNames, list):
	famNames = [famNames]
famParams = doc.FamilyManager.Parameters
for e in elem:
	elemParams = e.Parameters
	
	elemAssoc = []
	famAssoc = []
	
	for param in elemParams:
		for name in paramNames:
			if param.Definition.Name == name:
				elemAssoc.append(param)
	
	for fparam in famParams:
		for fname in famNames:
			if fparam.Definition.Name == fname:
				famAssoc.append(fparam)
	
	TransactionManager.Instance.EnsureInTransaction(doc)
	
	for i,j in zip(elemAssoc, famAssoc):
		doc.FamilyManager.AssociateElementParameterToFamilyParameter(i, j)
		
	TransactionManager.Instance.TransactionTaskDone()

OUT = elem

Now we can run this for multiple items.

No need to use the e.Elementtype at dynamo though …
works for me, but got an issue trying to assign family Description at the nested family instance ‘test’ parameter. Says parameter type not the same … both text, :sweat_smile:But this is a puzzle for me tomorrow. First dinner!

3 Likes

Nice adjustment to work with lists :slight_smile: The e.ElementType portion was because I was associating type parameters in the test case but I used it for instance parameters with my actual use – are you certain that your test parameter is a type parameter? The OOTB Description parameter is a Type parameter - in Revit itself we can’t associate type parameters with instance parameters so that might be the cause of error?

@awilliams, hmmmm perhaps it’s the other way around you say (or as how I understand it):

My family I’m editting (MyFamily)
has several nested (shared) families (MyNestedFamiliy).

So running the script allows me to acces

  • get familytypeparameter ‘mytypeparameter’. … let’s name it MyFamily.mytypeparameter
  • get familytypeparameter ‘Description’ … let’s name it MyFamily.Description

So running the script allows me to connect

  • MyFamily.mytypeparameter to MyNestedFamily.instanceparameter-a
  • MyFamily.Description to MyNestedFamily.instanceparameter-b

Currently mytypeparameter connecting to instanceparameter-a: awesome. np.
Description to instanceparameter-b … error. While doing this manually: np.

A nice puzzle and perhaps it has to do with assigning things to type parameters vs. instance parameters. I’ll dive into this one asap. :smiley:

Knipsel

So:
IN[0] is a list of 200 ‘shared nested families’.
‘start_lagenmaat’ and ‘merk’ are both instance family parameters of those shared parameters.

Oops, I was definitely confusing the instance/type parameter association conflict with formulas (the “Instance Parameters can’t be used in Type Parameter formulas” warning). That is awfully peculiar that your case is working one way but not the other. Maybe it is possible that it has something to do with Shared Parameters vs. Project Parameters? Do you mind sharing your RFAs so I can take a look?

Oeh, I’m getting closer.

It has to do with the input as list. So setting connecting “Description” to “merk” solo: np.
part of a list as shown above: nope.

And sure, hang on let me get it.

awilliams_dynamo.rfa (360 KB)

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference('RevitNodes')

import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument

elem = UnwrapElement(IN[0])
paramNames = IN[1]
famNames = IN[2]

if not isinstance(paramNames, list):
	paramNames = [paramNames]
if not isinstance(famNames, list):
	famNames = [famNames]
famParams = doc.FamilyManager.Parameters
#tester =[]
for e in elem:
	elemParams = e.Parameters
	
	elemAssoc = []
	famAssoc = []
	
	for param in elemParams:
		for name in paramNames:
			if param.Definition.Name == name:
				elemAssoc.append(param)
	
	for fparam in famParams:
		#tester.append(fparam.Definition.Name)
		for fname in famNames:
			if fparam.Definition.Name == fname:
				famAssoc.append(fparam)
	
	TransactionManager.Instance.EnsureInTransaction(doc)
	for i,j in zip(elemAssoc, famAssoc):
		try:
			doc.FamilyManager.AssociateElementParameterToFamilyParameter(i, j)
			#doc.FamilyManager.CanElementParameterBeAssociated(i,j)		
			error_report = "No errors"
			
		except:
			error_report = "can't assign parameter"
	TransactionManager.Instance.TransactionTaskDone()
	

OUT = elem, error_report

That is very strange :thinking: oddly enough, I just ran the script with your exact same set up, and it worked (unless I’m misunderstanding which condition is causing error for you). I’ll add that I don’t have Revit 2017 so had to upgrade your RFA to 2018.

I don’t think R18 / R17 should be an issue for this non-complex-family.

I’m uploading a screencast … so you see what I’m doing / the result I’m getting …

Perhaps a memory issue? Or scripting limitation? It puzzles me for sure…
.http://autode.sk/2G2dNjf

This is extremely puzzling, lol. I wouldn’t think the version difference would be an issue either but it is the only thing I did differently. I ran it again to double check that I hadn’t just overlooked some that weren’t associating, but it certainly associated all of them. I’m not sure what exactly would cause that – maybe try this below to see if any of the elements are in fact not returning parameters. Not sure if that will help us get anywhere with it though, but I’d imagine that must be what is happening?

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference('RevitNodes')

import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument

elem = UnwrapElement(IN[0])
paramNames = IN[1]
famNames = IN[2]

if not isinstance(paramNames, list):
	paramNames = [paramNames]
if not isinstance(famNames, list):
	famNames = [famNames]
famParams = doc.FamilyManager.Parameters
#tester =[]
elemP = []
famP = []
for e in elem:
	elemParams = e.Parameters
	
	elemAssoc = []
	famAssoc = []
	
	for param in elemParams:
		for name in paramNames:
			if param.Definition.Name == name:
				elemAssoc.append(param.Definition.Name)
	
	for fparam in famParams:
		#tester.append(fparam.Definition.Name)
		for fname in famNames:
			if fparam.Definition.Name == fname:
				famAssoc.append(fparam.Definition.Name)
	elemP.append(elemAssoc)
	famP.append(famAssoc)

	

OUT = zip(elem,elemP,famP)
1 Like

oh my… your suggestion… look at the output.

the list order changes … sometimes …
look at [0][1] which has merk, start_lagenmaat which the script will try to pair this up with [0][2] start_lagenmaat, Description … well, that obvious won’t work.

My yesterday’s screencast show several elements not being assigned … well, now we know why. Next step, figuring out why python changes the order of the list.
I’m going to try a zip alternative … unsure if python libraries could cause this issue? (how to check this?)

Coding result parameter order … seems just fine.
TransactionManager.Instance.EnsureInTransaction(doc)
error_report.append(sorted(zip(elemAssoc, famAssoc)))
for pair in ((elemAssoc[c], famAssoc[c]) for c in xrange(min(len(elemAssoc), len(famAssoc)))):

		error_report.append(pair)
	#for i,j in sorted(zip(elemAssoc, famAssoc)):
	#	try:
			#doc.FamilyManager.AssociateElementParameterToFamilyParameter(pair[0], pair[1])
			#doc.FamilyManager.CanElementParameterBeAssociated(i,j)		
	#		error_report.append(pair)
			
	#	except:
	#		error_report.append("can't assign parameter")
	TransactionManager.Instance.TransactionTaskDone()

I honestly have no idea what would cause instances of the same element types to return the parameters in a different order. Looking at the for loop for elemAssoc and famAssoc, I would assume it would return the parameters in the same order because of the input search names. And I’m not sure why it would be occurring for you, but not for me, with the same file, lol. In any case, would you try this script? It creates a dictionary for element and family parameters to create the elemAssoc and famAssoc lists, so they should return in the same order

@3Pinter sorry I just removed the Python script in my previous post because I tested it and it failed with your rfa; I’ve modified it and this here appears to be working!

import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *

clr.AddReference('RevitNodes')

import Revit
clr.ImportExtensions(Revit.GeometryConversion)
clr.ImportExtensions(Revit.Elements)

clr.AddReference('RevitServices')
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument


elements = IN[0]
epSearch = IN[1]
fpSearch = IN[2]

if not isinstance(elements, list):
	elements = [elements]
if not isinstance(epSearch, list):
	epSearch = [epSearch]
if not isinstance(fpSearch, list):
	fpSearch = [fpSearch]

for e in elements:
	e = UnwrapElement(e)
	elemParams = e.Parameters

	epNames = []
	fpNames = []
	elemAssoc = []
	famAssoc = []
	
	for ep in elemParams:
		epNames.append(ep.Definition.Name)
		
	epDict = dict(zip(epNames,elemParams))
	elemAssoc = [epDict.get(ep, "none") for ep in epSearch]
	
	famParams = doc.FamilyManager.Parameters
	
	for fp in famParams:
		fpNames.append(fp.Definition.Name)
		
	fpDict = dict(zip(fpNames,famParams))
	famAssoc = [fpDict.get(fp,"none") for fp in fpSearch]
	

	TransactionManager.Instance.EnsureInTransaction(doc)
	
	for i,j in zip(elemAssoc,famAssoc):
		try:
			doc.FamilyManager.AssociateElementParameterToFamilyParameter(i, j)
			error_report = "No errors"
			
		except:
			error_report = "can't assign parameter"
	TransactionManager.Instance.TransactionTaskDone()


OUT = elements,error_report
12 Likes

@awilliams, awesome! it works!

it still baffles me, was trying to sorted() all kinds of lists to order things but they were … etc.etc. Now, that dictionary-piece: impressive. I’ll have a look at it to understand what you did.

Again: awesome!

Yeah this one was baffling, especially so because we experienced different results with the same file :thinking: I’m thinking it could be something related to this, but the github link in the post doesn’t work, so I’m not sure what exactly that issue was… I still would have thought the elemAssoc/famAssoc would have been built in the order of the input parameter names. :face_with_raised_eyebrow: I figured the dictionary method would avoid the parameters going out of that order, because the dictionary maps the parameter name to the parameter element.

Glad its finally working!