Get all unused legends (Sheet.Views fails)

Trying to get the views on a sheet so I can then remove those views from a LegendsView List - eventual export to PyRevit (So trying to avoid toDS for now)

Are the methods broken?
Revit 22 CYPY3
image

I finally got some traction by looking up the doc.GetElement(by id).Name but all the methods for the sheet.Views failed…

I.e. SheetsAll()[3].Views returned “Warning: AttributeError : ‘ViewSheet’ object has no attribute ‘Views’ [’ File “”, line 57, in \n’]”

Where:

x= SheetsAll()[3] 
OUT=x.Views

using the same DEFs below. The CODE below back-ends the View Names using the get element id…

import sys                                      ##Standard system input
import clr
##https://gist.github.com/gtalarico/e6be055472dfcb6f597e3dcd20d11f37
from Autodesk.Revit.DB import FilteredElementCollector
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
#  Drafting Views
from Autodesk.Revit.DB import BuiltInCategory, BuiltInParameter
from Autodesk.Revit.DB import ViewFamilyType, ViewDrafting, Element, ViewFamily 
from Autodesk.Revit.DB import ViewFamily
from Autodesk.Revit.DB import ViewType
from Autodesk.Revit.DB import ViewSheet


from Autodesk.Revit.DB import Transaction
###Creates a Drafting View###
from Autodesk.Revit.DB import Transaction, Element, ElementTransformUtils
##https://forum.dynamobim.com/t/collecting-all-elements-of-family-types-in-active-view/19838/2
from Autodesk.Revit.DB import BuiltInCategory, BuiltInParameter

# ViewFamilyTypes and Drafting views creation 
from Autodesk.Revit.DB import ViewFamilyType, ViewDrafting, Element
from Autodesk.Revit.DB import ViewFamily 

import System                                   ##filterAnnot = System.Predicate  <<Work on removing         
from System.Collections.Generic import List     ##Not same as type() = List       <<Work on removing 

import math                                     ##For truncate to integer-RA
import re                                       ##Regular expressions for 'natural' sort

doc = DocumentManager.Instance.CurrentDBDocument
uidoc=DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument

def ViewLegendsAll():
    viewLegends=[v for v in FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Views).ToElements() if v.ViewType == ViewType.Legend]
    return viewLegends

def SheetsAll():
    #sheets = FilteredElementCollector(doc).OfClass(ViewSheet).ToElements()
    sheets = [i for i in FilteredElementCollector(doc).WhereElementIsNotElementType().OfClass(ViewSheet)]# if i.IsPlaceholder == False]
    return sheets
    
def ViewPortsAll():
    #sheets = FilteredElementCollector(doc).OfClass(ViewSheet).ToElements()
    sheets = [i for i in FilteredElementCollector(doc).WhereElementIsNotElementType().OfCategory(BuiltInCategory.OST_Viewports)]# if i.IsPlaceholder == False]
    return sheets


def ViewsOnSheetsAll(sheets):
    VL_On=[]
    for sheet in UnwrapElement(sheets):
        VL_On.append([v for v in sheet.ViewPorts ])# if v.ViewType == ViewType.Legend])
    return VL_On

x=[doc.GetElement(i).Name for i in SheetsAll()[3].GetAllPlacedViews()]
OUT=x

When I dir (Sheet) I get this:

