NXOpen.Features.Feature and the Feature Collection

In the NXOpen API, the "part" object is among the most used and most useful to the programmer. The part object gives us access to dozens (hundreds?) of properties and collections of other objects that we can use to query and modify the part. Let's take a look at the NXOpen.Feature object and the {part}.Features collection.

The feature tree in the part navigator will be immediately familiar if you have been using NX to create models for any length of time. The order of the features in the navigator act as an algorithm for creating the current model that you see in the graphics window. Scroll through the part navigator, and you can quickly tell which feature is current, which features are suppressed, and which features have update errors (hopefully none!). Expanding the "dependencies" window will give quick access to the selected feature's parent feauture(s) and any dependent features. The NXOpen part object gives us similar access to the part's features through our program. The part navigator lists all of the features in the current model; the part's .Features collection is analogous to the part navigator, it is a container for the individual feature objects that make up the model.

The code below shows how to access the features collection and process each feature object that it contains.

Option Strict Off
Imports System
Imports NXOpen

Module Module1

Sub Main()

Dim theSession As Session = Session.GetSession()
If IsNothing(theSession.Parts.BaseWork) Then
'active part required
Return
End If

Dim workPart As Part = theSession.Parts.Work
Dim lw As ListingWindow = theSession.ListingWindow
lw.Open()

For Each partFeature As Features.Feature In workPart.Features
lw.WriteLine("feature name: " & partFeature.GetFeatureName)
lw.WriteLine("feature type: " & partFeature.GetType.ToString)
lw.WriteLine("")
Next

lw.Close()

End Sub

Public Function GetUnloadOption(ByVal dummy As String) As Integer

'Unloads the image immediately after execution within NX
GetUnloadOption = NXOpen.Session.LibraryUnloadOption.Immediately

End Function

End Module

Ok, so the code above isn't all that exciting. There are, however, a few interesting aspects that may not be immediately obvious. First, depending on the features used in your current work part, the journal output may list more features than what you see in the part navigator; furthermore, multiple features may report the same timestamp. If you did not experience this with your current part and would like to see it for yourself, try the following: create a block feature then start the hole command, select a large face of the block to sketch on and pick multiple points on the face for the hole locations. Now run the journal again. In the part navigator you will see the block and hole commands, but in the journal output, in addition to the block and hole, you will also see a sketch feature and instead of a single hole feature, there will be multiple hole features. So what's going on here? An error in the journal code? or worse, a bug in the API? Actually, neither. Here the curtain starts to part and we begin to see the inner workings of NX. Some features can be internal to other features. In this case, the sketch feature and the individual hole features are wrapped up and presented to the user as a single feature in the part navigator. This behavior helps the user by keeping the feature tree concise, but we now know that each of these items is a feature in its own right.

The second item of note about the journal code above is not about feature objects or even the NXOpen API, but about programming in general; specifically it is about object oriented programming (OOP). Take another look at the For loop variable "partFeature"; you will notice that it is declared as type "Features.Feature", yet none of the reported features were of the type "Features.Feature". In the journal output, each feature was reported as a specific type such as .Features.Block or Features.HolePackage. How did we process these specific feature types if we didn't declare our variable as the proper type? If you look up the Feature object type in the NXOpen API reference, you will see that it is described as an "abstract" class. Dozens of specific feature types are shown as deriving from the Feature class. If you are familiar with OOP, the Feature class is a "superclass" or "base class". The Feature class defines all of the properties and methods that will be common to all feature types. Specific feature objects, such as a hole feature, will automatically "inherit" all of the properties and methods defined in the "Feature" class. These specific feature types, also known as "subclasses" or "child classes", may use these properties and methods differently and the subclass will often add new properties and/or methods that are specific to the subclass. Any subclass object can be treated as if it were of the superclass type. What this means for us is that every specific feature type (subclass) can be treated as if it were of type Features.Feature (superclass); any calls to properties or methods that are defined in the superclass will work on the subclass objects. This gives us access to a variety of useful properties and methods without needing to know the specific feature type.

Iterating through the part features and querying each feature's properties is a good way to learn more about specific features. Once you know the .FeatureType, you can look it up in the API reference to see what specific properties and methods the feature type supports. This knowledge better equips you to write useful and robust code to handle the feature type. When learning about a new feature, the first thing that I do is create a file that uses the feature and run the journal code below:

Option Strict Off
Imports System
Imports NXOpen
Imports NXOpen.UF
Imports NXOpen.Features

Module Module2
Sub Main()

Dim theSession As Session = Session.GetSession()
Dim theUfSession As UFSession = UFSession.GetUFSession
Dim lw As ListingWindow = theSession.ListingWindow
lw.Open()

