Rhino Inside Transactions

Hey!

I’m trying to create a custom GH component in Python that performs a EditFamily operation on a family loaded in the open document. I am struggling to understand how Transactions work under the Rhino Inside Revit environment.

It looks like Rhino Inside has created a transaction of its own and its conflicting with my own transaction. I get the following error:
1. Solution exception:The document is currently modifiable! Close the transaction before calling EditFamily.

I’ve also tried to perform this operation without creating a transaction and so I obtain:
1. Solution exception:Unable to close all open transaction phases! or
1. Solution exception:Saving is not allowed in the current application mode.

I would really appreciate if someone could she me some light on this.
Thank you in advance,

Here is the sequence code of what the GH component is meant to do:

"""Provides a scripting component.
    Inputs:
        x: The Autodesk Revit Family
    Output:
        a: The Byte Array Output"""

__author__ = "Rita"
__version__ = "2023.08.07"

ghenv.Component.Name = "Family To Byte Array"
ghenv.Component.NickName = "FTBA"
ghenv.Component.Description = "This is a custom component that converts a family to a byte array."

import rhinoscriptsyntax as rs
import clr
import System
from System.IO import File, MemoryStream

# Revit API references
clr.AddReference('RevitAPI')
clr.AddReference('RevitAPIUI')
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import UIApplication
from RhinoInside.Revit import Revit

# Get the active Revit application
uiapp = UIApplication(Revit.ActiveDBApplication)
doc = uiapp.ActiveUIDocument.Document

def family_to_byte_array(family):
    # Define a temporary file path
    tempFilePath = System.IO.Path.GetTempFileName() + ".rfa"
    
    familyDoc = doc.EditFamily(family)
    
    # Save the family document to a temporary file
    saveOptions = SaveAsOptions()
    saveOptions.OverwriteExistingFile = True
    familyDoc.SaveAs(tempFilePath, saveOptions)
    familyDoc.Close(False)
    
    # Read the saved family into a memory stream
    with File.OpenRead(tempFilePath) as fileStream:
        memStream = MemoryStream()
        fileStream.CopyTo(memStream)
        byteArray = memStream.ToArray()
    
    # Cleanup: Delete the temporary file
    File.Delete(tempFilePath)
    
    return byteArray

# Assuming 'x' is your family
a = family_to_byte_array(x)

Hi Rita,

Please see similar issues discussed here…

Hi @Rita_Aguiar,

It looks like you have an open transaction while you are trying to save.
To be sure all your other Python components close the transactions they open, even when they fail, use the with python keyword.

# create and start the transaction
with DB.Transaction(doc, '<give a descriptive name to your transaction>') as t:
    t.Start()
    # change Revit document here
    # commit the changes after all changes has been made
    t.Commit()

from Rhino.Inside®.Revit - Handling Transactions

Your code runs well here when no transactions are open.

May I ask why do you need to convert a Family to a byte array?
If you are trying to obtain a hash out of it keep in mind that each time you save a Family to file the result is different.
Mainly because each time you save it Revit assigns a new CreationGUID to your new Family.

Hi @kike

I stumbled upon that article some time ago and made an attempt to perform this operation inside a DB Transaction but I still wasn’t able to get this working. I still get the error that 1. Solution exception:The document is currently modifiable! Close the transaction before calling EditFamily.
I am converting a Family to a Byte Array because I am sending it to a database in the cloud in a POST Request, using the Swifltet GH package.

"""Provides a scripting component.
    Inputs:
        x: The Autodesk Revit Family
    Output:
        a: The Byte Array Output"""

__author__ = "Rita"
__version__ = "2023.08.07"

ghenv.Component.Name = "Family To Byte Array"
ghenv.Component.NickName = "FTBA"
ghenv.Component.Description = "This is a custom component that converts a family to a byte array."

import rhinoscriptsyntax as rs
import clr
import System
from System.IO import File, MemoryStream, Path

# Revit API references
clr.AddReference('RevitAPI')
clr.AddReference('RevitAPIUI')
from Autodesk.Revit import DB
from Autodesk.Revit.DB import *
from Autodesk.Revit.UI import UIApplication
from RhinoInside.Revit import Revit

# Get the active Revit application
uiapp = UIApplication(Revit.ActiveDBApplication)
doc = uiapp.ActiveUIDocument.Document

def family_to_byte_array(family):
    # Define a temporary file path
    tempFilePath = Path.GetTempFileName() + ".rfa"
    
    familyDoc = doc.EditFamily(family)
    
    # Save the family document to a temporary file
    saveOptions = SaveAsOptions()
    saveOptions.OverwriteExistingFile = True
    familyDoc.SaveAs(tempFilePath, saveOptions)
    familyDoc.Close(False)
    
    # Read the saved family into a memory stream
    with File.OpenRead(tempFilePath) as fileStream:
        memStream = MemoryStream()
        fileStream.CopyTo(memStream)
        byteArray = memStream.ToArray()
    
    # Cleanup: Delete the temporary file
    File.Delete(tempFilePath)
    
    return byteArray

# create and start the transaction
with DB.Transaction(doc, 'Edit Family') as t:
    t.Start()
    # change Revit document here
    # commit the changes after all changes has been made
    # Assuming 'x' is your family
    a = family_to_byte_array(x)
    t.Commit()

Save operation should be done without any Transaction open, but maybe you have other Python components on your Grasshopper canvas that use transactions and are not using the with keyword.

Since v 1.13 ‘Save Component Family’ has a ‘Path’ output and if you remove the ‘Path’ input it generate a file on a temporary folder for you.
So you can use it like this.

RiR-FamilyToBytes.gh (12.2 KB)

1 Like

Thanks for the suggestion @kike !
I am using now the Save Family Component and saving to a temporary path by not specifying this parameter. Since Swiftlet package has a Path to Byte Array component I decided to use that instead of needing a custom script.

1 Like