[
  [
    [
      AddFilter,
      AreJoined,
      BoundingBox,
      ByNameNumberTitleBlock,
      ByNameNumberTitleBlockAndView,
      ByNameNumberTitleBlockAndViews,
      ByNameNumberTitleBlockViewAndLocation,
      ByNameNumberTitleBlockViewsAndLocations,
      CanViewBeDuplicated,
      CropBox,
      CropBoxActive,
      CropBoxVisible,
      Curves,
      Delete,
      Discipline,
      Displaystyle,
      Dispose,
      DuplicateSheet,
      DuplicateView,
      ElementCurveReferences,
      ElementFaceReferences,
      ElementType,
      Equals,
      ExportAsImage,
      Faces,
      FilterOverrides,
      Filters,
      Finalize,
      Geometry,
      GetCategory,
      GetCategoryOverrides,
      GetChildElements,
      GetHashCode,
      GetHostedElements,
      GetIntersectingElementsOfCategory,
      GetJoinedElements,
      GetLocation,
      GetMaterials,
      GetParameterValueByName,
      GetParentElement,
      GetType,
      HideCategoriesTemporary,
      HideElementsTemporary,
      Id,
      InternalElement,
      InternalElementId,
      InternalUniqueId,
      IsAlive,
      IsCategoryHidden,
      IsFrozen,
      IsHiddeninView,
      IsPinned,
      IsViewTemplate,
      IsolateCategoriesTemporary,
      IsolateElementsTemporary,
      JoinGeometry,
      MemberwiseClone,
      MoveByVector,
      Name,
      Origin,
      Outline,
      Overloads,
      OverrideColorInView,
      OverrideInView,
      OverridesInView,
      Parameters,
      Partsvisibility,
      ReferenceEquals,
      RightDirection,
      SafeInit,
      Scale,
      Schedules,
      SetCategoryOverrides,
      SetCropBox,
      SetCropBoxActive,
      SetCropBoxVisible,
      SetDiscipline,
      SetDisplayStyle,
      SetFilterOverrides,
      SetGeometryJoinOrder,
      SetLocation,
      SetParameterByName,
      SetPartsVisibility,
      SetPinnedStatus,
      SetScale,
      SetSheetName,
      SetSheetNumber,
      SetSketchPlane,
      SheetName,
      SheetNumber,
      SketchPlane,
      Solids,
      Tessellate,
      TitleBlock,
      ToString,
      UniqueId,
      UnjoinAllGeometry,
      UnjoinGeometry,
      ViewDirection,
      Viewports,
      Views,
      __call__,
      __class__,
      __delattr__,
      __delitem__,
      __dir__,
      __doc__,
      __enter__,
      __eq__,
      __exit__,
      __format__,
      __ge__,
      __getattribute__,
      __getitem__,
      __gt__,
      __hash__,
      __init__,
      __init_subclass__,
      __iter__,
      __le__,
      __lt__,
      __module__,
      __ne__,
      __new__,
      __overloads__,
      __reduce__,
      __reduce_ex__,
      __repr__,
      __setattr__,
      __setitem__,
      __sizeof__,
      __str__,
      __subclasshook__,
      get_BoundingBox,
      get_CropBox,
      get_CropBoxActive,
      get_CropBoxVisible,
      get_Curves,
      get_Discipline,
      get_Displaystyle,
      get_ElementCurveReferences,
      get_ElementFaceReferences,
      get_ElementType,
      get_Faces,
      get_Filters,
      get_GetCategory,
      get_Id,
      get_InternalElement,
      get_InternalElementId,
      get_IsAlive,
      get_IsPinned,
      get_Name,
      get_Origin,
      get_Outline,
      get_OverridesInView,
      get_Parameters,
      get_Partsvisibility,
      get_RightDirection,
      get_Scale,
      get_Schedules,
      get_SheetName,
      get_SheetNumber,
      get_SketchPlane,
      get_Solids,
      get_TitleBlock,
      get_UniqueId,
      get_ViewDirection,
      get_Viewports,
      get_Views,
      set_InternalElementId
    ]
  ]
]

Which the .Views is in there, but fails.

Fails how?

What about get_Views?

Also it looks like you may have some placeholder sheets in there, which may want to be filtered out before running.

Fails:
image

Yesterday .Views was one of the methods, when I ran the same dir(sheet) today the methods changed - I must have had a glitch in the cache or something returning the methods above. So frustrating!

Workaround I found a few minutes ago:

pvl=[]
for i in SheetsAll():
    pvl.extend(i.GetAllPlacedViews() )          ##Flat list of IDs

pvl = list(set(pvl))                            ##Strip duplciate IDs

LegendsOnSheets = [doc.GetElement(i) for i in pvl if doc.GetElement(i).ViewType  == ViewType.Legend]   ##Elements from IDs

LegendsAll=ViewLegendsAll()

LegendsNoOnSheets = list(set(LegendsOnSheets) - set(LegendsAll)) ##https://stackoverflow.com/questions/41125909/python-find-elements-in-one-list-that-are-not-in-the-other

OUT=LegendsAll,LegendsOnSheets,LegendsNoOnSheets

Need to verify the sets are valid, but so far looks good.

