Python Scrips Newbie - "expected an indented block"

color
dynamo
python

#1

Hello,

I’m trying to create new materials with Python script. I have used @Scott_Crichton script from this topic but Im getting an error “expected an indented block”. I know that @mimp had the same problem, but this has not been resolved.

Have you any ideas what might be wrong with that script?
I’m just a total beginner :confused:

import clr
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)

def ToRevitColor(dynamoColor):
return Color(dynamoColor.Red, dynamoColor.Green, dynamoColor.Blue)

def ToDynamoObject(revitObject, isRevitOwned=False): # isRevitOwned should be False if Dynamo script created the object.
return revitObject.ToDSType(isRevitOwned)

doc = DocumentManager.Instance.CurrentDBDocument

names = IN[0]
transparencies = IN[1]
colors = IN[2]

newMaterials = []

for mat_name, transparency, color in zip(names, transparencies, colors):
TransactionManager.Instance.EnsureInTransaction(doc)
new_mat_id = Material.Create(doc, mat_name)
new_mat = doc.GetElement(new_mat_id)
new_mat.Color = ToRevitColor(color)
new_mat.Transparency = transparency
TransactionManager.Instance.TransactionTaskDone()
newMaterials.append(ToDynamoObject(new_mat))

OUT = newMaterials

Thanks in advance
Jack

Here is attached my .dyn file
New_Materials_Script.dyn (39.5 KB)


#2

As a start, for this section:

TransactionManager.Instance.EnsureInTransaction(doc)
new_mat_id = Material.Create(doc, mat_name)
new_mat = doc.GetElement(new_mat_id)
new_mat.Color = ToRevitColor(color)
new_mat.Transparency = transparency
TransactionManager.Instance.TransactionTaskDone()
newMaterials.append(ToDynamoObject(new_mat))

OUT = newMaterials

Indent all lines with one Tab spacing, as this lets the for function know what it is trying to apply.


#3

So the script has to look like this?

import clr
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)

def ToRevitColor(dynamoColor):
return Color(dynamoColor.Red, dynamoColor.Green, dynamoColor.Blue)

def ToDynamoObject(revitObject, isRevitOwned=False): # isRevitOwned should be False if Dynamo script created the object.
return revitObject.ToDSType(isRevitOwned)

doc = DocumentManager.Instance.CurrentDBDocument

names = IN[0]
transparencies = IN[1]
colors = IN[2]

newMaterials = []

for mat_name, transparency, color in zip(names, transparencies, colors):
	TransactionManager.Instance.EnsureInTransaction(doc)
	new_mat_id = Material.Create(doc, mat_name)
	new_mat = doc.GetElement(new_mat_id)
	new_mat.Color = ToRevitColor(color)
	new_mat.Transparency = transparency
	TransactionManager.Instance.TransactionTaskDone()
	newMaterials.append(ToDynamoObject(new_mat))

	OUT = newMaterials

If so, then it is still not working. And error is same.
New_Materials_Script.dyn (39.5 KB)


#4

Not at the PC right now, so can’t plug the code into Dynamo.

Take the indent off the OUT line and see if that helps.


#5

You need to indent the definitions too. Add a tab before “return” for both definitions.

Also, remove indent on OUT. It should sit out of the for loop.

Also also, transaction should really sit out of the loop too, but it will still work as is


#6

Ok, so now it shows me another error.

Traceback (most recent call last):
File “”, line 29, in
Exception: The given value for name is already in use as a material element name.
Parameter name: name

At this moment my script looks like this:

import clr
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)

def ToRevitColor(dynamoColor):
	return Color(dynamoColor.Red, dynamoColor.Green, dynamoColor.Blue)

def ToDynamoObject(revitObject, isRevitOwned=False): # isRevitOwned should be False if Dynamo script created the object.
	return revitObject.ToDSType(isRevitOwned)

doc = DocumentManager.Instance.CurrentDBDocument

names = IN[0]
transparencies = IN[1]
colors = IN[2]

newMaterials = []

for mat_name, transparency, color in zip(names, transparencies, colors):
	TransactionManager.Instance.EnsureInTransaction(doc)
	new_mat_id = Material.Create(doc, mat_name)
	new_mat = doc.GetElement(new_mat_id)
	new_mat.Color = ToRevitColor(color)
	new_mat.Transparency = transparency
TransactionManager.Instance.TransactionTaskDone()
newMaterials.append(ToDynamoObject(new_mat))

OUT = newMaterials

#7

try this…

import clr
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)

def ToRevitColor(dynamoColor):
	return Color(dynamoColor.Red, dynamoColor.Green, dynamoColor.Blue)

def ToDynamoObject(revitObject, isRevitOwned=False): # isRevitOwned should be False if Dynamo script created the object.
	return revitObject.ToDSType(isRevitOwned)

doc = DocumentManager.Instance.CurrentDBDocument

names = IN[0]
transparencies = IN[1]
colors = IN[2]

newMaterials = []

