View Overrides base on linked Cad layers

I am trying to write a script that applies overrides to a drafting view based on a list of layers.

I have started a script but it doesn’t work. I am hopping someone could point me in the right direction.

from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *
from pyrevit import forms, revit, script

# Get the current document
doc = revit.doc

# Define a dictionary of category names and their corresponding override settings
category_overrides = {
    "s-hatch8": Color(192, 192, 192),  # LIGHT GREY
    "s-rein": Color(128, 128, 128),    # DARK GREY
    "e-hid2": Color(192, 192, 192),    # LIGHT GREY
    # I plan adding more categories and their corresponding colors when these work
}

# Here I collect all drafting views in the document
drafting_views = FilteredElementCollector(doc) \
    .OfClass(ViewDrafting) \
    .ToElements()

# Start a transaction
with Transaction(doc, "Modify Graphic Overrides in Drafting Views") as t:
    t.Start()
    
    # Iterate through drafting views
    for drafting_view in drafting_views:
        print("Processing drafting view:", drafting_view.Name)
        
        # Get imported CAD files
        imported_instances = FilteredElementCollector(doc) \
            .OfClass(ImportInstance) \
            .ToElements()
        
        # Iterate through the imported instances
        for import_instance in imported_instances:
            # Get the category of the import instance
            category = import_instance.Category
            if category and category.Name.lower() in category_overrides:
                # Create an override settings instance
                override_settings = OverrideGraphicSettings()
                # Set the projection line color based on the category match
                override_settings.SetProjectionLineColor(category_overrides[category.Name.lower()])
                
                # Apply the graphic override settings to the current drafting view
                drafting_view.SetCategoryOverrides(category.Id, override_settings)
                print("Applied override for %s in %s" % (category.Name, drafting_view.Name))
            #else:
                #print("Skipping category for import instance:", import_instance)  # For debugging

    t.Commit()

forms.alert("Graphic Overrides updated successfully for drafting views!")

Hi @clntnco,
“Doesn’t work” is not a great description. Are there any errors?
Does it do something wrong or absolutely nothing?

I invite you to learn how to ask questions the smart way

That being said, I see you’re looping through all the imported_instances for every view, maybe you need to use the FilteredElementCollector only on the specific view?

1 Like

Why are you looping through views?

a linked file (cad) is managed differently namely at object styles.

I use this approach:

  • select a link file
  • override its layer color from a file (.txt)
  • settings are applicable for each view.
selections = revit.get_selection()
for selection in selections:
                
                layers = sorted(selection.Category.SubCategories, key=lambda x: x.Name)
                
                for layer in layers:
                    
                    lname = layer.Name
                    
                    if lname in data:                
                        #print lname                    
                        cad_color = data[lname]
                        
                        if "," in cad_color:
                            cad_color_list = cad_color.split(",")
                            
                            with revit.Transaction("cadlayer_override"):                            
                                layer.LineColor = revit.DB.Color(int(cad_color_list[0]), int(cad_color_list[1]), int(cad_color_list[2]))          
                                output_success.append("{} \t {},{},{}".format(lname, layer.LineColor.Red, layer.LineColor.Green, layer.LineColor.Blue))

This will set you in the right direction I think.

…and note - Users are very bad at distinguishing between linked and import and Import current view only and not importing current view only.

That may give you some additional headache if your users are as skilled as mine.

Thank you for your replies. I guess “it doesn’t work” is a little vague. But, so is the result. When I run the code it doesn’t give me a error, but it doesn’t do anything either.

Thank you so much for your help. I am looping through the views to save time and my sanity.

So the override isn’t controlled by “Visibility/Graphic Overrides->Imported Categories” at the view level ?

I can be controlled at three different levels.

  • At the Object Styles level. This affects all view in th project and if you are indeed iterating through all views and doing the same thing, then the setting is best applied here. Set it. Forget it. Additional imports will respect the preset object styles. (You could in fact set this in your template and then not need a program at all if it is always the same layers and settings.)

  • At the view level by category. This is if each view iis a little different.

  • At the element level. This would be used if elements of the same category need different graphics. View filters could possibly be used here.

