Revit .Net API has function overloading, however Python doesn't support overloading

Revit .Net API has function overloading, however Python doesn't support overloading

chendong.jy
Participant Participant
2,087 Views
21 Replies
Message 1 of 22

Revit .Net API has function overloading, however Python doesn't support overloading

chendong.jy
Participant
Participant

I am using this function - NewFamilyInstance Method (Line, FamilySymbol, View). However, this function has overloading in API.

 

kRDct.png

The .net support function overloading, however Python doesn't support that. How can Revit Python API work in this case?

When I use one of these functions and pass the different parameters, how can this work properly?

fis = db.Collector(of_class='FamilyInstance')

start_point = XYZ(1, 0, 0)
end_point = XYZ(2, 0, 0)

line = Line.new(start_point, end_point)

views = db.Collector(of_class='View')
ItemFactoryBase.NewFamilyInstance(line, fis[1].Symbol, views[1])

The code above gave me the following error in RevitPythonShell

ItemFactoryBase.NewFamilyInstance(line, fis[1].Symbol, views[1])

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: NewFamilyInstance() takes at least 4 arguments (3 given)
 
2,088 Views
21 Replies
Replies (21)
Message 2 of 22

jeremy_tammik
Alumni
Alumni

Apparently, Python has methods to handle function overloads; I see some simply searching the Internet for "python .net overload":

   

https://duckduckgo.com/?q=python+.net+overload

  

Can't some of them be used to handle the Revit API methods? I would check out pyRevit, for instance, and the RevitPythonShell. They both support the entire Revit API, afaik, and they must support the NewFamilyInstance overloads in order to achieve that.

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 3 of 22

chendong.jy
Participant
Participant

I am not sure if it's implemented in Revit Python API. 

 

My understanding is Revit Python API is just a wrapper of .net framework.  It's not using any of tools like 

from pythonlangutil.overload import Overload, signature

 

from typing import overload

 

to achieve function overloading kind of things. 

Does .net implement that by default? I don't know. 

 

If you could provide a example, I would really appreciate that. Based on my trial in Revit Python Shell, it didn't work that way.  

0 Likes
Message 4 of 22

RPTHOMAS108
Mentor
Mentor

Python isn't my language but I've not really encountered many people using it saying that they can't create FamilyInstances due to overloading. I think it would have come up prior to now. Perhaps you can wait here for a reply from someone with better knowledge of Python or see if it has come up elsewhere:

 

RevitPythonShell: Families - THE PROVING GROUND

 

My understanding is that the signatures are not ambiguous in terms of the order of the types used in such. So although Python isn't a strongly typed language you are still in the end passing types to the Revit API in a set order. Would be an issue perhaps if some of the parameters were optional or you passed a base class where the distinction of a inherited class is required. I believe also that the issue with overloading in Python is likely more about the objects you author in Python since it isn't typed (although there are awkward mechanisms for that overloading). There has to be some disadvantage to writing less,.

 

My other observation is that you shouldn't be using ItemFactoryBase directly since it needs to be initialised with a Document.

 

From link above:

 

familyInst = doc.FamilyCreate.NewFamilyInstance(loc, famsymb, Structure.StructuralType.NonStructural)
 

 

 

Message 5 of 22

jeremy_tammik
Alumni
Alumni

Yup, fully agree with Richard on all points. If you prefer to get a faster answer on Python-specific aspects, the experts in the (Python-based) Dynamo forum may be able to help you better and faster:

 

https://forum.dynamobim.com

   

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 6 of 22

sragan
Collaborator
Collaborator

it looks like you are just taking the first item in a collection of all the elements and views in the project.

 

How do you know the family you are getting from the 1st element in the collector is a line based family?

 

And how do you know the view you are using is a suitable view.   

 

And I believe your view collector could actually be getting returning a view type instead of an actual view.   

0 Likes
Message 7 of 22

chendong.jy
Participant
Participant

Thank you @sragan , you raised very good quesions and actually these questions are also what I 'd like to know. 

 

I have tried using json.dumps(fis[1]) to get all the object information but unfortunately, this fis[1] is not JSON serializable. 

 

I have looked into all the members and methods of this object - fis[1], but none of them seems to be able to answer this question - whether or not fis[1] is a line based family? How can I find out, I need your help. 

 

Second question, the view - views[1] - the second element of views array, I have checked it's Name property and ToString() method.

 

The result is here, 

 >>> views[1].Name
'Level 0'
>>> views[1].ToString()
'Autodesk.Revit.DB.ViewPlan'

I don't have a clue if it's a view type of an actual view. Please advice. 

 

Anything else you'd like to know please let me know. Much appricate.  

0 Likes
Message 8 of 22

