The parts list autoballoon function can be a great time-saver. Unfortunately, it often picks undesirable locations when attaching the leader arrow to the component of interest. The leader locations are easily moved, but when working quickly or on a cramped drawing it can be easy to unintentionally pick the edge of a neighboring component. When the parts list updates, the callouts update and you are left with a component that has no callout.
The goal of this journal is to limit the selection to the edges of the component the callout belongs to. The journal will prompt you to select a callout balloon, it will then allow you to pick an edge of the corresponding component. The balloon leader will be reattached to the component near your pick point.
Limitations
This journal will only work with "Pre-NX 8.5 Exact" views. If you are running NX 8.5 or higher and using the "Exact" drafting view type, this journal will NOT work for you.
This journal has not been thoroughly tested with the "extracted edges" or "snapshot" view options. It may or may not work in this case.
Occasionally, the leader attachment point will be a bit "off" from the pick point. This is an issue that I have not dug into; if it happens to you, try picking a slightly different location.
The Code
Other than being an occasionally handy utility, it is also a good example of filtering the user selection "on the fly".
'NXJournaling.com 'September 30, 2014 'Allow user to select new leader point for parts list balloon, 'limit selection to drawing objects of the balloon's component. 'This will eliminate inadvertently picking a different 'component when all you want to do is move the leader point. 'Only works for "Pre-NX 8.5 Exact" views. 'May not work with the "extracted edges -> snapshot" option (not tested). Option Strict Off Imports System Imports System.Windows.Forms Imports System.Collections.Generic Imports NXOpen Imports NXOpen.UF Module Module3 Dim theSession As Session = Session.GetSession() Dim theUfSession As UFSession = UFSession.GetUFSession Dim workPart As Part = theSession.Parts.Work Dim displayPart As Part = theSession.Parts.Display Dim lw As ListingWindow = theSession.ListingWindow Dim lg As LogFile = theSession.LogFile Dim curComp As Assemblies.Component = Nothing Dim dicSectionEdges As New Dictionary(Of Tag, Tag) Sub Main() lg.WriteLine("~~ Reassociate ID symbol ~~") lg.WriteLine(" timestamp: " & Now) If IsNothing(theSession.Parts.Work) Then 'active part required lg.WriteLine(" no active part, exiting journal") Return End If lw.Open() Const undoMarkName As String = "reassociate parts list callout" Dim markId1 As Session.UndoMarkId markId1 = theSession.SetUndoMark(Session.MarkVisibility.Visible, undoMarkName) Dim myIdSymbol As Annotations.IdSymbol Dim newSelPoint(2) As Double Dim newSelViewTag As Tag = Tag.Null Do Until SelectIdSymbol("select ID Symbol", myIdSymbol) = Selection.Response.Cancel lg.WriteLine(" waiting for user to select ID balloon...") If Not IsAutoBalloon(myIdSymbol) Then lg.WriteLine(" balloon selected is not part of a callout (does not belong to the parts list)") lg.WriteLine(" skip processing, prompt user to select a different balloon") 'warn user it is not used in callout 'MsgBox("Selected balloon is not a parts list callout") MessageBox.Show("Selected balloon is not a parts list callout." & ControlChars.NewLine & _ "Please select a callout balloon", "Invalid selection", MessageBoxButtons.OK, MessageBoxIcon.Information) Continue Do End If curComp = GetComponentOfBalloon(myIdSymbol) If IsNothing(curComp) Then lg.WriteLine(" component of balloon = Nothing") lg.WriteLine(" skip processing, prompt user to select different balloon") Continue Do Else lg.WriteLine(" component of balloon: " & curComp.DisplayName) End If lg.WriteLine(" prompting user to select new edge") Dim newRef As DisplayableObject = select_component_edge("select new edge", newSelPoint, newSelViewTag) If IsNothing(newRef) Then lg.WriteLine(" select_component_edge() returned Nothing, skip to next balloon selection") Continue Do End If lg.WriteLine(" selected object type: " & newRef.GetType.ToString) Dim selView As NXOpen.View = Utilities.NXObjectManager.Get(newSelViewTag) lg.WriteLine(" selected in view: " & selView.Name) lg.WriteLine(" selected point: " & newSelPoint(0) & ", " & newSelPoint(1) & ", " & newSelPoint(2)) Dim symPt As Point3d symPt.X = newSelPoint(0) symPt.Y = newSelPoint(1) symPt.Z = newSelPoint(2) ReassociateIdSymbol(myIdSymbol, newRef, selView, symPt) Loop lw.Close() End Sub Function SelectIdSymbol(ByVal prompt As String, ByRef selObj As TaggedObject) As Selection.Response lg.WriteLine(" Function SelectIdSymbol()") Dim theUI As UI = UI.GetUI Dim title As String = "Select an ID Symbol" Dim includeFeatures As Boolean = False Dim keepHighlighted As Boolean = False Dim selAction As Selection.SelectionAction = Selection.SelectionAction.ClearAndEnableSpecific Dim cursor As Point3d Dim scope As Selection.SelectionScope = Selection.SelectionScope.WorkPart Dim selectionMask_array(0) As Selection.MaskTriple With selectionMask_array(0) .Type = UFConstants.UF_drafting_entity_type .Subtype = UFConstants.UF_draft_id_symbol_subtype End With Dim resp As Selection.Response = theUI.SelectionManager.SelectTaggedObject(prompt, _ title, scope, selAction, _ includeFeatures, keepHighlighted, selectionMask_array, _ selObj, cursor) If resp = Selection.Response.ObjectSelected OrElse resp = Selection.Response.ObjectSelectedByName Then lg.WriteLine(" object selected") lg.WriteLine("") Return Selection.Response.Ok Else lg.WriteLine(" selection canceled") lg.WriteLine("") Return Selection.Response.Cancel End If End Function Function IsAutoBalloon(ByVal theIdSymbol As Annotations.IdSymbol) As Boolean lg.WriteLine(" Function IsAutoBalloon()") Dim theCallout As Tag = Nothing theUfSession.Drf.AskCalloutOfAnnotation(theIdSymbol.Tag, theCallout) If theCallout <> Tag.Null Then lg.WriteLine(" ID symbol is autoballoon") lg.WriteLine("") Return True Else lg.WriteLine(" ID symbol is NOT autoballoon") lg.WriteLine("") Return False End If End Function Function GetComponentOfBalloon(ByVal theIdSymbol As Annotations.IdSymbol) As Assemblies.Component lg.WriteLine(" Function GetComponentOfBalloon()") Dim myIdBuilder As Annotations.IdSymbolBuilder myIdBuilder = workPart.Annotations.IdSymbols.CreateIdSymbolBuilder(theIdSymbol) 'ignore IDSymbols with no leaders If myIdBuilder.Leader.Leaders.Length = 0 Then 'ID Symbol has no leaders myIdBuilder.Destroy() Return Nothing End If Dim tagObj1 As Annotations.LeaderData tagObj1 = myIdBuilder.Leader.Leaders.FindItem(0) Dim leaderData1 As Annotations.LeaderData = CType(tagObj1, Annotations.LeaderData) Dim theObject As DisplayableObject Dim theView As Drawings.DraftingView Dim thePoint As Point3d leaderData1.Leader.GetValue(theObject, theView, thePoint) myIdBuilder.Destroy() If theObject Is Nothing Then Return Nothing End If If theView.IsOutOfDate Then 'lw.WriteLine("view is out of date, updating...") theView.Update() End If 'lw.WriteLine("view up to date") If TypeOf (theView) Is Drawings.SectionView Then BuildSectionDictionary(theView) End If If theObject.IsOccurrence Then 'object is owned by a component (edge, face, etc) Return theObject.OwningComponent Else 'object is owned by the drawing (silhouette edge curve, section edge curve, extracted edge curve, etc) If TypeOf (theObject) Is NXOpen.Line Or TypeOf (theObject) Is NXOpen.Arc Or _ TypeOf (theObject) Is NXOpen.Spline Or TypeOf (theObject) Is NXOpen.Conic Then Dim groupTag As Tag Dim groupType As Integer Dim groupSubType As Integer 'determine if this is a section or silhouette curve theUfSession.Draw.AskGroupOfCurve(theObject.Tag, groupTag, groupType, groupSubType) Select Case groupType Case Is = UFConstants.UF_solid_silhouette_type 'silhouette type 201 Dim theFaceTag As Tag theUfSession.Draw.AskFaceOfSil(theObject.Tag, theFaceTag) Dim theFace As Face = Utilities.NXObjectManager.Get(theFaceTag) Return theFace.OwningComponent Case Is = UFConstants.UF_solid_section_type 'section curve type 198 Dim tempComp As Assemblies.Component tempComp = ComponentOfSectionEdge(theObject.Tag) Return tempComp Case Else Return Nothing End Select End If 'theObject is not a line, arc, conic, or spline Return Nothing End If End Function Sub ReassociateIdSymbol(ByVal theIdSymbol As Annotations.IdSymbol, ByVal newObj As DisplayableObject, _ ByVal newView As NXOpen.View, ByVal newPt As Point3d) Dim myIdBuilder As Annotations.IdSymbolBuilder myIdBuilder = workPart.Annotations.IdSymbols.CreateIdSymbolBuilder(theIdSymbol) 'ignore IDSymbols with no leaders If myIdBuilder.Leader.Leaders.Length > 0 Then Dim tagObj1 As Annotations.LeaderData tagObj1 = myIdBuilder.Leader.Leaders.FindItem(0) Dim leaderData1 As Annotations.LeaderData = CType(tagObj1, Annotations.LeaderData) leaderData1.Leader.SetValue(newObj, newView, newPt) End If myIdBuilder.Commit() myIdBuilder.Destroy() End Sub Public Function select_component_edge(ByVal prompt As String, ByRef theCursor() As Double, _ ByRef theView As Tag) As TaggedObject Dim response As Integer = 0 Dim user_data As System.IntPtr Dim selObj As Tag = Nothing theUfSession.Ui.LockUgAccess(UFConstants.UF_UI_FROM_CUSTOM) Dim curCursorView As Integer theUfSession.Ui.AskCursorView(curCursorView) Try theUfSession.Ui.SetCursorView(0) theUfSession.Ui.SelectWithSingleDialog("Select component: ", prompt, _ UFConstants.UF_UI_SEL_SCOPE_ANY_IN_ASSEMBLY, AddressOf init_proc_body, _ user_data, response, selObj, theCursor, theView) Finally theUfSession.Ui.SetCursorView(curCursorView) theUfSession.Ui.UnlockUgAccess(UFConstants.UF_UI_FROM_CUSTOM) End Try If Not selObj.Equals(Tag.Null) Then 'the SelectWithSingleDialog method returns a tag, 'return the object from the given tag Dim myEdgeCurve As TaggedObject theUfSession.Disp.SetHighlight(selObj, 0) myEdgeCurve = Utilities.NXObjectManager.Get(selObj) Return myEdgeCurve Else Return Nothing End If End Function Public Function init_proc_body(ByVal select_ As IntPtr, _ ByVal userdata As IntPtr) As Integer 'this function must have the same signature as UFUi.SelInitFnT Delegate 'setup mask to filter for edges or curves Dim num_triples As Integer = 7 Dim mask_triples(num_triples - 1) As UFUi.Mask mask_triples(0).object_type = UFConstants.UF_solid_type mask_triples(0).object_subtype = UFConstants.UF_UI_SEL_FEATURE_ANY_EDGE mask_triples(0).solid_type = UFConstants.UF_UI_SEL_FEATURE_ANY_EDGE mask_triples(1).object_type = UFConstants.UF_line_type mask_triples(1).object_subtype = UFConstants.UF_all_subtype mask_triples(2).object_type = UFConstants.UF_circle_type mask_triples(2).object_subtype = UFConstants.UF_all_subtype mask_triples(3).object_type = UFConstants.UF_conic_type mask_triples(3).object_subtype = UFConstants.UF_all_subtype mask_triples(4).object_type = UFConstants.UF_spline_type mask_triples(4).object_subtype = UFConstants.UF_all_subtype mask_triples(5).object_type = UFConstants.UF_solid_silhouette_type mask_triples(5).object_subtype = UFConstants.UF_all_subtype mask_triples(6).object_type = UFConstants.UF_section_edge_type mask_triples(6).object_subtype = UFConstants.UF_all_subtype theUfSession.Ui.SetSelMask(select_, _ UFUi.SelMaskAction.SelMaskClearAndEnableSpecific, _ num_triples, mask_triples) theUfSession.Ui.SetSelProcs(select_, AddressOf component_edge_filter, Nothing, userdata) Return UFConstants.UF_UI_SEL_SUCCESS End Function Public Function component_edge_filter(ByVal _object As Tag, _ ByVal type As Integer(), _ ByVal user_data As IntPtr, _ ByVal select_ As IntPtr) As Integer 'type, user_data, and select_ are unused, but this function must have 'the same signature as UFUi.SelFilterFnT Delegate Dim dwgObj As NXObject dwgObj = Utilities.NXObjectManager.Get(_object) If TypeOf (dwgObj) Is Edge Then If ReferenceEquals(dwgObj.OwningComponent, curComp) Then Return UFConstants.UF_UI_SEL_ACCEPT Else Return UFConstants.UF_UI_SEL_REJECT End If End If Dim groupTag As Tag Dim groupType As Integer Dim groupSubType As Integer 'determine if this is a section or silhouette curve theUfSession.Draw.AskGroupOfCurve(dwgObj.Tag, groupTag, groupType, groupSubType) Select Case groupType Case Is = UFConstants.UF_solid_silhouette_type 'silhouette type 201 Dim theFaceTag As Tag theUfSession.Draw.AskFaceOfSil(dwgObj.Tag, theFaceTag) Dim theFace As Face = Utilities.NXObjectManager.Get(theFaceTag) If ReferenceEquals(theFace.OwningComponent, curComp) Then Return UFConstants.UF_UI_SEL_ACCEPT Else Return UFConstants.UF_UI_SEL_REJECT End If Case Is = UFConstants.UF_solid_section_type 'section curve type 198 Dim tempComp As Assemblies.Component tempComp = ComponentOfSectionEdge(dwgObj.Tag) If ReferenceEquals(tempComp, curComp) Then Return UFConstants.UF_UI_SEL_ACCEPT Else Return UFConstants.UF_UI_SEL_REJECT End If Case Else Return UFConstants.UF_UI_SEL_REJECT End Select End Function Sub BuildSectionDictionary(ByVal sectionView As Drawings.SectionView) Dim sxSolidTags() As Tag Dim numSxSolids As Integer theUfSession.Draw.AskSxsolidsOfSxview(sectionView.Tag, Nothing, numSxSolids, sxSolidTags) For Each sxSolidTag As Tag In sxSolidTags Dim numSxEdges As Integer Dim sxEdgeTags() As Tag theUfSession.Draw.AskSxedgesOfSxsolid(sxSolidTag, numSxEdges, sxEdgeTags) For Each sxEdgeTag As Tag In sxEdgeTags dicSectionEdges.Add(sxEdgeTag, sxSolidTag) Next Next End Sub Function ComponentOfSectionEdge(ByVal sectionEdgeTag As Tag) As Assemblies.Component Dim tempComp As Assemblies.Component If dicSectionEdges.ContainsKey(sectionEdgeTag) Then Dim sxSolidTag As Tag = dicSectionEdges.Item(sectionEdgeTag) Dim solidTag As Tag theUfSession.Draw.AskSolidOfSection(sxSolidTag, solidTag) Dim tempSolid As Body = Utilities.NXObjectManager.Get(solidTag) tempComp = tempSolid.OwningComponent Return tempComp Else Return Nothing End If 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
