Creating a Family Browser Toolbar

I’m very new to coding and I’ve been trying to develop a toolbar for my company that allows the user to place family instances using a pushbutton on a toolbar rather than searching through the project browser for the right family. I created an entire custom family library and now am trying to spoil us with a custom toolbar to easily find and place the family. I can get it to at least attempt to place the family but get an error that I can’t figure out as well “Exception: Family cannot be placed as hosted on an input face reference, because its FamilyPlacementType is not ViewBased” Im not sure exactly where I’m going wrong. Im trying to setup where it essentially runs the “Create Similar” command and automatically selects the corresponding family in the Project Browser.
#This is my Script following Erik Fritz#

from Autodesk.Revit.DB import *
#VARIABLES
doc = revit.ActiveUIDocument.Document
uidoc = revit.ActiveUIDocument
app = revit.Application
title = ‘Place New Instance’
active_view = doc.ActiveView
active_level = doc.ActiveView.GenLevel
import clr
clr.AddReference(‘System’)
from System.Collections.Generic import List

#pyrevit
from pyrevit import forms,revit

#MAIN
#=====================
#EXTRA FUNCTION
def get_type_by_name(type_name):
#CREATE RULE

param_type = ElementId(BuiltInParameter.ALL_MODEL_TYPE_NAME)
f_param = ParameterValueProvider(param_type)
evaluator = FilterStringEquals()
f_rule = FilterStringRule(f_param, evaluator, type_name, True)

#CREATE FILTER

filter_type_name = ElementParameterFilter(f_rule)
# GET ELEMENTS

return FilteredElementCollector(doc).WherePasses(filter_type_name).WhereElementIsElementType().FirstElement()

symbol = get_type_by_name(‘LB_Conduit_Body’)

#PLACE FAMILY

with Transaction(doc,title) as t:
t.Start()
# CHANGES HERE

element  = doc.Create.NewFamilyInstance(XYZ(0,0,0) , symbol, active_view)

t.Commit()

Your issue is that different families have different ways of being placed. Some are good with just a point and rotation. Others need a couple of points for a line-based family. Some need a host. So, I don’t think you want to do all the upfront decision makings. You just want to set the family you are using and then let Revit do its thing.

This might help:
The Building Coder: Launching a Revit Command (typepad.com)

1 Like

Have a look at a ready made solution with .content bundles

Is there a way it can work without the _content part at the end of the file name? I had also done it with the _content and it wasnt able to load so not sure where im wrong. Im also doing this in Revit 2020

Not that I know of without interfering with what is in place.
But probably not too bad of a job. Families could be located in 2 subfolders instead without renaming.

Anyway, here is a typical folder content. The folder needs to be named *.content

I finally got it to work with the _content. I didn’t realize the family had to be loaded into the project already with the _content name. The only thing that’s holding me back is having that name in the project as we export out model in schedules for material req’s and I cant have that _content visible in the schedule. As of now though the only way it will work is with the _content being present in the file name?

As of now, by default yes.
You did well filing an issue on GitHub, it makes sense.

There is always the possibility to hardcore the loading and placing in a script file

I did something similar for my company to place families from a toolbar as searching for them was a pain and we needed to address the issue of everyone not loading the most up to date family from the correct location.

A couple of things to note:

  1. If your company has alot of families that get updated periodically, best practice would be to load the family from a central server location that every user has access to if the family is not in the project.
  2. Create a family placement utility so the script for the button only has the name of the family and directory path to load it from if it is not already in the project. This will make editing script and creating them simpler if you need to create 50+ buttons for each family in the toolbar.
  3. Host the .tab folder on github so you can make updates for all users when there is an issue or you need to add/remove a button for a family.

Here is an example of my place_family function in my lib folder:

def place_family(family_path, name, category):
    doc = revit.doc
    # Checking if family is loaded
    family = is_family_loaded(category,name)
    
    # If family is not loaded
    if not family:
        forms.alert("Family Not Loaded.",
                    sub_msg="Loading Family from Standards Directory",
                    exitscript=False)
        family = load_family(family_path,name,category) 

    # Getting all types in the family and create in dictionary
    family_dict = {}
    for id in family.GetFamilySymbolIds():
        family_symbol = doc.GetElement(id)
        family_dict[family_symbol.LookupParameter("Type Name").AsString()] = family_symbol
    sorted_family_dict = OrderedDict(sorted(family_dict.items()))

    # Using the first item or asking for type if more than one
    selected_fam_type = (sorted_family_dict.values()[0] if len(sorted_family_dict.keys()) == 1
        else sorted_family_dict[forms.CommandSwitchWindow.show(sorted_family_dict,message="Select a Family Type to Use")])

    if selected_fam_type:
        try:
            revit.uidoc.PostRequestForElementTypePlacement(selected_fam_type)
        except Exception as e:
            if str(e) == "The user aborted the pick operation.":
                stri = e

Here is the main script file (15 lines) for the actual pushbutton:

from pyrevit import DB
from Family_Placement import place_family
from DB_Helper import user_cmd_info
if __name__ == '__main__':
        family_name = "Data_Device"
        family_path = r"PATH/TO/FAMILYDIRECTORY/Family.rfa"
        category = DB.BuiltInCategory.OST_CommunicationDevices
        status = 0
        error_message = None
        try:
            place_family(family_path, family_name, category)
        except Exception as e:
            status = -1
            error_message = str(e)
        user_cmd_info(status, error_message)