I’m trying to get the titleblock of a sheet using the python script node. The script I’m creating is actually much bigger, but for the sake of simplicity I’ve just included this one node. What I would like it to do: collect all sheets from the model. Get and output the titleblocks associated with the sheets.
I’ve been looking at the Rhythm (package) node “Sheet.Titleblock” which does what I want, however I’m trying to avoid custom packages. I’m writing this script to be used in Dynamo Player for people who do not know (and do not care to know) Dynamo - they just want a solution that works. I don’t have time to make sure the correct packages are installed for 100 or so computers.
So all that said I’m trying to translate the code below from the “Sheet.Titleblock” node (written in c#) to python script. Any help figuring this out would be much appreciated!
/// <summary>
/// This node will grab the titleblock from the given sheet.
/// </summary>
/// <param name="viewSheet">The sheet to get titleblock from.</param>
/// <returns name="titleblock">The sheet's titleblock.</returns>
/// <search>sheet, sheets, titleblock</search>
public static List<Element> Titleblock(Revit.Elements.Views.Sheet viewSheet)
{
return new List<Element>((IEnumerable<Element>) ((IEnumerable<Element>) new FilteredElementCollector(DocumentManager.Instance.get_CurrentDBDocument(), ((Element) viewSheet).get_InternalElement().get_Id()).OfCategory((BuiltInCategory) -2000280).ToElements()).Select<Element, Element>((Func<Element, Element>) (e => ElementWrapper.ToDSType(e, true))).ToArray<Element>());
}
Currently I’m stuck at .get_InternalElement(). I get the warning: AttributeError: ‘ViewSheet’ object has no attribute ‘InternalElement’
I think this is equivalent to the UnwrapElement function in Python. I would break out the entire return line into more legible variables and unwrap the view before passing it into the FilteredElementCollector
This comes up all the time and I understand some of the reasoning. But, what keeps Dynamo awesome is the community and the free packages they build and maintain.
More often than not, people hesitate to use packages because they fail to get their deployment strategy in line with their goals of making tools accesible.
Eg. "I tried to give this DYN to my staff for use in Dynamo player and it does not work.
This right here, is a problem because recreating everything from scratch in python is very anti-Dynamo in my opinion.
Trying to migrate everything to python yourself is another problem because it takes time to do and then to maintain it now. If something changes in the API between Revit versions, you now “own” that python script and are on the hook for it. But if that is more appealing than deploying packages, that is cool I guess.
Like, I said, I understand the reasoning, and I do not mean to sound negative, but it does come up a lot and it is unfortunate that managing Dynamo packages is not simpler, as it really should be.
Now, given the original question at hand,
The InternalElement equivalent in Python is UnwrapElement(elementGoesHere).
So looking at how that should work in Python, I came up with this.
#Provided under the BSD 3-Clause License https://opensource.org/licenses/BSD-3-Clause
#based off of Rhythm code here https://github.com/johnpierson/RhythmForDynamo/blob/master/src/Rhythm/Revit/Elements/Sheet.cs
import clr
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
clr.AddReference('System')
from System.Collections.Generic import List
clr.AddReference('RevitServices')
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
#ToList from https://github.com/dimven/SpringNodes/
def tolist(obj1):
if hasattr(obj1,'__iter__') : return obj1
else : return [obj1]
# the current doc
doc = DocumentManager.Instance.CurrentDBDocument
#our output variable
titleBlocks = []
#our input unwrapped and
sheets = tolist(IN[0])
for i in sheets:
sheet = UnwrapElement(i) #the internalelement equivalent
firstTTBL = FilteredElementCollector(doc,sheet.Id).OfCategory(BuiltInCategory.OST_TitleBlocks).FirstElement()
titleBlocks.append(firstTTBL)
OUT = titleBlocks
I also noticed that your code for the C# equivalent of the node, appears to be from a decompiler… (correct me if I am wrong, but that code originally posted is not what I typed) Rhythm is open source and you can peek under the hood and see the code in it’s as written state. No need to decompile DLLs.
Wow. So thorough - John your forum etiquette is impeccable. I totally agree with your philosophy about packages vs python and accessing the API…but I seem to have found myself between a rock and a hard place. People don’t want to take the time to learn/prep, but they also want a solution that works instantly. I’ve developed quite a few scripts that sit idle b/c others in the office don’t have the right packages installed.
Anyway, as you have probably guessed - I’m pretty new to coding in general. I don’t really know c# at all (learning), though I’m starting to get the hang of python. I’ll take a look at what you posted and try to integrate it into the larger script.
Sounds awesome! Best of luck along the way! It is an addicting journey and I hope the python code I posted pushes you a little further in this journey.
No worries about this, I understand as well and mostly thought it was a bummer that you were having to struggle through the decompiled gibberish (Also, I consider myself a noob as well and it is not a bad thing) .
I want to throw it out there that there are some fabulous resources available for getting started.
Awesome - thanks for the resources. Looks like APIDocs.co is an alternative to adding assemblies into the Visual Studio object browser to look at their namespaces (which is what I’ve been doing to date).
Also question for learning’s sake: in the script you provided why did you choose .FirstElement() instead of .ToElements() - is it possible for a sheet to have two titleblocks? My assumption is that .OfCategory() will cull out anything that isn’t a titleblock family from the collector, which I would have assumed left me with only 1 element…
My guess would be that they return different things. .ToElements() returns an IList of size 1 versus .FirstElement() returns an Element. In the first case, you would have to convert the IList to a single item, so it just saves a step.