While on my daily trawl through the Dynamo Forum I found this post by Denis Remesnik which piqued my interest. The idea was to change the font of the Text/Dimensions but within families (although the wording of the title was a bit misleading).

This is relatively straightforward with the Revit API but I haven’t tried doing anything much with opening and closing other documents with Dynamo as I’ve never needed to really.

Either way, as the conversation carried on, Denis’ solution looked really good, but still didn’t seem to work. This is really a problem when you are scripting, as it looks awesome, and should work… but it’s not… and the headache begins. Particularly if you are in some rush to get it done, or have made some promises off the back of saying…”Ah… yeah, I’ll just script that…will be easy…will only take 10 mins”. This is exactly what I say (my colleagues will back that up) and then regret it instantly the moment the code falls over. This isn’t too often thankfully, but it does happen, and today was one of them.

However, I chucked it into Dynamo and had a play around. I tested quite a bit with room tags and I was pretty stumped as it didn’t like them. I realised after some time alternating between scratching my head and my chin that Tag FamilyInstances are more like containers for Tag Families and to get the Tag Family you needed to get the Type Element and Family of that. Kind of obvious when I realised, but it always is. It can be done as follows…

Element.GetType (Py)

import clr

clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
doc = DocumentManager.Instance.CurrentDBDocument

# Import RevitAPI
clr.AddReference("RevitAPI")
import Autodesk
from Autodesk.Revit.DB import *

def tolist(obj1):
	if hasattr(obj1,"__iter__"): return obj1
	else: return [obj1]

elems = tolist(UnwrapElement(IN[0]))

OUT = [doc.GetElement(e.GetTypeId()) for e in elems]

Anyway, I did a little refactoring and Error Handling, switched a few things around and removed the chaff that you always get in testing and this is what we ended up with and I thought it was cool so had to share…

ChangeFontInFamily.JPG

And here is the code (mostly by Denis, in part by myself and a little credit to Jeremy Tammik for this post and who is like the god of the Revit API… all hail Jeremy)

Family.ChangeFont (Py)

import clr

# Import DocumentManager and TransactionManager...
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
uidoc = DocumentManager.Instance.CurrentUIDocument

# Import RevitAPI...
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *


############## Classes ###############

# Define FamilyLoadOptions...
class FamilyOption(IFamilyLoadOptions):
	def OnFamilyFound(self,familyInUse,overwriteParameterValues):
		overwriteParameterValues = True
		return True

	def OnSharedFamilyFound(self, sharedFamily, familyInUse, familySrc, overwriteParameterValues):
		familySrc = FamilySource.Family
		overwriteParameterValues = True
		return True

############# Definitions ##############

# Open and search family doc for suitable Element Types to set font...
def UpdateTextFontInFamily(doc, f):
	# Must force close all open transactions to open family...
	TransactionManager.Instance.ForceCloseTransaction()
	try:
		arr = []
		outList.append(f.Name)
		# The Parameter we are looking for...
		bip = BuiltInParameter.TEXT_FONT
         
		# Open Family Document...
		fDoc = doc.EditFamily(f)
		ElementTypes = FilteredElementCollector(fDoc).OfClass(ElementType).ToElements()

		# Wrap in a Transaction...
		TransactionManager.Instance.EnsureInTransaction(fDoc)
		# Search all ElementTypes for Parameter and set Text Font if found...
		for i in ElementTypes:
			tArr = []
			try:
				p = i.get_Parameter(bip)
				p.Set(font)
				tArr.append(i.FamilyName)
				tArr.append(i.get_Parameter(bip).AsString())
				arr.append(tArr)
			# If parameter was not found in this ElementType then move on to next ElementType...
			except:
				continue

		outList.append(arr)        
		# Must Force Close Transaction to close FamilyDoc...
		TransactionManager.Instance.ForceCloseTransaction()
		# Load Family back into project and Close Family Document...
		fDoc.LoadFamily(doc, FamilyOption())
		fDoc.Close(False)
	except Exception,e:
		outList.append(f.Name + " Failed:-\n" + e.message)

# Convert single object to list if not list already
def tolist(obj1):
	if hasattr(obj1,"__iter__"): return obj1
	else: return [obj1]

############## Inputs ###############

run = tolist(IN[0])[0]
fams = tolist(UnwrapElement(IN[1]))
font = tolist(IN[2])[0]

############## Outputs ###############

outList = []

############## Main ###############
if run:
	for f in fams:
		try:
			# Skip InPlace Families...
			if not f.IsInPlace:
				UpdateTextFontInFamily(doc,f)
		except Exception,e:
			outList.append("Failed:-\n" + e.message)
	# Refresh the Active View to see changes immediately...
	uidoc.RefreshActiveView()
	OUT = outList
else:
	OUT = "Please set Run to True"