Theme and variation is the name of the game.

1 Like

Thank you for the detailed explanation. The projects I work on can have up to 100 details attached and placed in their own drafting view. I would love to use the insert presets, except they only control line thickness not color, making shading impossible. I didn’t know you could change it at the object level and affect each view that a linked file is placed.

Again thank you so much for your help

I would suggest for the long term; you look at converting these over to Revit detail elements and views. It is much faster to copy sheets and views over from a template or other project than it is to link in AutoCAD drawings. In the end you would save considerable time.

That isn’t an option because we still do projects in AutoCAD and use those same details. Also having to worry about how each new version of Revit will convert the (insert object here) i.e. text, families and such. Keeping the details in cad lets us avoid that problem.

how would you do this without having to pick each link?

By selecting all cad links instead.

And/or if always a specific file name is used: filter by that name.

Thanks for all the help and I have the code almost done, but I am getting error:

“Exception: Attempting to create an ExternalEvent outside of a standard API execution”

from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import *
from pyrevit import forms, revit

with revit.Transaction("Convert Layers"):
    uidoc = __revit__.ActiveUIDocument
    doc = __revit__.ActiveUIDocument.Document

    data = {
        "S-HATCH8": "255,0,0",  # Red
        "S-HATCH6": "0,255,0",  # Green
        "S-HATCH14": "0,0,255"   # Blue
    }

    # Get all linked CAD files
    import_instances = FilteredElementCollector(doc) \
        .OfClass(ImportInstance) \
        .ToElements()

    # Loop through each ImportInstance
    for instance in import_instances:
        layers = sorted(instance.Category.SubCategories, key=lambda x: x.Name)
        
        for layer in layers:
            
            lname = layer.Name
            
            if lname in data:
                # Get the layer name
                cad_color = data[lname]
                
                print(cad_color)
                
                if "," in cad_color:
                    cad_color_list = cad_color.split(",")
                            
                    with revit.Transaction("cadlayer_override"):
                        layer.LineColor = revit.DB.Color(int(cad_color_list[0]), int(cad_color_list[1]), int(cad_color_list[2]))
                        output_success.append("{} \t {},{},{}".format(lname, layer.LineColor.Red, layer.LineColor.Green, layer.LineColor.Blue))

    else:
        print("No ImportInstances found.")

At which line you get that exception? You should always give us the full context of the errors to have as much info as possible to help you.

That being said, I believe the transaction at the start is useless, and it might be what’s causing the error.

Other unsolicited advice:

  • import as little modules as needed, here the revit api can be skipped by using pyrevit.revit.DB for FilteredElementCollector
  • the “else” of the “for” loop doesn’t do what you think :wink: it triggers if the loop completed without an early exit with “break”. I know, this can feel not intuitive, and I never use it because of that.
  • If you invert the if condition and jump to the next loop, you can avoid to indent the code too much
from pyrevit import revit, DB

def main():
    uidoc = __revit__.ActiveUIDocument
    doc = __revit__.ActiveUIDocument.Document

    data = {
        "S-HATCH8": "255,0,0",  # Red
        "S-HATCH6": "0,255,0",  # Green
        "S-HATCH14": "0,0,255"   # Blue
    }

    # Get all linked CAD files
    import_instances = DB.FilteredElementCollector(doc) \
        .OfClass(ImportInstance) \
        .ToElements()
    if not import_instances:
        print("No ImportInstances found.")
        return

    # Loop through each ImportInstance
    for instance in import_instances:
        layers = sorted(instance.Category.SubCategories, key=lambda x: x.Name)
        for layer in layers:         
            lname = layer.Name         
            if lname not in data:
                continue
            # Get the layer color
            cad_color = data[lname]
            print(cad_color)
            if "," not in cad_color:
                continue
            cad_color_list = cad_color.split(",")
                            
            with revit.Transaction("cadlayer_override"):
                layer.LineColor = DB.Color(int(cad_color_list[0]), int(cad_color_list[1]), int(cad_color_list[2]))
                print("{} \t {},{},{}".format(lname, layer.LineColor.Red, layer.LineColor.Green, layer.LineColor.Blue))

main()
1 Like