Have a question about LearnRevitAPI?

Let me know so I can help you make the right decision about this investment.

Ask Question

How to be efficient with your pyRevit code?

"Good programmers code, but great programmers reuse code.". Let me show you how you can be more efficient with your code by using pyRevit lib to reuse code snippets.

Summary

Reuse Your Code as early as possible

Whenever you catch yourself copy-pasting a large code snippets, consider making it a function and adding it to your library of reusable snippets. That's a really good habit for any programmer and you will benefit the most in the long run.

There are plenty of reason for that:

  • Better organized code

  • Cleaner code in scripts

  • Faster to make changes

  • Quicker to develop tools

It's easier to update function in 1 place than search for all placed


Let's look at how to create your custom library with pyRevit.

pyRevit Folder Structure

Let's have a look at pyRevit's folder structure. We've covered the basics already, but there are also a few utility folders like:

  • hooks

  • lib 👈

And lib is what we need to reuse our code!

lib is the library where you can place your code and pyRevit will look there by default. So it makes it very easy to reuse your code.

Setting up your pyRevit lib

Create lib folder in your extension folder.

Next we need to organize lib folder like it's a package, so python engine can recognize files as modules and import whatever is inside. And it's not complicated.

In a nutshell, you just need to create empty __init__.py files in lib folder and every nested folder inside of it. And then python will recognize your lib folder as a package.

💡You can name your folders and files anything you want here.

For example, if I would create:

LRA.extension/lib/Snippets/Erik/test.py
And I would put __init__.py in every folder.

Then, I would import whatever I want from this test file by writing:

from Snippets.Erik.test import *

I hope you start to understand the logic of how to import from lib folder.

StarterKit lib Example

Let's have a look at the existing example from EF-pyRevit StarterKit that we used to create your own extension in Lesson 01.02.

Have a look at: lib/Snippets/_customprint.py

# -*- coding: utf-8 -*-
from pyrevit import script
output = script.get_output()

def kit_button_clicked(btn_name):
    # 👀 Print Message
    output.print_md('## ✅️ {btn_name} was Clicked ✨'.format(btn_name=btn_name))  # <- Print MarkDown Heading 2
    output.print_md('---')
    output.print_md('⌨️ Hold **ALT + CLICK** to open the source code of this button. ')  # <- Print MarkDown Heading 2
    output.print_md('*You can Duplicate, or use this placeholder for your own script.*')

Inside you will see a very simple snippet with a function that makes a MarkDown print statement for all my placeholders in the StarterKit.

This function only takes the button name so I can make the same print statement and only change the button name from where it was clicked.

💡Once you write a function in lib folder, it can be reused in all your tools by importing it with the name of the file and folders where it's located.

How to Reuse lib Code?

We can also have a look at any placeholder button in the StarterKit and you will notice this snippet on the bottom of each script.

You will start to see similarity between the import statement and the folder structure of the lib.

Then once you import the function, you would use it as usual.

Create New Reusable Function

Let's create a new function together and test it.

Firstly, we will create a new python file in the lib folder.

  • lib/Snippets/_selection.py

💡Notice that I use underscore_ in the beginning of my file names. I do that because in the past I named my files similar to the variables I used in the code and that cause unexpected issues.

For example, it's easy to imagine a code where you would make imports from selection module and you would also name your variable selection

Since then, I started adding _ to mark that it's my package and I will never have variable like that.


Now inside the _selection.py we will write the following snippet.

# -*- coding: utf-8 -*-


# Imports
#==================================================
from Autodesk.Revit.DB import *

# Variables
#==================================================
app    = __revit__.Application
uidoc  = __revit__.ActiveUIDocument
doc    = __revit__.ActiveUIDocument.Document #type:Document


# Reusable Snippets


def get_selected_elements(filter_types=None):
    """Get Selected Elements in Revit UI.
    You can provide a list of types for filter_types parameter (optionally)

    e.g.
    sel_walls = get_selected_elements([Wall])"""
    print('Using Function from _selection.py')
    selected_element_ids = uidoc.Selection.GetElementIds()
    selected_elements    = [doc.GetElement(e_id) for e_id in selected_element_ids]

    # Filter Selection (Optionally)
    if filter_types:
        return [el for el in selected_elements if type(el) in filter_types]
    return selected_elements

This is a simple function that would get the selected elements in our Revit UI.

It's not complicated, and you can see that I've spent more effort on writing the doc-string than the code.

pyCharm Autocomplete

Next, when we want to reuse our functions from lib folder, it's good if autocomplete also works. It will help you avoid issues.

There are 2 ways you can make it work.

  1. You can add absolute path to your lib folder in the Interpreter References to have autocomplete and access to doc strings.

  1. Mark lib folder as Source Root in pyCharm

This is quick and easy way to do this.

Right Click lib-> Mark Directory as -> Source Root

💡When you setup your autocomplete in IDE it doesn't always work right away.

  • Sometimes you need to wait a little

  • Sometimes you need to restart IDE

  • Sometimes you even ened to restart PC

I know it's strange, but sometimes bugs happen
(maybe switch to dark theme, since light attracts bugs tu-du-tsss)

Import our new function

Lastly, we can import the function we created and try to use it in any of our pyRevit tools.

Here is a code sample:

from Snippets._selection import get_selected_elements


sel_el    = get_selected_elements()
sel_walls = get_selected_elements([Wall, Floor])

print(sel_el)
print(sel_walls)

lib Updates

A very important point!
In the past we had to reload pyRevit every time we made any changes in our lib.

I've literally spent hours trying to fix an issue with my code in lib and nothing worked, until I realized that every fix I tried wasn't even executed because pyRevit always used the old code…

Trust me it was horrible trying to debug something where your new code isn't executed.


Now I don't think it's the case anymore. At least it seems like it's always updated. But keep an eye on it, and maybe you would need to update once in a while too!

HomeWork

Remember:
💪 Good programmers code, but Great programmers reuse code!

✅ Create lib folder
✅ Write simple functions in lib folder (print something)
✅ Import your custom functions and test them in the script

⌨️ Happy Coding!

Questions:

What should I write in __init__.py file?

What should I write in __init__.py file?

What should I write in __init__.py file?

Should I always add uidoc as argument in lib functions?

Should I always add uidoc as argument in lib functions?

Should I always add uidoc as argument in lib functions?

Can I name folder 'Snippets' differently?

Can I name folder 'Snippets' differently?

Can I name folder 'Snippets' differently?