chendong.jy
Participant
Participant
Thank you for your advice, Jeremy.

I will try and once I believe it's done in this thread here. Actually, it's quite helpful and I have got quite good information here already. Really appreciate.
0 Likes
Message 9 of 22

sragan
Collaborator
Collaborator

I haven't used Python recently, and I'm not following the JSON comment. 

 

However, with Revit, its very common to have to go through a collection and the name of every element until you find the one you want.   With C#, this is accomplished with a "foreach" statement.  For example, this code finds a family symbol named data and places it in "familySymbolToFind":

 

FilteredElementCollector familyCollector = new FilteredElementCollector(doc);
familyCollector.OfClass(typeof(FamilySymbol));
FamilySymbol familySymbolToFind = null;
foreach (FamilySymbol familySymbol in familyCollector)
{ 
if (familySymbol.Name == "Data")
{familySymbolToFind = familySymbol;
}
}

   

Its also common to use .WhereElementIsNotElementType() to return only actual family instances.

 

Also, it looks like the overload that uses a line is only applicable to detail based families:

 

Solved: Insert line based family - Autodesk Community - Revit Products

    

0 Likes
Message 10 of 22

chendong.jy
Participant
Participant
Thank you for your help, @RPTHOMAS108.

I am not sure if this issue is caused by no equivalent function overloading in Python. This is a guess, hence I posted here to get help.

I will keep looking for the answer and once I am clear, will post some results here.

It's possible I am in a wrong way to use the FamilyInstance create function which caused this issue. I am very new to Revit API.

And no offence, the example in Revit Documentation mostly is about how to query a result once you have got the object but very little information about how to create an object. Hence, I am struggling to find how it works and how to use the API.

In this link, from a Python developer point of view,
https://www.revitapidocs.com/2022/899076fd-73d2-5be0-8872-b8f389d4ba49.htm
it's very natrual to initiatlize a ItemFactoryBase object first, then call it's method - NewFamilyInstance to create a line based family instane. Because, from the examples, no matter C# or Visual Basic, it didn't say the method is a static method, so initilise the object is very much based instinct of a developer. However, when I initilize ItemFactoryBase, the RevitPythonShell said, there is no public constructor.

public FamilyInstance NewFamilyInstance(
Line line,
FamilySymbol symbol,
View specView
)

I struggled to find bits and pieces from Internet, finally, I have found this
https://www.revitapidocs.com/2022/4f835512-a922-c7da-d389-3bdcb41a5660.htm,
Document class also have method with the same name with the same purpose ( I guess), but I don't know when and how to use both.
- NewFamilyInstance(Line, FamilySymbol, View)

So, if I use this approach, RevitPythonShell won't give the complain about the parameter numbers. (It complained something else, fis[1] is not a line based family)
doc.Create.NewFamilyInstance(line.unwrap(), fis[1].Symbol, views[0])

And thank you to point out this approach
- doc.FamilyCreate.NewFamilyInstance
I did try that, however, this method is used in FamilyEditor.

However, Actually, I am trying to place a FamilyInstance in a Project rather than editing/creating a new FamilyInstance.

Really appreciate your help and looking forwards for your feedback.
0 Likes
Message 11 of 22

chendong.jy
Participant
Participant
Thank you @sragan , you raised very good quesions and actually these questions are also what I 'd like to know.



I have tried using json.dumps(fis[1]) to get all the object information but unfortunately, this fis[1] is not JSON serializable.



I have looked into all the members and methods of this object - fis[1], but none of them seems to be able to answer this question - whether or not fis[1] is a line based family? How can I find out, I need your help.



Second question, the view - views[1] - the second element of views array, I have checked it's Name property and ToString() method.



The result is here,

>>> views[1].Name
'Level 0'
>>> views[1].ToString()
'Autodesk.Revit.DB.ViewPlan'

I don't have a clue if it's a view type of an actual view. Please advice.



Anything else you'd like to know please let me know. Much appricate.
0 Likes
Message 12 of 22

chendong.jy
Participant
Participant
In Javascript, to check all the info contained in an object is using JSON.stringify(obj1) so you will see all the properties value belonging to the obj.

Normally, Python approach is using print(obj) if the __repr__ private method is implemented and exposed as much as possible that the information of this object contained. This object is not a dataclass so JSON serilize approach doesn't work.

hmm... your code is very helpful, but it doesn't show how to verifiy this ojbect is a line based family or not.
Maybe which property of familySymbol I should check? Is there a condition I can use to filter and get the answer about if it's a line based family?

0 Likes
Message 13 of 22

jeremy_tammik
Alumni
Alumni

Probably, most of your questions would be immediately answered if you would be willing to take a plunge into any of the numerous 'getting started with the Revit API' tutorials. Many of them are C# based, but for absolute beginners. Some are Python-based. Some work with the built-in Revit macro IDE, the integrated development environment, so you don't have to install anything whatsoever:

  

https://duckduckgo.com/?q=revit+api+python+getting+started 

  

The main reason I make this suggestion is that there are a number of common standard procedures for identifying the objects and elements that you are searching for using a filtered element collector, and it would be useful for you to know about and understand them. First and forest, I am thinking of the Element .NET class name and the category. The class name will immediately inform you whether it is a type or instance.

  

Another point is that some aspects of the Revit API are very non-obvious for a seasoned programmer. An experienced Revit end user, on the other hand, will find it much easier to understand, since she knows the underlying BIM paradigm motivating the relationships and interactions between elements. In case you haven't taken a look yet, here are some hints:

  

https://thebuildingcoder.typepad.com/blog/about-the-author.html#2

  

  

Jeremy Tammik Developer Advocacy and Support + The Building Coder + Autodesk Developer Network + ADN Open
0 Likes
Message 14 of 22

RPTHOMAS108
Mentor
Mentor

The link I provided from the heading entitled 'Placing a Single Family Instance' onwards covers both filtering for elements and placing a family instance or multiple family instances. Is this still missing something you require or not working when you switch to the overload you require?

 

TypeError: NewFamilyInstance() takes at least 4 arguments (3 given)

 

It is expecting Document also but the above is more an indication you are doing something larger wrong because you can see from the overloads in the API documentation that there are overloads with three arguments. A lot of Python users when starting out seem to make the mistake of using certain class objects directly in a static way which results in an argument number count error similar to the above. They should ask themselves what are the requirements of this class i.e. which Document is the FamilyInstance being placed in and how do I specify that? They may believe it will be placed in the active document automatically but that would be wrong.

 

line = Line.new(start_point, end_point)

I believe the above is also wrong, there is no public constructor for Line, use Line.CreateBound.

 

Should have noted that you would only use Document.FamilyCreate.NewFamilyInstance if you are placing the instance in a family document otherwise you would be using Document.Create.NewFamilyInstance.

 

My limited use of Python made me aware that errors in Python sometimes go back further than the line noted. If you get the wrong object out from a line higher up it'll still get fed into the function on the line with the error (causing the error). In a strongly typed language such an issue would result in a more obvious to understand invalid cast exception (more local to the actual error you made). So you should also probably focus more on the whole thing rather than a specific line error. In a way you need to know more about the object model of the Revit API when using Python because of the lack of type safety. The error related to the 'Line.New' is a good example of that since you would not be able to access that ordinarily in VB.Net or C# so in Python you have to have that extra knowledge that there is no public 'New' on 'Line' class of any kind (unless the RPS/RPW is adding something here).

 

"is not a line based family" means you are on the right track and have got through to the API. Is the family you are adding a line based detail item?

0 Likes
Message 15 of 22

mhannonQ65N2
Collaborator
Collaborator

I looked through some detail family symbols with Revit Lookup and it seems like line based detail family symbols will have the built-in parameter FAMILY_LINE_LENGTH_PARAM while non-line-based symbols will not. However, even though the family symbol has that parameter, HasValue will usually--or maybe even always--be false as each instance defines its own line.

 

if symbol.get_parameter(BuiltInParameter.FAMILY_LINE_LENGTH_PARAM) != None
    # It's a line based family symbol

 

 

Also, to elaborate a bit on what @RPTHOMAS108 has already said, NewFamilyInstance wasn't working because it was being called as a static method when it is actually an instance method. The reason it was saying TypeError: NewFamilyInstance() takes at least 4 arguments (3 given) is that in .NET every instance method has the instance, this, as an implicit first argument. Instance methods are distinguished from static methods by the lack of the static keyword. In python, the instance parameter, self, is explicitly present in the signatures of instance methods.

0 Likes
Message 16 of 22

chendong.jy
Participant
Participant
Thank you @mhannonQ65N2. Do you know how to create a instance first then from the instance to call the instance method?

I tried, but it said no public constructor so I can't call instance method.

Actually, I still don't know how to use ItemFactoryBase.NewFamilyInstance this API still.

I am still struggling to understand this. Though I have better understanding now and have more materials to look at in this stage but still feel not confident about this topic.

0 Likes
Message 17 of 22

chendong.jy
Participant
Participant
So the Line class I used here is not a Revit Line class but from RevitPythonWrapper.
line = Line.new(start_point, end_point)

If we deep dive into the implementation of this rpw (RevitPythonWrapper) Line class, you will find this, it did use DB.Line.CreateBound, so rpw.Line.new is just a wrapper. So it's not the root cause.

class Line(Curve):

