Create a family library document (export Excel with PreviewImage)

For items that don’t generate a preview image. I put placeholder X_____X.

Edit: please paste entire python script and I can explain which bits do which

# coding: utf-8 
import clr
clr.AddReference('ProtoGeometry')
from Autodesk.DesignScript.Geometry import *

clr.AddReference('RevitAPI')
import Autodesk
from Autodesk.Revit.DB import *

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

doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
app = uiapp.Application
uidoc = uiapp.ActiveUIDocument


import System
from System import Environment
from System import Array
from System.Collections.Generic import *

clr.AddReference('System.Drawing')
import System.Drawing
from System.Drawing import *
clr.AddReference('System.Windows.Forms')
from System.Windows.Forms import Clipboard


clr.AddReferenceByName('Microsoft.Office.Interop.Excel, Version=11.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' )
from Microsoft.Office.Interop import Excel
from System.Runtime.InteropServices import Marshal


class ExcelUtils():
    def __init__(self, expSettings, filepath, sizebitmap):
	    self.expSettings = expSettings
	    self.filepath = filepath
	    self.sizebitmap = sizebitmap
	    print self.filepath
	    
	    
    def exportXls(self):
    	
	    ex = Excel.ApplicationClass()
	    ex.Visible = True
	    ex.DisplayAlerts = False
	    workbook = ex.Workbooks.Add()
	    workbook.SaveAs(self.filepath)
	    ws = workbook.Worksheets[1]	
	    nbr_row = len(self.expSettings)
	    nbr_colum = len(self.expSettings[0])
	    #
	    xlrange  = ws.Range[ws.Cells(1, 2), ws.Cells(nbr_row, nbr_colum )]
	    xlrange.EntireRow.RowHeight  = self.sizebitmap.Height
	    ws.Range("a1").EntireColumn.ColumnWidth = self.sizebitmap.Width / 4
	    a = Array.CreateInstance(object, nbr_row, nbr_colum - 1)
	    #
	    for indexR, row in enumerate(self.expSettings):
		    for indexC , value in  enumerate(row):
			    if indexC == 0 and value is not None:
				    Clipboard.SetDataObject(value)
				    rng  = ws.Range[ws.Cells(indexR + 1, 1), ws.Cells(indexR + 1 , 1)]
				    ws.Paste(rng, False)
				    
			    else:
				    a[indexR, indexC - 1] = value
			    
	    #copy Array in range			
	    xlrange.Value2 = a		
	    used_range = ws.UsedRange	
	    for column in used_range.Columns:
		    column.AutoFit()
		    
toList = lambda x : x if hasattr(x, '__iter__') else [x]		
listcat = toList(UnwrapElement(IN[0]))			
#make filter			
lstbipCat = [System.Enum.ToObject(BuiltInCategory, x.Id.IntegerValue) for x in listcat]			
filtercat = ElementMulticategoryFilter(List[BuiltInCategory](lstbipCat))
#collector
fecSymb = FilteredElementCollector(doc).OfClass(FamilySymbol).WherePasses(filtercat).ToElements()

fecsNew = []

for fec in fecSymb:
    if fec.FamilyName == "Model Text":
	    pass
    else:
	    fecsNew.append(fec)

catName = []
sortedsymbs = []


for s in fecsNew:
    fam = s.Family
    cat = fam.FamilyCategory
    catName.append(cat.Name)

sortedsymbs = [x for _,x in sorted(zip(catName, fecsNew))]

exceps = []
outdata = []
imgSize = Size( 100, 100 )

for symb in sortedsymbs:
    try:
	    if not symb.GetPreviewImage(imgSize):
		    bitm = "X---------------------X"
	    else:
		    bitm = symb.GetPreviewImage(imgSize) 
	    famName = symb.Family.Name
	    symbName = Element.Name.GetValue(symb)
	    fam = symb.Family
	    cat = fam.FamilyCategory
	    catName = cat.Name
	    try:
		    famDoc = doc.EditFamily(fam)	
		    if not famDoc.PathName:
			    path = ("Family location not found")
		    else:
			    path = (str(famDoc.PathName))
			    famDoc.Close(False)
	    except:
		    path = ("This is a system family")
    except Exception, e:
	    exceps = str(e)
    outdata.append([bitm, famName, symbName ,catName , path])
    
#define folder to export		
directory = System.Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)

objXls = ExcelUtils(outdata, directory + '\\Family_Container.xls', imgSize)
objXls.exportXls()

OUT = directory

So I know this part determines what the path output is

for symb in sortedsymbs:
try:
	if not symb.GetPreviewImage(imgSize):
		bitm = "X---------------------X"
	else:
		bitm = symb.GetPreviewImage(imgSize) 
	famName = symb.Family.Name
	symbName = Element.Name.GetValue(symb)
	fam = symb.Family
	cat = fam.FamilyCategory
	catName = cat.Name
	try:
		famDoc = doc.EditFamily(fam)	
		if not famDoc.PathName:
			path = ("Family location not found")
		else:
			path = (str(famDoc.PathName))
			famDoc.Close(False)
	except:
		path = ("This is a system family")
except Exception, e:
	exceps = str(e)
outdata.append([bitm, famName, symbName ,catName , path])

I am still not sure why it’s empty though. And apparently only detail items and generic models all have problem generating a preview image.

1 Like

https://revitforum.org/showthread.php/37169-Preview-Image-Generator-(P-I-G-)

I can only think this plug-in could help.

It’s interesting that you pointed out this tool. This is actually what I am currently using as an alternative to this library document. Still not exactly what I wanted but this would help if someone was browsing within the family folder. I have updated all my detail families preview images and they are all optimized. I am still getting no preview image through the script. I was thinking about trying @c.poupin original script with just the detail item category feeding the script, but I figured the imgSize part is the exact same so it wouldn’t yield any different result.

Edit: I decided to update the generic models with the P.I.G. tool and now I am actually getting a preview image. So the tool actually helped to an extend. Maybe the tool is generating preview images for detail items in a way that isn’t working with the script.

1 Like

Ah, so this is why. That sucks. The weird part also is that there is obviously a preview image for detail items (either plan view or whatever active view/sheet was on) and that preview image also shows as a file thumbnail, but it just doesn’t show in the user interface. So for some reason Revit either only recognize something else other than that data, that’s why it’s not showing the actual thumbnail, or Revit just disable thumbnails for all detail items. I wonder if there is a way to communicate to Revit to just use the file thumbnail, instead of asking Revit where they would have usually stored their thumbnails, if that makes sense.

Edit: A dumb way to get around it might be to have a different script, or just edit within the original script, to extract the detail item thumbnails, and do a copy and paste onto the spreadsheet, where the file names matched, maybe this could be achieved within the Microsoft framework and would have nothing to do with Revit, since I have already went through all my detail items and use P.I.G. to update their thumbnails. Updating the thumbnails would probably be part of the workflow for future families anyway.

The Revit API only seems to have their own PreviewImage property but as you say maybe there might be a Python Library that could get an image that way. I think the script could be altered to do that if such a python module could be found.

Right. I am not at all an expert with Python, and I am not too familiar with Revit API. I will ask people who knows Python and see if they can think of any method that can achieve getting preview images from the detail item families (file thumbnails) and paste it in the spreadsheet where names matched. Basically bypassing Revit. Because it seems like it’s not quite possible to get preview images from Revit anymore. Hopefully this will go somewhere. In the meantime, if anyone has any idea or any suggestions please drop a comment below, it’ll be greatly appreciated.

1 Like