List comprehension and sets don’t work with Revit objects?!? :man_facepalming:t3:

I’ll see about removing placeholder sheets.

Verified this works (Based off element IDs - quick test on revit with a couple of legends, one on two sheets and another not on sheets )

import sys                                      ##Standard system input
import clr
##https://gist.github.com/gtalarico/e6be055472dfcb6f597e3dcd20d11f37
from Autodesk.Revit.DB import FilteredElementCollector
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
#  Drafting Views
from Autodesk.Revit.DB import BuiltInCategory, BuiltInParameter
from Autodesk.Revit.DB import ViewFamilyType, ViewDrafting, Element, ViewFamily 
from Autodesk.Revit.DB import ViewFamily
from Autodesk.Revit.DB import ViewType
from Autodesk.Revit.DB import ViewSheet


from Autodesk.Revit.DB import Transaction
###Creates a Drafting View###
from Autodesk.Revit.DB import Transaction, Element, ElementTransformUtils
##https://forum.dynamobim.com/t/collecting-all-elements-of-family-types-in-active-view/19838/2
from Autodesk.Revit.DB import BuiltInCategory, BuiltInParameter

# ViewFamilyTypes and Drafting views creation 
from Autodesk.Revit.DB import ViewFamilyType, ViewDrafting, Element
from Autodesk.Revit.DB import ViewFamily 

import System                                   ##filterAnnot = System.Predicate  <<Work on removing         
from System.Collections.Generic import List     ##Not same as type() = List       <<Work on removing 

import math                                     ##For truncate to integer-RA
import re                                       ##Regular expressions for 'natural' sort

doc = DocumentManager.Instance.CurrentDBDocument
uidoc=DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument

def ViewLegendsAllId():
    viewLegends=[v.Id for v in FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Views).ToElements() if v.ViewType == ViewType.Legend]
    return viewLegends

def SheetsAll():
    #sheets = FilteredElementCollector(doc).OfClass(ViewSheet).ToElements()
    sheets = [i for i in FilteredElementCollector(doc).WhereElementIsNotElementType().OfClass(ViewSheet)]# if i.IsPlaceholder == False]
    return sheets
    
def ViewPortsAll():
    #sheets = FilteredElementCollector(doc).OfClass(ViewSheet).ToElements()
    sheets = [i for i in FilteredElementCollector(doc).WhereElementIsNotElementType().OfCategory(BuiltInCategory.OST_Viewports)]# if i.IsPlaceholder == False]
    return sheets


def ViewsOnSheetsAll(sheets):
    VL_On=[]
    for sheet in UnwrapElement(sheets):
        VL_On.append([v for v in sheet.ViewPorts ])# if v.ViewType == ViewType.Legend])
    return VL_On

sheet=SheetsAll()

pvl=[]
for i in SheetsAll():
    pvl.extend(i.GetAllPlacedViews() )          ##Flat list of IDs

pvl = list(set(pvl))                            ##Strip duplciate IDs

#LegendsOnSheets = [doc.GetElement(i) for i in pvl if doc.GetElement(i).ViewType  == ViewType.Legend]   ##Elements from IDs
LegendsOnSheets = [i for i in pvl if doc.GetElement(i).ViewType  == ViewType.Legend]   ##Elements from IDs

LegendsAll=ViewLegendsAllId()

LegendsOffSheets = [item for item in LegendsAll if not item in LegendsOnSheets] #https://stackoverflow.com/questions/41125909/python-find-elements-in-one-list-that-are-not-in-the-other
#LegendsOffSheets=list(set(LegendsAll) - set(LegendsOnSheets)) 

LegendsAll= [doc.GetElement(i) for i in LegendsAll]
LegendsOnSheets = [doc.GetElement(i) for i in LegendsOnSheets]   ##Elements from IDs
LegendsOffSheets = [doc.GetElement(i) for i in LegendsOffSheets] ##Elements from IDs
OUT=LegendsAll,LegendsOnSheets,LegendsOffSheets

