Gridlines to ScopeBox for mutliple Views

Hi hoping someone will be able to help me figure this out. Im trying to get the following python code (setting gridline extents to scopeboxes) to run on a list of views rather than the active view it currently works on.

I’ve read through a few topics. I’ve managed to get it working using the python code in these topics. thank you @Ewan_Opie @kdh @c.poupin

https://forum.dynamobim.com/t/grid-to-crop-line-using-python/77447

https://forum.dynamobim.com/t/grid-extents-alignment-based-on-crop-region/55538/5

I have tried changing these:-

activView = doc.ActiveView
cropBox = activView.CropBox

to use UnwrapElement(IN[0]) for ‘activView’ and a few variations of that to deal with a list of views, but it keeps coming up with an error.

"Warning: IronPythonEvaluator.EvaluateIronPythonScript operation failed. 
Traceback (most recent call last):
  File "<string>", line 78, in <module>
AttributeError: 'List[object]' object has no attribute 'CropBox'"

So assuming i need to do some sort of loop with ‘cropBox’, but again i can’t seem to get it work.

Could anyone help me out please?

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

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

#import transactionManager and DocumentManager (RevitServices is specific to Dynamo)
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
app = uiapp.Application
sdkNumber = int(app.VersionNumber)


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

def createDatumLine(boundLines, grid):
	gridLine = None
	curveG = grid.Curve.ToProtoType()
	vectGrid = curveG.Direction 
	ptmid = curveG.PointAtParameter(0.5)
	lstPtToLine = []
	for lineBound in boundLines:
		lineBoundDs = lineBound.ToProtoType()
		ptmid = DSGeo.Point.ByCoordinates(ptmid.X, ptmid.Y, lineBoundDs.StartPoint.Z)
		interResultA = ptmid.Project(lineBoundDs, vectGrid)
		interResultB = ptmid.Project(lineBoundDs, vectGrid.Reverse())

		if len(interResultA) > 0:
			lstPtToLine.append(interResultA[0].ToXyz())
		if len(interResultB) > 0:
			lstPtToLine.append(interResultB[0].ToXyz())			

	if len(lstPtToLine) == 2:
		
		# Enforce Line Orientation
		if lstPtToLine[0].Y > lstPtToLine[1].Y:
			# The grid is vertial and starts from the top
			gridLine = Autodesk.Revit.DB.Line.CreateBound(lstPtToLine[0], lstPtToLine[1])
		elif lstPtToLine[0].X < lstPtToLine[1].X and lstPtToLine[0].Y == lstPtToLine[1].Y:
			# The grid is horizontal and starts from the left
			gridLine = Autodesk.Revit.DB.Line.CreateBound(lstPtToLine[0], lstPtToLine[1])
		else:
			# The grid orientation is incorrect, swap the direction
			gridLine = Autodesk.Revit.DB.Line.CreateBound(lstPtToLine[1], lstPtToLine[0])


	return gridLine

					
def getBoundLines(bbx, Zvalue = 0):
	lstPt = []
	lstLine = []
	lstPt.append(XYZ(bbx.Min.X, bbx.Min.Y, Zvalue))
	lstPt.append(XYZ(bbx.Max.X, bbx.Min.Y, Zvalue))
	lstPt.append(XYZ(bbx.Max.X, bbx.Max.Y, Zvalue))
	lstPt.append(XYZ(bbx.Min.X, bbx.Max.Y, Zvalue))
	for idx, pt in enumerate(lstPt):
		if idx == 0:
			lstLine.append(Line.CreateBound(lstPt[- 1], pt))
		else:	
			lstLine.append(Line.CreateBound(lstPt[idx - 1], pt))			
	return lstLine		

activView = doc.ActiveView
cropBox = activView.CropBox 


fecGrids = FilteredElementCollector(doc, activView.Id).OfClass(DatumPlane).ToElements()
cutOffset = fecGrids[0].GetCurvesInView(DatumExtentType.ViewSpecific, activView)[0].GetEndPoint(0).Z
fecGrids = [x for x in fecGrids if isinstance(x, DB.Grid)]

outLst = []
boundLines = getBoundLines(cropBox, cutOffset)


TransactionManager.Instance.EnsureInTransaction(doc)
for grid in fecGrids:
	newGLine = createDatumLine(boundLines, grid)
	if newGLine and True:
		grid.SetCurveInView(DatumExtentType.ViewSpecific, activView, newGLine)
		outLst.append(newGLine)

TransactionManager.Instance.TransactionTaskDone()

OUT = outLst
1 Like

Once you convert the activView to a list, anything which utilizes that variable will need to be inside of a for loop so that your Python code knows you want to ‘do these actions for each element in the list.

Something like this should get you started:

activViews = UnwrapElement(IN[0])
for activView in activeViews:

These lines would go in in place of the activView line. After that take every line from cropBox to just before the OUT line, and add one level of indent (highlight them all and hit tab once).

That should get you a good for loop going for each of the views.
Now you’ll want to edit the variable names to be more accurate (technically these aren’t active views, but a list of views so views and view would be better suited, and add some error handling (ie: what if someone input a single view instead of many and so you no longer have a list?).

2 Likes

Thank you! @jacob.small, great advice.

I renamed all the ActivViews to view/views as it started to make more sense.

seem to be working for both single and lists now!

might not be the best way of doing it, but it works.

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

Views = tolist(UnwrapElement(IN[0]))
for View in Views:
	cropBox = View.CropBox

i did try and use this below, I’ve used this before to deal with list of lists for views. but i end up with the same error as before.

Traceback (most recent call last):
  File "<string>", line 88, in <module>
  File "<string>", line 83, in getViewCrop
AttributeError: 'List[object]' object has no attribute 'CropBox'
Views = UnwrapElement(IN[0])

def ProcessList(_func, _list):
    return map( lambda x: ProcessList(_func, x) if type(x)==list else _func(x), _list )
    
def getViewCrop(view):
	return view.CropBox

if isinstance (Views, list):
	cropBox = ProcessList(getViewCrop, Views)
else:
	cropBox = getViewCrop(Views)

Thanks again!

1 Like