Get Titleblock of Sheet using Python Script

Hey all,

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’


HoldenGet Sheet Titleblock.dyn (3.2 KB)

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

1 Like

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. :man_shrugging:

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.

:point_up: 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. :man_shrugging:

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

#based off of Rhythm code here
import clr

from Autodesk.Revit.DB import *

from System.Collections.Generic import List

import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager

#ToList from
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()

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.

1 Like

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.

Thanks for your help @john_pierson and @cgartland ,

Edit: I did use a dll decompiler - didn’t even think to go to github…I’m a noob

1 Like

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. :grin:

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 :slight_smile: (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.

Some great github repos:

:point_up: These are the guys I learned from when I started out with Dynamo and continue to learn from to this day.

Also, is really awesome for searching examples now. When you search something like “Sheets”, you can go to that area and see related code.


Awesome - thanks for the resources. Looks like 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.


It definitely is, so I just went with that in my case. You could modify the code to return all possible TTBLs