lw.WriteLine("*** All Features ***")
For Each myFeature As Feature In theSession.Parts.Work.Features
lw.WriteLine(".GetFeatureName: " & myFeature.GetFeatureName)
lw.WriteLine(".Name: " & myFeature.Name)
lw.WriteLine(".IsInternal: " & myFeature.IsInternal.ToString)
lw.WriteLine(".FeatureType: " & myFeature.FeatureType)
lw.WriteLine(".GetType: " & myFeature.GetType.ToString)
lw.WriteLine(".Timestamp: " & myFeature.Timestamp.ToString)
lw.WriteLine(".Suppressed: " & myFeature.Suppressed.ToString)

Try
lw.WriteLine(".Location: " & myFeature.Location.ToString)
Catch ex As NXException
End Try

lw.WriteLine("")

Next
lw.Close()

End Sub
End Module

The call to the .Location property is wrapped in a Try block; this is because even though .Location is defined in the .Feature superclass, not all feature types make use of it. For those features that do not make use of it, the .Location property will be empty and trying to access its value will throw an error.

Conclusion
Have a look at the API reference for the Feature object type. There are many more interesting properties and methods that this article did not touch on. You should now have a better idea of how to use the feature collection and how to interrogate the individual features.

Comments

Is there a way to collect all solids in the assembly (at each level).
I want to create a journal that will mirror the whole assembly as (Mirrored Body Wavelink) with all solids included, regardless if they are inside a part or the assembly.

For solids collection (.Bodies) I need to reference the Part.
Component.OwningPart doesnt seem to work for me...

The code below demonstrates the easiest method that I have found so far to get all the bodies from a given assembly.

Option Strict Off
Imports System
Imports System.Collections.Generic
Imports NXOpen
Imports NXOpen.UF

Module Module1

Dim theSession As Session = Session.GetSession()
Dim theUfSession As UFSession = UFSession.GetUFSession()
Dim lw As ListingWindow = theSession.ListingWindow

Sub Main()

If IsNothing(theSession.Parts.BaseWork) Then
'active part required
Return
End If

Dim workPart As Part = theSession.Parts.Work
lw.Open()

Const undoMarkName As String = "report assembly bodies"
Dim markId1 As Session.UndoMarkId
markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, undoMarkName)

Dim theBodies As New List(Of Body)
theBodies = AskAllBodies(theSession.Parts.Display)

For Each tempBody As Body In theBodies
lw.WriteLine("isOccurrence: " & tempBody.IsOccurrence.ToString)
If tempBody.IsOccurrence Then
lw.WriteLine("OwningComponent: " & tempBody.OwningComponent.DisplayName)

Else
lw.WriteLine("OwningPart: " & tempBody.OwningPart.Leaf)

End If

lw.WriteLine("")

Next

lw.Close()

End Sub

Function AskAllBodies(ByVal thePart As Part) As List(Of Body)

Dim theBodies As New List(Of Body)

Dim aBodyTag As Tag = Tag.Null
Do
theUfSession.Obj.CycleObjsInPart(thePart.Tag, _
UFConstants.UF_solid_type, aBodyTag)
If aBodyTag = Tag.Null Then
Exit Do
End If

Dim theType As Integer, theSubtype As Integer
theUfSession.Obj.AskTypeAndSubtype(aBodyTag, theType, theSubtype)
If theSubtype = UFConstants.UF_solid_body_subtype Then
theBodies.Add(Utilities.NXObjectManager.Get(aBodyTag))

End If
Loop While True

Return theBodies

End Function

Public Function GetUnloadOption(ByVal dummy As String) As Integer

'Unloads the image immediately after execution within NX
GetUnloadOption = NXOpen.Session.LibraryUnloadOption.Immediately

End Function

End Module

How to check if the feature was selected in the part navigator UI or not? Intention is to suppress/unsuppress multiple selected features using f.suppress / f.unsuppress

BH

You can use the selection manager to see what objects (if any) are currently selected when your journal runs. Open a part, select some objects, and run the code below. It will report the type of each object selected and will gather any features found into a list object that you can use later in your code.

'NXJournaling.com
'February 18, 2022

'Get features from the pre-selected objects.

Option Strict Off
Imports System
Imports System.Collections.Generic
Imports NXOpen
Imports NXOpen.UF

Module Module181

Dim theSession As Session = Session.GetSession()
Dim theUfSession As UFSession = UFSession.GetUFSession()

Dim theUI As UI = UI.GetUI()
Dim lw As ListingWindow = theSession.ListingWindow

Sub Main()

Dim markId1 As Session.UndoMarkId
markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, "NXJ")

lw.Open()

Dim preSelFeatures As New List(Of Features.Feature)
'Dim type As Integer
'Dim subtype As Integer
Dim selobj As TaggedObject