for mat_name, transparency, color in zip(names, transparencies, colors):
    try:
	    TransactionManager.Instance.EnsureInTransaction(doc)
	    new_mat_id = Material.Create(doc, mat_name)
	    new_mat = doc.GetElement(new_mat_id)
	    new_mat.Color = ToRevitColor(color)
	    new_mat.Transparency = transparency
        TransactionManager.Instance.TransactionTaskDone()
        newMaterials.append(ToDynamoObject(new_mat))
    except:
        newMaterials.append(None)

OUT = newMaterials

I have added Try/Except. It should pass now, but it will return a null if there was an error (for instance if you have a material with that name already in the project).


#8

Hang on, I edited this in the forum and Python gets a little funny with mixed tabs and spaces and failed when i ran it. I have also pulled the transaction out of the loop…

import clr
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)

def ToRevitColor(dynamoColor):
	return Color(dynamoColor.Red, dynamoColor.Green, dynamoColor.Blue)

def ToDynamoObject(revitObject, isRevitOwned=False): # isRevitOwned should be False if Dynamo script created the object.
	return revitObject.ToDSType(isRevitOwned)

doc = DocumentManager.Instance.CurrentDBDocument

names = IN[0]
transparencies = IN[1]
colors = IN[2]

newMaterials = []

TransactionManager.Instance.EnsureInTransaction(doc)
for mat_name, transparency, color in zip(names, transparencies, colors):
	try:
		new_mat_id = Material.Create(doc, mat_name)
		new_mat = doc.GetElement(new_mat_id)
		new_mat.Color = ToRevitColor(color)
		new_mat.Transparency = transparency
		newMaterials.append(ToDynamoObject(new_mat))
	except:
		newMaterials.append(None)
TransactionManager.Instance.TransactionTaskDone()

OUT = newMaterials

#9

Wow! It’s working! Thank you.
Pitty that it is not overriding materials :frowning:


#10

Nah, this creates new materials, i can do a condition to check first if material exists and use that instead of creating a new one.

I will sort in the morning as it’s late where i am.


#11

Hi @Jacko

This should now override any materials with the same name.

I have also added an option to disable whether the Material is Owned by Dynamo (this feature can be annoying sometimes as Dynamo keeps track of which Elements it made and always remembers these for modification, now you have a choice of whether you want to do this. I prefer to disable this personally, particularly on a generic script that I will use many times in different situations on the same document).

I have also made sure that even if you are passing in one one item (not in a list), that this will still work and I have added a new OUT variable for errors that will give you a description of the error it encountered.

Additionally, I have commented on what’s going on to make it forum friendly so people new to python can understand step by step.

import clr
clr.AddReference("RevitAPI")
from Autodesk.Revit.DB import *
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

doc = DocumentManager.Instance.CurrentDBDocument

clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)

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

# Convert DSCore Colour to Revit Colour...
def ToRevitColor(dynamoColor):
	return Color(dynamoColor.Red, dynamoColor.Green, dynamoColor.Blue)

# Add Dynamo Ownership to Element...
def ToDynamoObject(revitObject, isRevitOwned=False): # isRevitOwned should be False if Dynamo script created the object.
	return revitObject.ToDSType(isRevitOwned)

# Gets the Material Id by name from a list of Materials ...
def GetMaterialIdByName(mat_Name, mats):
	return next((m.Id for m in mats if m.Name == mat_Name), None)

# Used to Ensure that object is in a list...
def tolist(obj1):
	if hasattr(obj1,"__iter__"): return obj1
	else: return [obj1]

################ IN Variables ################

names = tolist(IN[0])
transparencies = tolist(IN[1])
colors = tolist(IN[2])
toDynObj = tolist(IN[3])[0]

################ OUT Variables ################

newMaterials = []
err = []

################ Main Body ################

# Get all the Materials that are currently created in the Revit doc...
mats = FilteredElementCollector(doc).OfClass(Material).ToElements()

TransactionManager.Instance.EnsureInTransaction(doc)
for mat_name, transparency, color in zip(names, transparencies, colors):
	try:
		# Check if material exists already...
		new_mat_id = GetMaterialIdByName(mat_name, mats)
		# Create one if it doesn't...
		if not new_mat_id:
			new_mat_id = Material.Create(doc, mat_name)
		# Get Material from Revit doc...
		new_mat = doc.GetElement(new_mat_id)
		# Set Material colour...
		new_mat.Color = ToRevitColor(color)
		# Set Material Transparency...
		new_mat.Transparency = transparency
		
		# If toDynObj is true, Dynamo will own the Elements it creates and overwrite those...
		if toDynObj:
			newMaterials.append(ToDynamoObject(new_mat))
		# if toDynObj is false, Dynamo will not own this Element...
		else:
			newMaterials.append(new_mat)
		err.append(None)
	except Exception, ex:
		newMaterials.append(None)
		err.append(ex.message)
TransactionManager.Instance.TransactionTaskDone()

################ OUT ################

OUT = newMaterials, err  

Cheers,
Dan


#12

Hi jacek -

Try this code if you can make any sense of it


#13

@Daniel_Woodcock1 Sir, you are great!
Well it is a real black magic for me :smiley:
I see I have a lot to learn.


#14

You’re welcome @Jacko! Glad this is now working for you!

Ah, it might seem like black magic now, but I’m sure soon enough, you’ll be up and running with python and this will look simple! It’s scarier than it looks! :grin: