Read and set Revit parameters with Python (with a Transaction)
Parameters are the data of your model. How to read them, how to fill them in bulk, and the pitfalls that make Set do nothing or raise an error.
Short answer: get a parameter with element.get_Parameter(BuiltInParameter...) (or LookupParameter("name") for your own parameters), read it with AsString(), AsDouble(), AsInteger() or AsValueString(), and change it with Set(value). Changing is only allowed inside a Transaction; in pyRevit you write with revit.Transaction("name"):.
Getting a parameter
There are two ways:
get_Parameter(BuiltInParameter.X)for parameters Revit itself has, such as length, diameter, mark, comments or system name. The names are in theBuiltInParameterenum, for exampleCURVE_ELEM_LENGTH,RBS_PIPE_DIAMETER_PARAM,ALL_MODEL_MARKandALL_MODEL_INSTANCE_COMMENTS.LookupParameter("name")for parameters you added yourselves: project parameters and shared parameters.
Not sure what a parameter is called in the API? Select the element with the free RevitLookup add-in and look at its parameters.
Why LookupParameter breaks in a localised Revit
LookupParameter searches by the name you see in Revit. That depends on Revit's language: what is called "Length" in an English Revit is "Lengte" in a Dutch one. A script with LookupParameter("Length") then works for one colleague and not for another. So for built-in parameters always use get_Parameter with a BuiltInParameter: that name is the same in every language.
Reading a value
A parameter has a storage type (StorageType): text, decimal number, whole number or a reference to another element. The read function follows from it:
from Autodesk.Revit.DB import *
doc = __revit__.ActiveUIDocument.Document
pipe = (FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_PipeCurves)
.WhereElementIsNotElementType()
.FirstElement())
diameter = pipe.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM)
print(diameter.StorageType) # Double
print(diameter.AsDouble()) # in feet
print(diameter.AsValueString()) # as Revit displays it
system = pipe.get_Parameter(BuiltInParameter.RBS_SYSTEM_NAME_PARAM)
print(system.AsString())
print(pipe.get_Parameter(BuiltInParameter.ELEM_TYPE_PARAM).AsValueString())Double
0.13123359580052493
40 mm
CV Aanvoer
Staal gelastAsDouble()returns the number in Revit's internal unit: feet. 0.131 feet is 40 mm.AsValueString()returns the text as Revit shows it, with the project units. Handy for a list, not for calculations.AsString()is for text parameters,AsInteger()for whole numbers and yes/no,AsElementId()for references.
Changing a value: always in a Transaction
Anything that changes the model must happen inside a transaction. With pyRevit's helper module that is one line. This script puts a comment on every pipe and counts how often it worked:
from pyrevit import revit, DB
pipes = (DB.FilteredElementCollector(revit.doc)
.OfCategory(DB.BuiltInCategory.OST_PipeCurves)
.WhereElementIsNotElementType()
.ToElements())
changed = 0
with revit.Transaction("Fill in comment"):
for pipe in pipes:
p = pipe.get_Parameter(DB.BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS)
if p and not p.IsReadOnly:
if p.Set("checked"):
changed += 1
print("Comment set on {} pipes".format(changed))Comment set on 45 pipesIn Revit the whole with block is one step you can undo with Ctrl+Z. If something goes wrong halfway, pyRevit rolls everything back.
Without pyRevit's helper: a plain Transaction
Outside pyRevit, or if you prefer to handle it yourself, use the Transaction class from the Revit API. Do not forget to roll back on an error:
from Autodesk.Revit.DB import *
doc = __revit__.ActiveUIDocument.Document
pipe = (FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_PipeCurves)
.WhereElementIsNotElementType()
.FirstElement())
p = pipe.get_Parameter(BuiltInParameter.ALL_MODEL_INSTANCE_COMMENTS)
t = Transaction(doc, "Fill in comment")
t.Start()
try:
p.Set("checked")
t.Commit()
except Exception:
t.RollBack()
raise
print(t.GetStatus())CommittedPitfalls
Set returns False instead of raising an error
Pass a value of the wrong type and Set does nothing and returns False. A classic: a whole number on a parameter that stores decimals.
from Autodesk.Revit.DB import *
doc = __revit__.ActiveUIDocument.Document
pipe = (FilteredElementCollector(doc)
.OfCategory(BuiltInCategory.OST_PipeCurves)
.WhereElementIsNotElementType()
.FirstElement())
d = pipe.get_Parameter(BuiltInParameter.RBS_PIPE_DIAMETER_PARAM)
t = Transaction(doc, "Diameter")
t.Start()
print(d.Set(5)) # whole number on a Double parameter
new = UnitUtils.ConvertToInternalUnits(50, UnitTypeId.Millimeters)
print(d.Set(new)) # Double: in feet
print(d.AsValueString())
t.RollBack()False
True
50 mmSo check the result of Set, as in the comments example above.
Read-only parameters
Some parameters are calculated by Revit itself, such as the system name of a pipe. IsReadOnly is then True, and Set raises an error: The parameter is read-only. So check IsReadOnly first.
Type parameters
Part of the data is not on the element but on its type. You get the type with doc.GetElement(element.GetTypeId()). If you change a type parameter, it changes for all elements of that type. So do it on purpose.
Units
Lengths and diameters are stored in feet internally. Convert with UnitUtils.ConvertToInternalUnits and UnitUtils.ConvertFromInternalUnits, using for example UnitTypeId.Millimeters.
What exactly happens when you forget the transaction is covered in "Attempt to modify the model outside of transaction".