How to implement an interface in Python?

Hi everyone,
I would like to know how can I implement this Interface (IFailuresPreprocessor) from the revit API in Python. Actually I need to write the following C# class in Python:

class MyFailureProcessor : IFailuresPreprocessor
{
    public FailureProcessingResult PreprocessFailures(FailuresAccessor failuresAccessor)
    {
        return FailureProcessingResult.Continue;
    }
}

any help would be very appreciated. Thanks!

I don’t have a way to test, but I think like this right?

class MyFailureProcessor(IFailuresPreprocessor):
    def PreprocessFailures(self, failuresAccessor):
        return FailureProcessingResult.Continue

Thanks a lot for your proposal @Archibum!
I will explain better what I’m triing to achieve maybe you could help me better. I am writing a scrip to modify the elevation of all the points in a TopographycSurface (TS). I need to move vertically all the points of a TS a same distance.
To edit de TS you have to call a the TopographyEditScope class and call a .start() and .commit() method like you would do with a Transaction. My problem comes with the TopographyEditScope.Commit() method.It needs the IFailuresPreprocessor interface to be passed. I found on the net this issue that handles a similar problem, here is where I found the definition of the class.
I leave my code below (with your definition of the class) if you want to test. For the moment I get no errors from pyRevit but it fails and I get this error (see image below) from Revit itself. I suspect that something goes wrong with the TopographyEditScope.Commit() method.
211208-01 error

# coding=utf-8

from Autodesk.Revit.DB import *
from Autodesk.Revit.DB.Architecture import TopographyEditScope
from Autodesk.Revit.UI import *
from Autodesk.Revit.UI.Selection import ObjectType
from System.Collections.Generic import List

# Helper classes

class MyFailureProcessor(IFailuresPreprocessor):
    def FailureProcessingResult(self, FailuresAccessor):
        return FailureProcessingResult.Continue

# 0 - initial statements 

uiapp = __revit__
uidoc = uiapp.ActiveUIDocument
doc = uiapp.ActiveUIDocument.Document

scriptName = 'SetTopoPointsElevation'

refPick = uidoc.Selection.PickObject(ObjectType.Element, "Pick an element")
TopoSurface = doc.GetElement(refPick)
TopoSurfaceId = TopoSurface.Id

## Here, it would be nice to be able to pick just TopographySurface object.

#Define offset elevation

OffsetElevation = 10.00

TopoSurfacePoints = TopoSurface.GetPoints()

TopoEditScope = TopographyEditScope(doc,"Edit Points Elevation")
TopoEditScope.Start(TopoSurfaceId)

t = Transaction (doc,scriptName)
t.Start()

for point in TopoSurfacePoints:
    PointElevation = point.Z
    NewPointElevation = PointElevation + OffsetElevation
    TopoSurface.ChangePointElevation(point, NewPointElevation)

failProc = MyFailureProcessor()

t.Commit()

TopoEditScope.Commit(failProc)

# N - OUTPUT

print('Nom: ' + TopoSurface.Name + '\n')
print('Nombre de punts: ' + str(TopoSurfacePoints.Count))

Thanks!

Haven’t try it but starting the transaction before TopographyEditScope might do the trick?

Hi @Jean-Marc . I’ve already tried what you suggested and it doesn’t work. I really don’t understand what’s going on.
If I set the t.start() before the TopoEditScope.Start() I get this error:


I also tried to finish the Transaction before finishing de TopoEditScope and I get this error:

“Autodesk.Revit.Exceptions.InvalidOperationException: EditScope cannot be closed, for there is a transaction group still open in the document.”

I am not sure but it seems somehow it doesn’t close the transaction or the TopoEditScope.
I don’t understand why the helper class MyFailureProcessor defines the function FailureProcessingResult()

class MyFailureProcessor(IFailuresPreprocessor):
    def FailureProcessingResult(self, FailuresAccessor):
        return FailureProcessingResult.Continue

It’s not used in the main code and maybe the FailureProcessingResult.Continue is the way to close/commit succesfully the TopoEditScope…
I’ll keep triing.

The function inside the MyFailureProcessor class should be PreprocessFailures(self, failuresAccessor), not FailureProcessingResult(self, FailuresAccessor) as you have in your code.

Also, if you want to add a filter to select only topo surfaces, you can add this class:

class topo_filter(Selection.ISelectionFilter):
    def AllowElement(self, element):
        if isinstance(element, Architecture.TopographySurface):
            return True
    def AllowReference(self, ref, point):
        return True

and then edit your PickObject method to include that filter, like this:

refPick = uidoc.Selection.PickObject(ObjectType.Element, topo_filter(), "Pick an element")

IMHO with this :arrow_up: you already started a transaction

that could help :arrow_down: RevitSdkSamples/SDK/Samples/Site/CS/SiteNormalizeTerrainInRegionCommand.cs at master · jeremytammik/RevitSdkSamples · GitHub

