Basic problem. inputting levels in list format not accepted

I have a problem with my plan creation dynamo schematic.
I require a node to feed a script levels. using the level node it works fine. But i’m creating plans on multiple levels so the level needs to change with the element that is used to create the plan. so I’m now using that element gettnig the level name and then the level id by thet name and feeding that to the custon node with the levels in a list (that same way the rooms which generate the plan are provided) but it’s not working.
That might not be a very clear description but the attached jpeg should be obvious. What am I missing?

It’s hard to help when we can’t see the entire code, but based on the error and at least the portion handling levels, it looks like your code is written for a single element and not a list. You have to write your code to loop through lists. It won’t handle them automatically like Dynamo nodes.

Thanks for the quick reply @Nick_Boyts
Yep that’s the problem isn’t it. I’ve copied the code below. It’s originally by Archi-labs with some modifications since then. I don’t really know how to go about setting it to loop but as it’s doing it for the rooms with this code

if isinstance(IN[0], list):
	rooms = UnwrapElement(IN[0])
else:
	rooms = [UnwrapElement(IN[0])]

Should I be doing something similar with the

if isinstance(IN[4], list):
       levelId = UnwrapElement(IN[4]).Id 
else:
       levelId = [UnwrapElement(IN[4]).Id]

No doubt it’s more complex than that…Original code below

# Copyright(c) 2015, Konrad K Sobon
# @arch_laboratory, http://archi-lab.net

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

# Import Element wrapper extension methods
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)

# Import geometry conversion extension methods
clr.ImportExtensions(Revit.GeometryConversion)

# 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")
import Autodesk
from Autodesk.Revit.DB import *

import System
from System import Array
from System.Collections.Generic import *

import sys
pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
sys.path.append(pyt_path)

#The inputs to this node will be stored as a list in the IN variable.
dataEnteringNode = IN

if isinstance(IN[0], list):
	rooms = UnwrapElement(IN[0])
else:
	rooms = [UnwrapElement(IN[0])]
nameSuffix = IN[1]
bboxOffset = IN[2]
runMe = IN[3]

def OffsetBBox(bbox, offset):
	bboxMinX = bbox.Min.X - offset
	bboxMinY = bbox.Min.Y - offset
	bboxMinZ = bbox.Min.Z - offset
	bboxMaxX = bbox.Max.X + offset
	bboxMaxY = bbox.Max.Y + offset
	bboxMaxZ = bbox.Max.Z + offset
	newBbox = BoundingBoxXYZ()
	newBbox.Min = XYZ(bboxMinX, bboxMinY, bboxMinZ)
	newBbox.Max = XYZ(bboxMaxX, bboxMaxY, bboxMaxZ)
	return newBbox

try:
	levelId = UnwrapElement(IN[4]).Id 
	errorReport = None
	if runMe:
		viewTypes = FilteredElementCollector(doc).OfClass(ViewFamilyType)
		for i in viewTypes:
			if i.ViewFamily == ViewFamily.FloorPlan:
				viewTypeId = i.Id
				break
			else:
				continue
		
		existingPlans = FilteredElementCollector(doc).OfClass(View).ToElements()
		
		existingPlanNames, existingPlanElements = [], []
		for i in existingPlans:
			if not i.IsTemplate:
				if i.ViewType == ViewType.FloorPlan:
					existingPlanNames.append(i.ToDSType(True).Name)
					existingPlanElements.append(i)
					
		# Start Transaction
		TransactionManager.Instance.EnsureInTransaction(doc)
		
		floorPlans = []
		for i in rooms:
			#levelId = i.LevelId
			bbox = i.BoundingBox[doc.ActiveView]
			newBbox = OffsetBBox(bbox, bboxOffset)
			viewName = i.get_Parameter(BuiltInParameter.ROOM_NUMBER).AsString() + " " + i.get_Parameter(BuiltInParameter.ROOM_NAME).AsString().Replace("-", " - ")  + " - " +  nameSuffix
			if viewName in existingPlanNames:
				view = existingPlanElements[existingPlanNames.index(viewName)]
				view.CropBox = newBbox
				view.CropBoxActive = True
				view.CropBoxVisible = False
				floorPlans.append(view)
			else:
				newView = ViewPlan.Create(doc, viewTypeId, levelId)
				newView.Name = viewName
				newView.CropBox = newBbox
				newView.CropBoxActive = True
				newView.CropBoxVisible = False
				floorPlans.append(newView)
	
		# End Transaction
		TransactionManager.Instance.TransactionTaskDone()
	else:
		errorReport = "Run Me set to False"
except:
	# if error accurs anywhere in the process catch it
	import traceback
	errorReport = traceback.format_exc()

#Assign your output to the OUT variable
if errorReport == None:
	OUT = floorPlans
else:
	OUT = errorReport

Close. The isinstance portion of the code ensures that no matter how many levels you supply, the code will convert them to a single list so your structure is always the same. The parameter levelId is what’s used to set the level on each view, but here we need to define the list of levels that will be used. Instead of setting these lists to levelId, set them to something like levels.