Dim numsel As Integer = theUI.SelectionManager.GetNumSelectedObjects()
For inx As Integer = 0 To numsel - 1
selobj = theUI.SelectionManager.GetSelectedTaggedObject(inx)
'theUfSession.Obj.AskTypeAndSubtype(selobj.Tag, type, subtype)

lw.WriteLine("object type: " & selobj.GetType.ToString)

If TypeOf (selobj) Is Features.Feature Then
preSelFeatures.Add(selobj)
End If

Next

lw.WriteLine("features pre-selected: " & preSelFeatures.Count.ToString)

lw.Close()

End Sub

Public Function GetUnloadOption(ByVal dummy As String) As Integer

'Unloads the image immediately after execution within NX
GetUnloadOption = NXOpen.Session.LibraryUnloadOption.Immediately

End Function

End Module

Thanks for the code.
I tried selecting a few features and it lists the features, but the count is always 0.

```
object type: NXOpen.Sketch
object type: NXOpen.Sketch
object type: NXOpen.Sketch
features pre-selected: 0
```
object type: NXOpen.DatumPlane
object type: NXOpen.DatumPlane
object type: NXOpen.DatumPlane
object type: NXOpen.DatumPlane
object type: NXOpen.DatumPlane
features pre-selected: 0
```
object type: NXOpen.DatumPlane
object type: NXOpen.DatumPlane
object type: NXOpen.DatumPlane
object type: NXOpen.Sketch
object type: NXOpen.Sketch
object type: NXOpen.Sketch
features pre-selected: 0

Strangely this runs in VB, but if I try that with C#, it gives an error:

//
Line ## : The best overloaded method match for 'System.Collections.Generic.List.Add(NXOpen.Features.Feature)' has some invalid arguments
Line ## : Argument 1: cannot convert from 'NXOpen.TaggedObject' to 'NXOpen.Features.Feature'

lw.Open();
var preSelFeatures = new List();
TaggedObject selobj;
int numsel = theUI.SelectionManager.GetNumSelectedObjects();
for (int inx = 0, loopTo = numsel - 1; inx <= loopTo; inx++)
{
selobj = theUI.SelectionManager.GetSelectedTaggedObject(inx);
// theUfSession.Obj.AskTypeAndSubtype(selobj.Tag, type, subtype)

lw.WriteLine("object type: " + selobj.GetType().ToString());
if (selobj is NXOpen.Features.Feature)
{
preSelFeatures.Add(selobj);
}
}

lw.WriteLine("features pre-selected: " + preSelFeatures.Count.ToString());
lw.Close();

BH

I just tested my code with various feature types (it was not an exhaustive test, but I did use several different feature types) and they all worked except for datum planes, datum axes, and sketches. Strangely, datum csys's did work as expected. I'm not sure why certain features are not working; it is as if NX has selected the datum and sketch object rather than the feature object, even when you select a feature directly from the part navigator. Even with the selection priority set to feature, the feature isn't selected. However, if you set the selection filter to "feature" before selecting entries from the part navigator, then the code works as expected.

The code could be modified to return the feature of the selected objects (if there is one). However, it is odd to me that certain features behave differently. I'd suggest contacting GTAC with an incident report (IR); they would be able to see if this is a bug or the expected behavior.

Can you please provide the python version of this code?

Here is a python version of the "get pre-selected features" journal:

import NXOpen
import NXOpen.Features
import NXOpen.UF

theSession = NXOpen.Session.GetSession()
theUfSession = NXOpen.UF.UFSession.GetUFSession()
lw = theSession.ListingWindow

def main():

lw.Open()

theUi = NXOpen.UI.GetUI()
numsel = theUi.SelectionManager.GetNumSelectedObjects()

lw.WriteLine(str(numsel) + " selected objects")
lw.WriteLine("")

preSelFeatures = []

for x in range(numsel):
selobj = theUi.SelectionManager.GetSelectedTaggedObject(x)
# uncomment the following lines for more info
#nx_type, nx_subtype = theUfSession.Obj.AskTypeAndSubtype(selobj.Tag)
#lw.WriteLine("x: " + str(x))
#lw.WriteLine("Object: " + str(selobj))
#lw.WriteLine(" Tag: " + str(selobj.Tag))
#lw.WriteLine(" Type: " + str(nx_type))
#lw.WriteLine(" Subtype: " + str(nx_subtype))
#lw.WriteLine(" Journal identifier: " + selobj.JournalIdentifier)
lw.WriteLine(" NXOpen python type: " + selobj.__class__.__name__)

if isinstance(selobj, NXOpen.Features.Feature):
preSelFeatures.append(selobj)

lw.WriteLine("features pre-selected: " + str(len(preSelFeatures)))
lw.WriteLine("")

lw.Close()

if __name__ == '__main__':
main()

Thanks for the code