Hello,
for those who work with CPython3 / PythonNet here is a workaround for using .Net class interfaces
UPDATE PythonNet3 engine 1.1.0 see this post
Syntax for general cases
the solution is to create a Class Type (with Namespace) only once and reuse it in subsequent calls.
import clr
import System
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
clr.AddReference('RevitAPIUI')
import Autodesk.Revit.UI as RUI
from Autodesk.Revit.UI import *
from Autodesk.Revit.UI.Selection import *
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
class Custom_SelectionElem:
def __new__(cls, *args, **kwargs):
cls.args = args
# name of namespace : "CustomNameSpace" + "_" + shortUUID (8 characters)
# IMPORTANT NOTE Each time you modify this class, you must change the namesapce name.
cls.__namespace__ = "SelectionNameSpace_tEfYX0DHE"
try:
# 1. Try to import the module and class. If it already exists, you've probably already created
module_type = __import__(cls.__namespace__)
return module_type._InnerClassInterface(*cls.args)
except ImportError as ex:
print(ex)
# 2. If we get an ImportError, then the derivative class hasn't been built. Build it. Make sure to set the
# __namespace__ so that it is created with the appropriate .Net namespace.
class _InnerClassInterface(ISelectionFilter):
__namespace__ = cls.__namespace__
#
def __init__(self, bic):
super().__init__()
self.bic = bic
def AllowElement(self, e):
if e.Category.Id == ElementId(self.bic):
return True
else:
return False
def AllowReference(self, ref, point):
return True
return _InnerClassInterface(*cls.args)
# usage
for i in range(3):
RUI.TaskDialog.Show("Select", f"Select a door {i+1}/3")
ref = uidoc.Selection.PickObject(ObjectType.Element, Custom_SelectionElem(BuiltInCategory.OST_Doors))
print(ref.ElementId)
Compatibility of this method :
-
Net Framework 4.x
Revit / Civil 2022 - CPython3/PythonNet2.5 : Yes
Revit / Civil 2023 - CPython3/PythonNet2.5 : Yes
Revit / Civil 2024 (Dynamo 2.19) - CPython3/PythonNet2.5 : NO -
Net Core 8+
Revit / Civil 2025+ - CPython3/PythonNet2.5 : Yes
Revit / Civil 2025+ - PythonNet3 : Yes
Note on .NET Class Interfaces with Python:
- due to a bug with Revit 2024, I recommend staying with IronPython up to and including Revit 2024 (unless this is corrected in a future update).
- the use of .Net Class Interfaces with
out
parameters work only PythonNet3 (engine currently being integrated by Team Dynamo)
Example use of .Net Class Interfaces with out
parameters work only PythonNet3
import clr
import System
clr.AddReference('RevitAPI')
from Autodesk.Revit.DB import *
clr.AddReference('RevitAPIUI')
import Autodesk.Revit.UI as RUI
from Autodesk.Revit.UI import *
from Autodesk.Revit.UI.Selection import *
clr.AddReference("RevitServices")
import RevitServices
from RevitServices.Persistence import DocumentManager
from RevitServices.Transactions import TransactionManager
doc = DocumentManager.Instance.CurrentDBDocument
uiapp = DocumentManager.Instance.CurrentUIApplication
uidoc = uiapp.ActiveUIDocument
#
class Custom_FamilyOption:
def __new__(cls, *args, **kwargs):
cls.args = args
# name of namespace : "CustomNameSpace" + "_" + shortUUID (8 characters)
# IMPORTANT NOTE Each time you modify this class, you must change the namesapce name.
cls.__namespace__ = "FamilyOptionNameSpace_tEfYX0DHE"
try:
# 1. Try to import the module and class. If it already exists, you've probably already created it once, so you can use it.
module_type = __import__(cls.__namespace__)
return module_type._InnerClassInterface(*cls.args)
except ImportError as ex:
print(ex)
# 2. If we get an ImportError, then the derivative class hasn't been built. Build it. Make sure to set the
# __namespace__ so that it is created with the appropriate .Net namespace.
class _InnerClassInterface(IFamilyLoadOptions) :
__namespace__ = cls.__namespace__
def __init__(self):
super().__init__()
def OnFamilyFound(self, familyInUse, _overwriteParameterValues):
overwriteParameterValues = True
return (True, overwriteParameterValues)
def OnSharedFamilyFound(self, sharedFamily, familyInUse, source, _overwriteParameterValues):
overwriteParameterValues = True
return (True, overwriteParameterValues)
return _InnerClassInterface(*cls.args)
#
def loadFamily(path):
TransactionManager.Instance.EnsureInTransaction(doc)
opts = Custom_FamilyOption()
dummyFamily = None
loadFamily = doc.LoadFamily(path, opts, dummyFamily)
TransactionManager.Instance.TransactionTaskDone()
for path in IN[0] : loadFamily(path)
for explanations and more information, I wrote an article about this (use the translation widget if needed)