The second part is getting the code to loop through those levels properly. In the original code you have

levelId = UnwrapElement(IN[4]).Id

which is expecting a single level and just setting that as the parameter to use for all views. This whole line needs to be removed since we now have a list of levels to loop through. We’ll replace it in the next section. A little farther down you have

for i in rooms:

which is where the code loops through each room (i) in the list of rooms (rooms). Here is where you would also loop through each room’s respective level. Because each room is paired up with a specific level, we have to loop through the two lists simultaneously. This is done by zipping the lists together.

for i,j in zip(rooms,levels):
    levelId = j.Id

This code ensures that our rooms and levels are looped through together, in their respective pairs. The second line is where we get the levelId from each individual level as we loop through the list.

I think that’s all you’ll need to do at this point. Hopefully that explains it all.

3 Likes

Thanks @Nick_Boyts that’s really helpful
I’ll try that out when I get a break and get back to you.

Tried this morning. What you say makes sense to me but my Python execution is sadly lacking… I don’t really understand j.Id and I’m getting


Sorry I bodge my way through this stuff without a proper education in it but the results save me hundreds of hours. I’m utterly indebted to people like yourselves.

# Copyright(c) 2015, Konrad K Sobon
# @arch_laboratory, http://archi-lab.net

import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

# Import Element wrapper extension methods
clr.AddReference("RevitNodes")
import Revit
clr.ImportExtensions(Revit.Elements)

# Import geometry conversion extension methods
clr.ImportExtensions(Revit.GeometryConversion)

# 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")
import Autodesk
from Autodesk.Revit.DB import *

import System
from System import Array
from System.Collections.Generic import *

import sys
pyt_path = r'C:\Program Files (x86)\IronPython 2.7\Lib'
sys.path.append(pyt_path)

#The inputs to this node will be stored as a list in the IN variable.
dataEnteringNode = IN

if isinstance(IN[0], list):
	rooms = UnwrapElement(IN[0])
else:
	rooms = [UnwrapElement(IN[0])]
	
nameSuffix = IN[1]
bboxOffset = IN[2]
runMe = IN[3]

def OffsetBBox(bbox, offset):
	bboxMinX = bbox.Min.X - offset
	bboxMinY = bbox.Min.Y - offset
	bboxMinZ = bbox.Min.Z - offset
	bboxMaxX = bbox.Max.X + offset
	bboxMaxY = bbox.Max.Y + offset
	bboxMaxZ = bbox.Max.Z + offset
	newBbox = BoundingBoxXYZ()
	newBbox.Min = XYZ(bboxMinX, bboxMinY, bboxMinZ)
	newBbox.Max = XYZ(bboxMaxX, bboxMaxY, bboxMaxZ)
	return newBbox
	

levels = UnwrapElement(IN[4])

try:
	errorReport = None
	if runMe:
		viewTypes = FilteredElementCollector(doc).OfClass(ViewFamilyType)
		for i in viewTypes:
			if i.ViewFamily == ViewFamily.FloorPlan:
				viewTypeId = i.Id
				break
			else:
				continue
		
		existingPlans = FilteredElementCollector(doc).OfClass(View).ToElements()
		
		existingPlanNames, existingPlanElements = [], []
		for i in existingPlans:
			if not i.IsTemplate:
				if i.ViewType == ViewType.FloorPlan:
					existingPlanNames.append(i.ToDSType(True).Name)
					existingPlanElements.append(i)
					
		# Start Transaction
		TransactionManager.Instance.EnsureInTransaction(doc)
		
		floorPlans = []
	
		for i,j in zip(rooms,levels):
			levelId = j.Id
			#levelId = i.LevelId
			bbox = i.BoundingBox[doc.ActiveView]
			newBbox = OffsetBBox(bbox, bboxOffset)
			viewName = i.get_Parameter(BuiltInParameter.ROOM_NUMBER).AsString() + " " + i.get_Parameter(BuiltInParameter.ROOM_NAME).AsString().Replace("-", " - ")  + " - " +  nameSuffix
			if viewName in existingPlanNames:
				view = existingPlanElements[existingPlanNames.index(viewName)]
				view.CropBox = newBbox
				view.CropBoxActive = True
				view.CropBoxVisible = False
				floorPlans.append(view)
			else:
				newView = ViewPlan.Create(doc, viewTypeId, levelId)
				newView.Name = viewName
				newView.CropBox = newBbox
				newView.CropBoxActive = True
				newView.CropBoxVisible = False
				floorPlans.append(newView)
	
		# End Transaction
		TransactionManager.Instance.TransactionTaskDone()
	else:
		errorReport = "Run Me set to False"
except:
	# if error accurs anywhere in the process catch it
	import traceback
	errorReport = traceback.format_exc()

#Assign your output to the OUT variable
if errorReport == None:
	OUT = floorPlans
else:
	OUT = errorReport


No wait I got it working. so j is from the list and appending it with .Id tells it it’s an ID. It’s running!

@wildtypitch you are feeding your IN[4] a list of strings and not a list of levels, revert to the original snapshot you posted, it shows that its a list of levels, which will work in that case I suppose.