New topics covered in this post so far are:-

  • ForceCloseTransaction() method:
    • This is really useful for times where you have to kill the Transaction and aren’t used too often to be honest. The contextual warning for the python node does kindly tell you sometimes when you have to Force Close which is nice.
  • Classes and Class Inheritance:
    • I haven’t created any classes in any of my posts so far as I have never needed to, but the class FamilyOptions inherits the IFamilyLoadOptions that we need to use further down the script to load the Family we just modified back into the original Revit Document. Another blog by Jess Hamrick explains this well enough.
  • Opening and Closing Families:
    • I don’t really ever need to do this, but I can see this as really useful (hence the blog post). The Script above shows how to do this and where to use the Force Closing of transactions needed for this to happen. Have a read through Jeremy Tammiks link above as he has written lots on this.
  • RefreshActiveView() Method:
    • Again, I haven’t really used this in any posts so far, but I do use it quite a bit when I know the graphics will need updating (Actually, I may have use this on the Datum Planes post) Either way, if you need Revit to redraw the graphics, then doing a refresh on the active View will force Revit to redraw the graphics and you should usually call this at the end of you script to avoid unnecessary computation time. Note, this method is part of the UIDocument NameSpace, so you will need to make sure you reference this like so…
       # Import DocumentManager and TransactionManager...  clr.AddReference("RevitServices")  import RevitServices  from RevitServices.Persistence import DocumentManager  from RevitServices.Transactions import TransactionManager  doc = DocumentManager.Instance.CurrentDBDocument  uidoc = DocumentManager.Instance.CurrentUIDocument  uidoc.RefreshActiveView() 

      Unlike the Document.Regenerate() method, this doesn’t need an open Transaction.

That’s about it really, I hope you find this code useful or at least informative.

Edit:

One of the comments down below was to also change the colour of the text. To do this, you need to use the BuiltInParameter.LINE_COLOR. Also, you need to convert the RGB colour to a format Revit uses. Here is an example of how to do it based on a post on (of course) The Building Coder

By RGB Values…

This takes inputs of a TextNote Element and the R,G & B values and then sets the TextNote’s TextNoteType Color value…

###### Imports ######
import clr
# Import DocumentManager and TransactionManager...
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

# Import RevitAPI...
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *
# Import the python Math Library...
import math

###### Definitions ######
# Convert RBG colour to Revit Colour...
def CreateColour(r,g,b):
	return int(r) + int(g) * int(math.pow(2,8)) + int(b) * int(math.pow(2,16))

###### Inputs ######
txtNote = UnwrapElement(IN[0])
r = IN[1]
g = IN[2]
b = IN[3]

###### Output ######
result = None

###### Main ######

# Open a Transaction as we are making changes to the Document...
TransactionManager.Instance.EnsureInTransaction(doc)
# Get the TextNote's Symbol (TextNoteType) and then get the BuiltInParameter "LINE_COLOR"...
p = txtNote.Symbol.get_Parameter(BuiltInParameter.LINE_COLOR)
# If the Parameter Exists...
if p:
	# Try set the Colour of the TextNoteType...
	try:
		p.Set(CreateColour(r,g,b))
		result = "Colour Changed"
	# If it fails, then catch the Exception and report the error message...
	except Exception, ex:
		result = "Failed: " + ex.message
# If the Parameter is null (i.e. Not Found)...
else:
	result = "Failed: Could not find the Parameter"
# Close the Transaction...
TransactionManager.Instance.TransactionTaskDone()

# Return the result...
OUT = result 

By DSCore.Color…

This takes inputs of a TextNote Element and DSCore.Color and then sets the TextNote’s TextNoteType Color value to suit. NOTE: This code is almost identical to the previous version, however, we need to import the DSCore Node Library and change the CreateColour() definition to now use the DSCore.Color object rather than the raw RGB values. Compare the two and see if you can spot the difference… 🙂

###### Imports ######
import clr

# Import DocumentManager and TransactionManager...
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

# Import DSCore Nodes...
clr.AddReference("DSCoreNodes")
import DSCore
from DSCore import *

# Import RevitAPI...
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *
# Import the python Math Library...
import math

###### Definitions ######

# Convert DSCore colour to Revit Colour...
def CreateColour(colour):
	return int(colour.Red) + int(colour.Green) * int(math.pow(2,8)) + int(colour.Blue) * int(math.pow(2,16))

###### Inputs ######
txtNote = UnwrapElement(IN[0])
colour = IN[1]

###### Output ######
result = None

###### Main ######

# Open a Transaction as we are making changes to the Document...
TransactionManager.Instance.EnsureInTransaction(doc)
# Get the TextNote's Symbol (TextNoteType) and then get the BuiltInParameter "LINE_COLOR"...
p = txtNote.Symbol.get_Parameter(BuiltInParameter.LINE_COLOR)
# If the Parameter Exists...
if p:
	# Try set the Colour of the TextNoteType...
	try:
		p.Set(CreateColour(colour))
		result = "Colour Changed"
	# If it fails, then catch the Exception and report the error message...
	except Exception, ex:
		result = "Failed: " + ex.message
# If the Parameter is null (i.e. Not Found)...
else:
	result = "Failed: Could not find the Parameter"
# Close the Transaction...
TransactionManager.Instance.TransactionTaskDone()

# Return the result...
OUT = result 

I hope you found this useful and thanks as always for reading! 🙂

Advertisement