ILists (the C# data structure returned by the Revit API) are not the same as Python lists, and as such you cannot utilize the same methods on them (similarly you’d an’t pass a Python list into a method expecting an IList data type).

Converting to a list via list(iListObjectHere) can usually resolve this.

1 Like

Hi,
a solution with GetDependentElements()

import clr
import sys
import System
from System.Collections.Generic import List
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument

clr.AddReference("RevitAPI")
import Autodesk
import Autodesk.Revit.DB as DB

clr.AddReference("System.Core")
from System.Linq import Enumerable


def legend_isUsed(v : DB.View) -> bool:
    typeViewPort = clr.GetClrType(DB.Viewport)
    filterClass = DB.ElementClassFilter(typeViewPort)
    lstVP = [vId for vId in v.GetDependentElements(filterClass) if doc.GetElement(vId).SheetId != DB.ElementId.InvalidElementId]
    return len(lstVP) > 0


filterFunc = System.Func[DB.Element, System.Boolean](lambda v : v.ViewType == DB.ViewType.Legend)
viewLegends = Enumerable.Where[DB.Element](DB.FilteredElementCollector(doc).OfCategory(DB.BuiltInCategory.OST_Views), filterFunc)

OUT = [[v, legend_isUsed(v)] for v in viewLegends]
1 Like

@c.poupin that is some serious voodoo ; )

definitely at the limits of my understanding with the lambda- also the → is “new” to me. (Used to be a pointer in c#) is that how we cast in Python?

I have my current function to find all unused legends- I will sub out @c.poupin 's much cleaner code for my less-clean code which is finally working to delete unused legends:

import sys                                      ##Standard system input
import clr
##https://gist.github.com/gtalarico/e6be055472dfcb6f597e3dcd20d11f37
from Autodesk.Revit.DB import FilteredElementCollector
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
#  Drafting Views
from Autodesk.Revit.DB import BuiltInCategory, BuiltInParameter
from Autodesk.Revit.DB import ViewFamilyType, ViewDrafting, Element, ViewFamily, View
from Autodesk.Revit.DB import ViewFamily
from Autodesk.Revit.DB import ViewType
from Autodesk.Revit.DB import ViewSheet


from Autodesk.Revit.DB import Transaction, TransactionGroup ##For outside and inner transactions
###Creates a Drafting View###
from Autodesk.Revit.DB import Transaction, Element, ElementTransformUtils
##https://forum.dynamobim.com/t/collecting-all-elements-of-family-types-in-active-view/19838/2
from Autodesk.Revit.DB import BuiltInCategory, BuiltInParameter

# ViewFamilyTypes and Drafting views creation 
from Autodesk.Revit.DB import ViewFamilyType, ViewDrafting, Element
from Autodesk.Revit.DB import ViewFamily 

import System                                   ##filterAnnot = System.Predicate  <<Work on removing         
from System.Collections.Generic import List     ##Not same as type() = List       <<Work on removing 

import math                                     ##For truncate to integer-RA
import re                                       ##Regular expressions for 'natural' sort

doc = DocumentManager.Instance.CurrentDBDocument
uidoc=DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument

def ViewLegendsAllId():
    viewLegends=[v.Id for v in FilteredElementCollector(doc).OfCategory(BuiltInCategory.OST_Views).ToElements() if v.ViewType == ViewType.Legend]
    return viewLegends

def SheetsAll():
    #sheets = FilteredElementCollector(doc).OfClass(ViewSheet).ToElements()
    sheets = [i for i in FilteredElementCollector(doc).WhereElementIsNotElementType().OfClass(ViewSheet)]# if i.IsPlaceholder == False]
    return sheets
    
def ViewPortsAll():
    #sheets = FilteredElementCollector(doc).OfClass(ViewSheet).ToElements()
    sheets = [i for i in FilteredElementCollector(doc).WhereElementIsNotElementType().OfCategory(BuiltInCategory.OST_Viewports)]# if i.IsPlaceholder == False]
    return sheets


def ViewsOnSheetsAll(sheets):
    VL_On=[]
    for sheet in UnwrapElement(sheets):
        VL_On.append([v for v in sheet.ViewPorts ]) 
    return VL_On
    
def Main():
    trans=Transaction(doc, "trans: Delete Unused Legends") ##Transaction group start name
    trans.Start()
    #####
    sheet=SheetsAll()
    
    pvl=[]
    for i in SheetsAll():
        pvl.extend(i.GetAllPlacedViews() )          ##Flat list of IDs
    
    pvl = list(set(pvl))                            ##Strip duplciate IDs
    
    #LegendsOnSheets = [doc.GetElement(i) for i in pvl if doc.GetElement(i).ViewType  == ViewType.Legend]   ##Elements from IDs
    
    LegendsOnSheets = [i for i in pvl if doc.GetElement(i).ViewType  == ViewType.Legend]   ##Elements from IDs

    LegendsAll=ViewLegendsAllId()
    
    LegendsOffSheets = [ i for i in LegendsAll if not i in LegendsOnSheets] #https://stackoverflow.com/questions/41125909/python-find-elements-in-one-list-that-are-not-in-the-other
    #LegendsOffSheets=list(set(LegendsAll) - set(LegendsOnSheets)) 

    LegendsAll= [doc.GetElement(i) for i in LegendsAll]
    LegendsOnSheets = [doc.GetElement(i) for i in LegendsOnSheets]   ##Elements from IDs
    #LegendsOffSheets = [doc.GetElement(i) for i in LegendsOffSheets] ##Elements from IDs
    
    LegendsOffSheets=list(dict.fromkeys(LegendsOffSheets)) ##sorted(set(LegendsOffSheets)) 	##Sorted doesn't work as is *db.element.Id 

    LegendsDeleted=["Deleted:"]
    if LegendsOffSheets:                            ##IF there are more than 0 legends off sheeets
        for i in LegendsOffSheets:
            LegendsDeleted.append(doc.GetElement(i).Name)
            doc.Delete(i) 
    
    #####
    trans.Commit()  
    return LegendsDeleted 
############################################################################################################################
OUT=Main()

I’ll post the cleaner version with c.poupin’s code after I dissect and repurpose it ; )