"""
DB.Line Wrapper

>>> line = Line.new([-10,0], [10,0])
>>> # or
>>> line = Line.new(ExistingLineObject)
>>> line.create_detail()

"""
_revit_object_class = DB.Line

@classmethod
def new(cls, pt1, pt2):
"""
Args:
point1 (``point``): Point like object. See :any:`XYZ`
point2 (``point``): Point like object. See :any:`XYZ`
"""
pt1 = XYZ(pt1)
pt2 = XYZ(pt2)
line = DB.Line.CreateBound(pt1.unwrap(), pt2.unwrap())
return cls(line)

I am a newbie about Revit model, I already have many years of Python experience.

I understand it's a instance method not a class/static method, however, I am not able to create a instance because it said no public constructor.

Actually your last question is also what I am trying to figure out.

"Is the family you are adding a line based detail item?" I don't know how to tell if this is a line based detail item.
0 Likes
Message 18 of 22

mhannonQ65N2
Collaborator
Collaborator

It's like @RPTHOMAS108 said; you don't create a new instance, you use your current document's instance.

myDocument.Create.NewFamilyInstance(line, fis[1].Symbol, views[1])

 

As for if it's a line based detail item, check it's Category to see if it's a detail item and check if it has the FAMILY_LINE_LENGTH_PARAM built-in parameter to see if it's line-based.

0 Likes
Message 19 of 22

chendong.jy
Participant
Participant

Since it's not a line based family, I also tried using location based method (

NewFamilyInstance Method (XYZ, FamilySymbol, StructuralType)

)to create that family instance. 

 

>>> doc.Create.NewFamilyInstance(start_point.unwrap(), fis[1].Symbol, DB.Structure.StructuralType.Column)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
EnvironmentError: System.Runtime.InteropServices.SEHException (0x80004005😞 External component has thrown an exception.
   at SubTransaction.start(SubTransaction* )
   at Autodesk.Revit.Creation.ItemFactoryBase.NewFamilyInstance(XYZ location, FamilySymbol symbol, StructuralType structuralType)
   at Microsoft.Scripting.Interpreter.FuncCallInstruction`5.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.LightLambda.Run6[T0,T1,T2,T3,T4,T5,TRet](T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
   at System.Dynamic.UpdateDelegates.UpdateAndExecute5[T0,T1,T2,T3,T4,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
   at Microsoft.Scripting.Interpreter.FuncCallInstruction`8.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.LightLambda.Run6[T0,T1,T2,T3,T4,T5,TRet](T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
   at IronPython.Compiler.Ast.CallExpression.Invoke3Instruction.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.LightLambda.Run2[T0,T1,TRet](T0 arg0, T1 arg1)
   at IronPython.Compiler.PythonScriptCode.RunWorker(CodeContext ctx)
   at IronPython.Hosting.PythonCommandLine.<>c__DisplayClass27_0.<RunOneInteraction>b__0()

 

The above exception is raised. 

Ah, it's so hard, so difficult to use and very limited information online. 

0 Likes
Message 20 of 22

chendong.jy
Participant
Participant

Also tried this API - 

NewFamilyInstance Method (XYZ, FamilySymbol, View)

 

 

views = db.Collector(of_class='View')

>>> views[3].Name
'Level 1'

doc.Create.NewFamilyInstance(start_point.unwrap(), fis[1].Symbol, views[3])

Exception : Autodesk.Revit.Exceptions.ArgumentException: Family cannot be placed as hosted on an input face reference, because its FamilyPlacementType is not ViewBased
Parameter name: symbol
   at Autodesk.Revit.Creation.ItemFactoryBase.NewFamilyInstance(XYZ origin, FamilySymbol symbol, View specView)
   at Microsoft.Scripting.Interpreter.FuncCallInstruction`5.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.LightLambda.Run6[T0,T1,T2,T3,T4,T5,TRet](T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
   at System.Dynamic.UpdateDelegates.UpdateAndExecute5[T0,T1,T2,T3,T4,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
   at Microsoft.Scripting.Interpreter.FuncCallInstruction`8.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.LightLambda.Run6[T0,T1,T2,T3,T4,T5,TRet](T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5)
   at IronPython.Compiler.Ast.CallExpression.Invoke3Instruction.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.Interpreter.Run(InterpretedFrame frame)
   at Microsoft.Scripting.Interpreter.LightLambda.Run2[T0,T1,TRet](T0 arg0, T1 arg1)
   at IronPython.Compiler.PythonScriptCode.RunWorker(CodeContext ctx)
   at Microsoft.Scripting.Hosting.ScriptSource.Execute(ScriptScope scope)
   at Microsoft.Scripting.Hosting.ScriptSource.ExecuteAndWrap(ScriptScope scope, ObjectHandle& exception)

0 Likes