AttributeError: What do I do? :(

Complete novice here trying to make my first plugin for pyRevit. I have little to no coding knowlege so I used chatGPT to write this code but I can’t seem to get past the following error:
AttributeError: ‘NoneType’ object has no attribute ‘ActiveUIDocument’

import clr
import math
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import TaskDialog

clr.AddReference("RevitServices")
from RevitServices.Persistence import DocumentManager

clr.AddReference("System")
from System.Collections.Generic import List

# Get the current Revit document and UI document
def get_revit_context():
    try:
        doc = DocumentManager.Instance.CurrentDBDocument
        uidoc = DocumentManager.Instance.CurrentUIApplication.ActiveUIDocument
        
        if not doc:
            TaskDialog.Show("Error", "Revit DB Document is None. Ensure that the script is running in Revit.")
            raise Exception("Revit DB Document is None")
        
        if not uidoc:
            TaskDialog.Show("Error", "Revit UI Document is None. Ensure that the script is running in Revit.")
            raise Exception("Revit UI Document is None")
        
        return doc, uidoc
    except Exception as e:
        TaskDialog.Show("Error", str(e))
        raise

doc, uidoc = get_revit_context()

def set_leader_angle(leader, angle_radians):
    """
    Set the angle of a leader based on the angle from the y-axis (in radians).
    """
    # Get the start and end points of the leader
    leader_elbow = leader.Elbow
    leader_head = leader.Head
    
    # Calculate new elbow point based on the angle provided
    dy = leader_elbow.Y - leader_head.Y
    dx = dy * math.tan(angle_radians)
    
    new_elbow = XYZ(leader_head.X + dx, leader_elbow.Y, leader_elbow.Z)
    
    # Set new elbow location
    leader.Elbow = new_elbow

def get_user_angle_input():
    """
    Prompts the user to enter an angle (in degrees) for the leader lines.
    """
    input_dialog = TaskDialog("Input Leader Angle")
    input_dialog.MainInstruction = "Enter the angle in degrees (bearing from the y-axis):"
    input_dialog.MainContent = "You can set the leader angle to be applied to the selected tags."
    
    angle_input = input("Angle (in degrees): ")
    
    try:
        return float(angle_input)
    except ValueError:
        TaskDialog.Show("Error", "Invalid angle input. Please enter a valid number.")
        return None

def select_tags():
    """
    Prompt the user to select multiple tags in Revit.
    """
    uidoc.PromptForSelectionStatusText = "Select tag(s) to set leader angle. Finish selection with Enter/Spacebar"
    selected_tags = uidoc.Selection.PickObjects(ObjectType.Element, "Select the tags to modify.")
    
    return [doc.GetElement(tag.ElementId) for tag in selected_tags]

def filter_tags_with_leaders(tags):
    """
    Filters out tags that do not have leaders and informs the user.
    """
    tags_with_leaders = []
    tags_without_leaders = []
    
    for tag in tags:
        if tag.HasLeader and tag.Leader.HasElbow:
            tags_with_leaders.append(tag)
        else:
            tags_without_leaders.append(tag)
    
    if tags_without_leaders:
        TaskDialog.Show("Warning", "{} tags without leaders were ignored.".format(len(tags_without_leaders)))
    
    return tags_with_leaders

def run_plugin():
    """
    Main function to run the plugin.
    """
    # Let the user choose between inputting an angle or selecting a base tag
    base_tag_selection = TaskDialog.Show("Choose Mode", "Do you want to select a base tag or input an angle?",
                                         TaskDialogCommonButtons.Yes | TaskDialogCommonButtons.No)
    
    # Option 1: User selects a base tag and copies its leader angle to other tags
    if base_tag_selection == TaskDialogResult.Yes:
        uidoc.PromptForSelectionStatusText = "Select a single base tag"
        base_tag = uidoc.Selection.PickObject(ObjectType.Element, "Pick the base tag with the desired leader angle")
        base_leader = doc.GetElement(base_tag.ElementId).Leader
        
        # Ensure the base tag has a leader
        if not base_leader.HasElbow:
            TaskDialog.Show("Error", "The selected base tag does not have a leader.")
            return
        
        base_leader_angle = math.atan2(base_leader.Elbow.X - base_leader.Head.X,
                                       base_leader.Elbow.Y - base_leader.Head.Y)
        
        # Prompt user to select other tags to match the leader angle
        uidoc.PromptForSelectionStatusText = "Pick tag(s) to match leader angle"
        selected_tags = select_tags()
        
        # Filter out tags without leaders
        selected_tags = filter_tags_with_leaders(selected_tags)
        
        # Apply base leader angle to all selected tags
        with Transaction(doc, "Set Leader Angle") as t:
            t.Start()
            for tag in selected_tags:
                leader = tag.Leader
                set_leader_angle(leader, base_leader_angle)
            t.Commit()
    
    # Option 2: User inputs an angle manually
    else:
        angle_degrees = get_user_angle_input()
        if angle_degrees is None:
            return
        
        # Convert degrees to radians
        angle_radians = math.radians(angle_degrees)
        
        # Prompt user to select tags to set the angle
        selected_tags = select_tags()
        
        # Filter out tags without leaders
        selected_tags = filter_tags_with_leaders(selected_tags)
        
        # Apply the specified angle to all selected tags
        with Transaction(doc, "Set Leader Angle") as t:
            t.Start()
            for tag in selected_tags:
                leader = tag.Leader
                set_leader_angle(leader, angle_radians)
            t.Commit()
    
    TaskDialog.Show("Success", "Leader angles have been updated.")

# Run the plugin
if __name__ == "__main__":
    run_plugin()

I am trying to create a plugin that will match all the leader lines of a tag to the same angle.
I have no idea what I am doing so any help would be appreciated.

Welcome,

We all started somewhere, no worry

start by removing this code :point_up: no use.

declare doc and uidoc like so

from pyrevit import HOST_APP

doc = HOST_APP.doc
uidoc = HOST_APP.uidoc

this method does not exist: PromptForSelectionStatusText
remove:

and this:
uidoc.PromptForSelectionStatusText = "Select a single base tag"

and this:
uidoc.PromptForSelectionStatusText = "Pick tag(s) to match leader angle"

and you should get closer to the result you are trying to achieve.

You should start by reading

and maybe, to get you started much faster, join @ErikFrits course Learn Revit API - The Most Comprehensive Course for Beginners (I am not affiliated, we know each other, and I think its work is great)
or at list go through its free videos https://www.youtube.com/@ErikFrits

And do not really too much on chatGPT to code from scratch, you won’t make friends :slight_smile: on forum and won’t get the expected result if you don’t know a bit of the ins and outs of the Revit API or pyRevit

Hi, and welcome! :slight_smile:
As someone that also started from “scratch” around April this year, I suppose I know where you are. :stuck_out_tongue:

I recommend the same as @Jean-Marc, the Youtube videos and course from @ErikFrits is great!

For ChatGPT I recommend to use the custom GPT named “PyRevit Helper”, as it has much better context for pyRevit code than standard ChatGPT 4o.
The “only” mistake it usually does for basic pyRevit code is f-strings (which you can tell it to fix and it will remember in that chat for a while).

Hi Jesse,
Welcome on board of pyRevit Ship :ship:!

Chat GPT is amazing tool and can help beginners get started x10 times faster, but you still need to pickup Revit API basics so at least you know what to ask and how to fix incomplete code. John also has a good point about pyRevit Helper.
Personally, I usually use raw GPT (I’m lazy to make extra click), but I also know what to ask and provide the needed context myself when it struggles.

As anyone here who used AI, we all know that AI can spit out completely made up classes and methods because of lack of resources out there for Revit API.

As Jean-Marc and John has suggested, I do have a course that will teach everything step by step. But also I have a lot of free resources that will help you get started and provide a lot of help.

:blue_book:E-Book: Beginner’s Guide to Revit API (Get a Roadmap and Learn Revit API Basics)
:gift:EF-pyRevit StarterKit (Create Extension in 2 min and get all necessary resources and code sampels)
:cinema:Module 01 from my Course is Free for All (I’m still releasing videos 1-2 a week)

Happy Coding and may bugs be few!

3 Likes