1 Like

it’s type hinting in Python 3.5+

1 Like

Would be neat if we had a way to measure the impacts of these different functions…
This is an alternative method to delete unused legends using C.Poupin’s viewport references… brought back to a little plainer code with comments : )

import clr                                                  ##Import .net https://forum.dynamobim.com/t/import-os-sys-system/34604/2
import System                                               ##Underlying base  system functions (Windows?)
clr.AddReference("RevitAPI")

import Autodesk                                             ##REquired for doc.Delete() Method
import Autodesk.Revit.DB as ARDB

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

from Autodesk.Revit.DB import Transaction                   ##For outside and inner transactions

doc = DocumentManager.Instance.CurrentDBDocument

clr.AddReference("System.Core")
from System.Linq import Enumerable

def legend_isUsed(v : ARDB.View): ##-> bool:                ##-> is a 'Meta data attachment' for hinting of return values
    typeViewPort = clr.GetClrType(ARDB.Viewport)            ##Viewports types (On sheets?)
    filterClass = ARDB.ElementClassFilter(typeViewPort)     ##All instance elements matching viewports type
    SheetId = [doc.GetElement(vId).SheetId for vId in v.GetDependentElements(filterClass) if doc.GetElement(vId).SheetId != ARDB.ElementId.InvalidElementId]
    
    return len(SheetId)>0                                   ##If the list has elements then then the legends are in viewports on sheets

##Filter instances matching ViewType.Legend
filterFunc = System.Func[ARDB.Element, System.Boolean](lambda v : v.ViewType == ARDB.ViewType.Legend)
##Element instances where view legends have viewports (I.e. on sheets only)
viewLegends = Enumerable.Where[ARDB.Element](ARDB.FilteredElementCollector(doc).OfCategory(ARDB.BuiltInCategory.OST_Views), filterFunc)

NotOnSheets = [ v for v in viewLegends if legend_isUsed(v)== False ]

if NotOnSheets:
    trans=Transaction(doc, 'trans:DynamoSetDraftCrop')      ##Transaction group start name
    trans.Start()                                           ##Start outside transaction

    deleted=["Deleted:"]                                    ##PRefix list with "Deleted"
    for i in NotOnSheets:                                   ##For each element not on sheets
        deleted.append(i.Name)                              ##Append its name tothe deleted list    
        doc.Delete(i.Id)                                    ##Delete the element VIA the doc handler and its Id
        
    trans.Commit()                                          ##Commit the transaction
else:
    deleted=["Nothing to delete."]                          ##Report nothign to delete

############################################################
OUT = deleted                                               ##Return our report