from sdk sample in C#


            // Edit scope for all changes
            using (TopographyEditScope editScope = new TopographyEditScope(doc, "Edit TS"))
            {
                editScope.Start(toposurface.Id);

                using (Transaction t = new Transaction(doc, "Normalize terrain"))
                {
                    t.Start();

                    // Change all points to same elevation
                    toposurface.ChangePointsElevation(points, elevation);
                    t.Commit();
                }

                editScope.Commit(new TopographyEditFailuresPreprocessor());
            }
        }

Great! If works! Thanks @Archibum !
And thanks also for this:

Thanks @Jean-Marc !
It is clear that the Transactions must be inside the EditScope. Thanks for the example also.

For the sake of the exercise, here is a cleaned up version using 99% pyrevit (couldn’t find the TopographyEditScope in pyRevit lib)

from Autodesk.Revit.DB.Architecture import TopographyEditScope

from pyrevit import revit, DB, forms, script, UI

from pyrevit.revit.selection import pick_element_by_category

# Helper classes

class MyFailureProcessor(DB.IFailuresPreprocessor):

    def PreprocessFailures(self, failuresAccessor):

        return DB.FailureProcessingResult.Continue

# initial statements

doc = revit.doc

with forms.WarningBar(title='Select Topography Surface'):

    try:

        TopoSurface = pick_element_by_category(DB.BuiltInCategory.OST_Topography, 'Select Topography Surface')

        TopoSurfaceId = TopoSurface.Id

    except:

        script.exit()

#Define offset elevation

OffsetElevation = 10.00

TopoSurfacePoints = TopoSurface.GetPoints()

# Main transaction getting to the topo edition mode

TopoEditScope = TopographyEditScope(doc,"Edit Points Elevation")

TopoEditScope.Start(TopoSurfaceId)

# sub transaction assigning the elevation to the points

# handling of commiting the transaction is done with pyrevit revit.Transaction

with revit.Transaction("Move Points Elevation"):

    for point in TopoSurfacePoints:

        PointElevation = point.Z

        NewPointElevation = PointElevation + OffsetElevation

        TopoSurface.ChangePointElevation(point, NewPointElevation)

# sub transaction is ended

#  Closing the topo edition mode

failProc = MyFailureProcessor()    

TopoEditScope.Commit(failProc)

output = script.get_output()

output.close_others()

# N - OUTPUT

forms.alert(msg = 'Nom de la surface: ' + TopoSurface.Name + '\n' + 'Nombre de points: ' + str(TopoSurfacePoints.Count), title = 'Nombre de points')

@vbertran you just have to ask the user for the offset now :grinning_face_with_smiling_eyes:

1 Like

Great @Jean-Marc !
I’ve added the UnitUtils.ConvertToInternalUnits() method because I work in meters. It is writen for Revit2020, I think in Revit2022 it has change the implementation (I leave it below).
I’ll learn many things from you code. I am new coding with pyRevit, just a question; Why do you import from the pyRevit library? Is it faster?? Is it better??
And by the way, there is in the pyRevit library a form to ask the user to write an imput (in this case the OffsetElevation)?
Thanks!

from Autodesk.Revit.DB.Architecture import TopographyEditScope

from Autodesk.Revit.DB import UnitUtils, DisplayUnitType 

from pyrevit import revit, DB, forms, script, UI

from pyrevit.revit.selection import pick_element_by_category

# Helper classes

class MyFailureProcessor(DB.IFailuresPreprocessor):

    def PreprocessFailures(self, failuresAccessor):

        return DB.FailureProcessingResult.Continue

# initial statements

doc = revit.doc

with forms.WarningBar(title='Select Topography Surface'):

    try:

        TopoSurface = pick_element_by_category(DB.BuiltInCategory.OST_Topography, 'Select Topography Surface')

        TopoSurfaceId = TopoSurface.Id

    except:

        script.exit()

#Define offset elevation (in meters)

OffsetElevation = 10.00

OffsetElevationInternalUnits = UnitUtils.ConvertToInternalUnits(OffsetElevation,DisplayUnitType.DUT_METERS)

TopoSurfacePoints = TopoSurface.GetPoints()

# Main transaction getting to the topo edition mode

TopoEditScope = TopographyEditScope(doc,"Edit Points Elevation")

TopoEditScope.Start(TopoSurfaceId)

# sub transaction assigning the elevation to the points

# handling of commiting the transaction is done with pyrevit revit.Transaction

with revit.Transaction("Move Points Elevation"):

    for point in TopoSurfacePoints:

        PointElevation = point.Z

        NewPointElevation = PointElevation + OffsetElevationInternalUnits

        TopoSurface.ChangePointElevation(point, NewPointElevation)

# sub transaction is ended

#  Closing the topo edition mode

failProc = MyFailureProcessor()    

TopoEditScope.Commit(failProc)

output = script.get_output()

output.close_others()

# N - OUTPUT

forms.alert(msg = 'Nom de la surface: ' + TopoSurface.Name + '\n' + 'Nombre de points: ' + str(TopoSurfacePoints.Count), title = 'Nombre de points')

Check out the effective input and effective output links on this page: Notion – The all-in-one workspace for your notes, tasks, wikis, and databases.