Get Level Dependant Elements

Let's create a unique tool that uses Transaction RollBack in an unusual way. We will get elements associated with a selected level, and you'll discover a clever trick to achieve that.

Get Level Dependant Elements

Let's create a unique tool that uses Transaction RollBack in an unusual way. We will get elements associated with a selected level, and you'll discover a clever trick to achieve that.

Summary

Brainstorm the tool

So we want to select a level in our project and then get a list of all related elements to it. There are different ways to do that, but I want to show you a trick of using Transaction's RollBack.

For this tool we will need:

  • Get All Levels

  • Select Level

  • Trick to get associated ElementIds

  • RollBack Transaction

  • Display Results

Prepare a Button

Create a button in your extension for this tool. It's a very simple tool and we need very little preparation.

# -*- coding: utf-8 -*-
__title__ = "05.04 - GetLevelDependantElements"
__doc__ = """
_____________________________________________________________________
Description:
Get dependant elements to selected levels by using Transaction.RollBack()
You will see how to use transactions by thinking outside of the box.
_____________________________________________________________________
Author: """

#⬇️ IMPORTS
from Autodesk.Revit.DB import *
from pyrevit           import forms

#📦 VARIABLES
uidoc = __revit__.ActiveUIDocument
doc   = __revit__.ActiveUIDocument.Document
Get All Levels

First of all we need to get all Levels. We will need to use FilteredElementCollector class for that.

❗You will learn everything you need to know about this Class in the next module!
For now, you can use this snippet I provided below.

all_levels = FilteredElementCollector(doc)\
                        .OfCategory(BuiltInCategory.OST_Levels)\
                        .WhereElementIsNotElementType()\
                        .ToElements()

💡 I will briefly describe what it does line by line.

  • Create Instance of FilteredElementCollector by providing doc

  • Get All Elements with Category of Level

  • Get All Instance Elements (Exclude Types)

  • Convert collector into a list of Elements.

💡 P.S. It's a single line of code but it's broken down into multiple using '\' symbol

Select a Level

I mentioned before, that creating a custom form is not a beginner friendly topic, but thanks to pyRevit we have a lot of pre-written forms we could use.

This time we are going to use SelectFromList function from pyrevit.forms. This allows us to provide a list of any values and it will create a form asking user to select one or multiple options.

Here is code example from pyRevit docs:

from pyrevit import forms
items = ['item1', 'item2', 'item3']
res     = forms.SelectFromList.show(items, 
                multiselect=True,
                button_name='Select Item')

print(res) # Print Selected Item

👆 The code snippet above works well for list of text items. But if you use a list of actual Elements we can use name_attr to display correct names in the form.

We need to provide name of the property as the argument, and it will try to get that property. In case of Levels, we can use "Name" property since it's readable and it will display elements names correctly by getting Level.Name.

👇 Let's use this snippet where we use all_levels and allow MultiSelect.

from pyrevit import forms
selected_levels  = forms.SelectFromList.show(all_levels, 
                        multiselect=True,
                        name_attr='Name',
                        button_name='Select Levels')

print(selected_levels) # Print Selected Levels
Trick: Delete Selected Level

Now, let's Delete our selected levels. I know it sounds illogical to delete our Level, but don't worry, it won't be deleted from our project for real.

It's very simple to Delete elements from the project. We just need to provide ElementId of an element that we want to Delete.

And the reason we want to do that, is because this method will return you a list of element ids of deleted elements.

💡So the Trick here is when we delete a Level, all related elements are going to be deleted as well, and a list of all these ElementIds will be returned.

RollBack Transaction

Once we have this list of ElementIds, we can RollBack Transaction, so our level will never be deleted from the project for real.

👇Here is the Snippet for that

# Delete Selected Level to get a list of Dependant Elements.
# RollBack() so level is not Delted!
for level in selected_levels:
    print(level)

    t = Transaction(doc, 'temp Delete')
    t.Start()
    element_ids = doc.Delete(level.Id)
    t.RollBack()

    # Convert ElementIds to Elements
    elements = [doc.GetElement(e_id) for e_id in element_ids]

💡 Now we have a list of Elements that are related to selected Level

Display Results

Last step is simple, we can print all elements in that List to see what's happening.

But be aware that you might get hundreds or even thousands of elements. So to get a better overview I will only print unique types of elements to see what kind of elements are related to the Level.

    unique_types = {type(el) for el in elements}

    print('{} has {} Dependant Elements.'.format(level.Name, len(elements)))
    print('It includes {} unique Types'.format(len(unique_types)))

    for typ in unique_types:
        print(typ)

💡Don't create a variable type, because there is a type() function in python!

👇👀 And this will print something like this in the console.

Final Code

Let's put it all together:

# -*- coding: utf-8 -*-
__title__ = "05.04 - GetLevelDependantElements"
__doc__ = """Date    = 07.01.2023
_____________________________________________________________________
Description:
Code from lesson 05.04
Tool: Get dependant elements to selected levels by using Transaction.RollBack()
You will see how to use transactions by thinking outside of the box.
_____________________________________________________________________
Author: Erik Frits"""

#⬇️ IMPORTS
from Autodesk.Revit.DB import *
from pyrevit           import forms

#📦 VARIABLES
uidoc = __revit__.ActiveUIDocument
doc   = __revit__.ActiveUIDocument.Document

all_levels = FilteredElementCollector(doc)\
                .OfCategory(BuiltInCategory.OST_Levels)\
                .WhereElementIsNotElementType()\
                .ToElements()

#🎯 MAIN

#1️⃣ Select Levels
selected_levels = forms.SelectFromList.show(all_levels,
                                multiselect=True,
                                name_attr='Name',
                                button_name='Select Levels')


#2️⃣ Attempt to Delete Selected Level to get a list of Dependant Elements.
for level in selected_levels:
    print(level)

    t = Transaction(doc, 'temp')
    t.Start()
    element_ids = doc.Delete(level.Id)
    
    #3️⃣ RollBack so level is not deleted!
    t.RollBack()

    #4️⃣ Convert to Elements
    elements = [doc.GetElement(e_id) for e_id in element_ids]
    
    #5️⃣ Print Unique Types
    unique_types = {type(el) for el in elements}
    print('{} has {} Dependant Elements.'.format(level.Name, len(elements)))
    print('It includes {} unique Types'.format(len(unique_types)))
    for typ in unique_types:
        print(typ)

Questions:

Why we use Transaction if we are not Commiting it?

Why we use Transaction if we are not Commiting it?

Discuss the lesson:

P.S. Sometimes this chat might experience connection issues.
Please be patient or join via Discord app so you can get the most out of this community and get access to even more chats.

P.S. Sometimes this chat might experience connection issues.
Please be patient or join via Discord app so you can get the most out of this community and get access to even more chats.

© 2023-2024 EF Learn Revit API

© 2023-2024 EF Learn Revit API

© 2023-2024 EF Learn Revit API