Continue to Site

Eng-Tips is the largest engineering community on the Internet

Intelligent Work Forums for Engineering Professionals

  • Congratulations IRstuff on being selected by the Eng-Tips community for having the most helpful posts in the forums last week. Way to Go!

API, Assembly traversal, grab children attribute 4

Status
Not open for further replies.

ShaggyPE

Mechanical
Sep 8, 2003
1,127
I am trying to do a seemingly simple task that is to be a portion of a much larger macro. I have created several test components and assemblies. The children of the top assy have all been assigned a custom property called "Level". The value associated with "Level" is 0 or 1. The function of this macro is to traverse through the children of the already open top assy, looking at the Level attribute. It will then create and assign a Level attribute to the top assy. The value of this attribute will be one higher than the highest of the children components.

The area I am having trouble with is grabbing the value of the attribute from the children. I downloaded a sample macro called TraverseAssembly to help me creating this one. The sample worked well to output the name of the children, I want to dive just a bit deeper. Below is my code. In bold is the line of code where I receive an error. Any help on this would be appreciated. Mind you I don't have much of a background in code so specific info is appreciated.
-Shaggy


Const swDocASSEMBLY = 2

Dim swApp As SldWorks.SldWorks
Dim ModelDoc As SldWorks.ModelDoc2
Dim Level As Integer
Dim strText As Variant

Sub Main()

Dim swRootComp As SldWorks.Component2
Dim swConf As SldWorks.configuration
Dim FileName As String

Set swApp = Application.SldWorks
Set ModelDoc = swApp.ActiveDoc


If Not ModelDoc Is Nothing Then
If ModelDoc.GetType = swDocASSEMBLY Then
'Get the active configuration
Set swConf = ModelDoc.GetActiveConfiguration
Set swRootComp = swConf.GetRootComponent
'Traverse the assembly
TraverseComponent swRootComp
End If
End If
ModelDoc.AddCustomInfo3 "", Level, swCustomInfoText, ""
ModelLevel = childlevel + 1
ModelDoc.CustomInfo2("", Level) = ModelLevel

End Sub


Sub TraverseComponent(swComp As SldWorks.Component2)
'this recursively traverses all of the components in an assembly
Dim vChildComp As Variant
Dim swChildComp As SldWorks.Component2
Dim i As Long
Dim childlevel As Long
Dim childlevel_temp As Long
Dim ModDoc As Object ' ModelDoc2 of child component
Dim ModName As String


vChildComp = swComp.GetChildren
childlevel_temp = 0
childlevel = 0
For i = 0 To UBound(vChildComp)
Set swChildComp = vChildComp(i)
ModName = swChildComp.Name
Set ModDoc = swChildComp.GetModelDoc()
childlevel_temp = swChildComp.GetCustomInfoValue("", Level)
If childlevel_temp > childlevel Then
childlevel = childlevel_temp
End If
Next i
End Sub
 
Replies continue below

Recommended for you

When I build a recursive Sub or Function that does things like travers an assembly, I build the level counter into the code as one of the arguments.
 
I'm not in front of SWX right now, but it looks like the line immediately above your bolded line gets the ModelDoc2 object for the current child component. That's the object you need to hit for the GetCustomInfoValue function. So you would change your bolded line to:

childlevel_temp = ModDoc.GetCustomInfoValue("", Level)

Hope this gets it for you!
 
handleman,
Thank you for the input. I attempted the change that you suggested, the same issue arose. When I ran the macro, the error popped up giving me the opportunity to debug.

Tick,
I am not quite clear what you mean. I do not want to dig any deeper than one level.

-Shaggy
 
I think I found your problem. CustomInfo values are returned as strings. The variables you are using to retrieve the CustomInfo values are Long.

Two possible fixes:
Code:
Dim childlevel As Long
Dim childlevel_temp As Long
-OR-
Code:
childlevel_temp = cLng(swChildComp.GetCustomInfoValue("", Level))
 
Tick,
I have tried both of your suggestions (the first one, I assumed you meant I should change the "Long" to "string" because both were previously dim as Long. That didn't work. I also tried changing the childlevel_temp as you described. That didn't work either.

The error I get is a Run Time error 438. The description is Object doesn't support this property or method.

I am using SW2005 if that makes a difference.

Thanks again for the assistance everyone.
-Shaggy
 
Sorry, just noticed something. I believe that Level needs to be in quotes, as it is a string. So your line would be:

Code:
childlevel_temp = cLng(swChildComp.GetCustomInfoValue("", "Level"))

Looking a bit deeper, you have a module level variable called Level that's defined as an integer. You're also trying to use the word "Level" as a string several places without quotes around it. So every time your macro sees the word Level without quotes it puts in the value of the integer variable "Level", which may be messing you up in a lot of ways. For example, this line

ModelDoc.AddCustomInfo3 "", Level, swCustomInfoText, ""

adds a custom property that is named whatever the current value of Level is. If you want to add a custom property named Level then you would need to have

ModelDoc.AddCustomInfo3 "", "Level", swCustomInfoText, ""

I'm about 99% confident that this is the main problem area. I'm at home, so I don't have access to SW right now to verify. My PC just doesn't have the power to run SW like my dual Xeon with 3G of ram, so it would be more aggravating than helpful. :)
 
Sorry, you are correct. I meant "String" and not "Long". Too quick to move on after the cut-and-paste.

I think handleman has found the other half of the problem. The name of the property is a string, and should be in quotes to be a string value.

Code:
Dim PropValue as String
Dim PropName as String
PropName = "Level"
PropValue =swChildComp.GetCustomInfoValue("", PropName)

Also keep in mind that property names are case-sensitive.
 
Tick and handleman,
Thank you for your contribution. Your input has allowed me to move forward on this macro. However I now have a couple new issues. I added a little msgBox so I could be sure that the macro was traversing and grabbing the child's "Level" attribute. It is. However when it completes the TraverseComponent sub routine I believe it loses the "knowledge" of the childlevel variable. When it gets back into the Main() it is supposed to add 1 to the childlevel to define the ModelLevel. It does not. It simply thinks childlevel is zero and therefore Modellevel is 1. Is there some special way that I need to carry that variable back into the Main()?

The second problem seems very basic and I am not sure why it isn't working. The ModelDoc.AddCustomInfo3 "", Level, swCustomInfoNumber, "" is not working. I can't create the new attribute in the parent. I think it may have something to do with the children components becoming the active doc while they were being searched through. How do I re-activate the parent doc so i can create the attribute? I tried Modeldoc.ActivateDoc2 function. That didn't seem to work. I may have used it wrong however.

Below is a copy of the macro. In bold are the areas that I believe are causing problems now. Italicized are the areas that I changed based on your input. Thanks again for your effort.
-Shaggy

Const swDocASSEMBLY = 2

Dim swApp As SldWorks.SldWorks
Dim ModelDoc As SldWorks.ModelDoc2
Dim strText As Variant

Sub Main()

Dim swRootComp As SldWorks.Component2
Dim swConf As SldWorks.configuration
'Dim FileNum As Integer
'Dim FS As Scripting.FileSystemObject
Dim FileName As String
Dim ModelLevel As Integer
Dim childlevel As Integer
Set swApp = Application.SldWorks
Set ModelDoc = swApp.ActiveDoc


If Not ModelDoc Is Nothing Then
If ModelDoc.GetType = swDocASSEMBLY Then
FileName = ModelDoc.GetTitle
'Get the active configuration
Set swConf = ModelDoc.GetActiveConfiguration
Set swRootComp = swConf.GetRootComponent
'Traverse the assembly
TraverseComponent swRootComp
End If
End If
'If AssySkipped = True Then
'MsgBox ("ASSEMBLY FILE SKIPPED")
'Else
'MsgBox ("The Active Doc is " & ModelDoc)
ModelDoc.AddCustomInfo3 "", "Level", swCustomInfoNumber, ""
'MsgBox (childlevel)
ModelLevel = (childlevel + 1)
ModelDoc.CustomInfo2("", "Level") = ModelLevel
MsgBox ("New Level for Parent is " & ModelLevel)
MsgBox ("ASSEMBLY FILE NOT SKIPPED")
'End If

End Sub


Sub TraverseComponent(swComp As SldWorks.Component2)
'this recursively traverses all of the components in an assembly
Dim vChildComp As Variant
Dim swChildComp As SldWorks.Component2
Dim i As Long
Dim childlevel As String
Dim childlevel_temp As String
Dim ModDoc As Object ' ModelDoc2 of child component
Dim ModName As String


vChildComp = swComp.GetChildren
childlevel_temp = 0
childlevel = 0
For i = 0 To UBound(vChildComp)
Set swChildComp = vChildComp(i)
ModName = swChildComp.Name
Set ModDoc = swChildComp.GetModelDoc()
childlevel_temp = CInt(ModDoc.GetCustomInfoValue("", "Level"))
'MsgBox ("childlevel for " & ModName & " is " & childlevel_temp)
If childlevel_temp > childlevel Then
childlevel = childlevel_temp
End If
Next i
End Sub

 
Your current issue is a problem with the scope of your variables. You have three variables that are declared at the very beginning, before any sub. These are "Module level".

Dim swApp As SldWorks.SldWorks
Dim ModelDoc As SldWorks.ModelDoc2
Dim strText As Variant

That means that any subroutine in the module can read or write to them. The rest of your variables are declared inside subroutines. Any variable declared inside a subroutine or function can only be seen by that subroutine or function. You can declare a variable with the same name in two different subs or functions, but they won't be the same variable. That's why your "Main" macro doesn't see the value of "childlevel". If you want "childlevel" to be the same variable in both subs then declare it up with the other three.

I'm not sure why the AddCustomInfo3 isn't working. I don't recall the syntax right now. It's not an issue of the wrong model being "active". Once you have a ModelDoc2 object it stays there until you either set the variable to some other value or it goes out of scope. You can verify pretty easily that you still have the right one by using a line like:

MsgBox ModelDoc.GetTitle

It looks like you tried to do that with

'MsgBox ("The Active Doc is " & ModelDoc)

but it probably errored out, since the message box can't display an object.

Good luck!

 
handleman,
Thanks again for the help. I was able to get the macro to work. I put the declaration for childlevel at the very top. I was able to get the macro to create the custom property by changing the ModelDoc.AddCustomInfo3 "", "Level", swCustomInfoNumber, "" line to ...swCustomInfoText. I am not sure however why Number didn't work. Doesn't really seem to matter though.

I am sure I will have future questions as this is only a portion of the actual macro I am trying to create. Your help as well as TheTick's help is greatly appreciated.

Also, I have not searched it, but what are the recommended books for learning SW API and/or Visual Basic?

-Shaggy
 
Sounds like you're going about it in the same way I did - a lot of not succeeding at first and trying, trying again. The SW API help is pretty good once you have a basic understanding of VBA. However, it looks like you're fairly new at VBA as well. I got my VBA a little bit at a time, mostly trial and error and help files. Perhaps someone else can suggest a resource to fill your head with VBA knowledge without having to crack it open banging it on your desk first. :)
 
Thanks handleman.

I did a search on Amazon and found a book called Automating Solidworks with Macros by Mike Spens. I think it should clue me in a bit more. I am presently using the SW API help quite a bit with some success. That and downloading sample macros to see how they work. I am brand new to VBA and I think that is where I am having the most difficulty. But I am chugging along.

Thanks again,
-Shaggy
 
handleman, Tick, Shaggy

1. Is it possible to create a custom property in the other direction, by reading it from the top level assy and then traverse the assembly and adding it to every children?

2. In what way is Shaggy's TraverseComponent routine recursive?
 
1. Sure. The macro would be real similar to (Shaggy's working version of) the macro above.

2. It is recursive by:

a. Get all children of the parent component (the assembly) with the line vChildComp = swComp.GetChildren. This returns an array with all the child components.
b. Looping through the children with the "For" loop starting with For i = 0 To UBound(vChildComp).
 
dogarila,
I have posted the working version of my macro for you to sample if you need it.
-Shaggy

Code:
Const swDocASSEMBLY = 2

Dim swApp As SldWorks.SldWorks
Dim ModelDoc As SldWorks.ModelDoc2
Dim childlevel As Integer
Dim strText As Variant

Sub Main()

    Dim swRootComp As SldWorks.Component2
    Dim swConf As SldWorks.configuration
    Dim ModelLevel As Integer
    Set swApp = Application.SldWorks
    Set ModelDoc = swApp.ActiveDoc
    
    
    If Not ModelDoc Is Nothing Then
        If ModelDoc.GetType = swDocASSEMBLY Then
            Set swConf = ModelDoc.GetActiveConfiguration
            Set swRootComp = swConf.GetRootComponent
            'Traverse the assembly
            TraverseComponent swRootComp
        End If
    End If
    'If AssySkipped = True Then
    'MsgBox ("ASSEMBLY FILE SKIPPED")
    'Else
    ModelDoc.AddCustomInfo3 "", "Level", swCustomInfoText, ""
    ModelLevel = (childlevel + 1)
    ModelDoc.CustomInfo2("", "Level") = ModelLevel
    MsgBox ("ASSEMBLY FILE NOT SKIPPED")
    'End If
        
End Sub


Sub TraverseComponent(swComp As SldWorks.Component2)
    'this recursively traverses all of the components in an assembly
    Dim vChildComp                  As Variant
    Dim swChildComp                 As SldWorks.Component2
    Dim childlevel_temp             As String
    Dim ModDoc                      As Object   ' ModelDoc2 of child component
    Dim ModName                     As String
    

    vChildComp = swComp.GetChildren
    childlevel_temp = 0
    childlevel = 0
    For i = 0 To UBound(vChildComp)
        Set swChildComp = vChildComp(i)
        ModName = swChildComp.Name
        Set ModDoc = swChildComp.GetModelDoc()
        childlevel_temp = CInt(ModDoc.GetCustomInfoValue("", "Level"))
           If childlevel_temp > childlevel Then
              childlevel = childlevel_temp
           End If
    Next i
End Sub
 
In the code above, in the TraverseComponent subroutine, in the For.. Next loop, I load a form with some custom properties, show it, do some changes in the form, save the changes and unload the form. I expect the program will return to the TraverseComponent and continue with next element. It does not do that. It just stops. I wonder why?
 
Try showing/hiding the form rather than loading and unloading it. Of course, if you do that you will need a form resetting sub that resets the form to its default values.
 
It doesn't stop anymore. Actually it doesn't stop at all. Shows the form. Hides the form. Shows the form, hides the form by itself until all the components in the assembly are traversed.

Now here's another question:

Code:
Option Explicit
Public docType As Integer
Public swApp As SldWorks.SldWorks
Public swModel As SldWorks.ModelDoc2
Dim returnOK As Boolean
Public Part As Object 

Private Sub Main()

Dim swRootComp As SldWorks.Component2
Dim swConf As SldWorks.Configuration


Set swApp = CreateObject("SldWorks.Application")
swApp.Visible = True
Set swModel = swApp.ActiveDoc

If swModel Is Nothing Then
    Call MsgBox("A SolidWorks document needs to be loaded!", vbExclamation, "Custom Properties")  ' Display error message
    returnOK = False
    swApp.Visible = True

    End                    ' If no model currently loaded, then exit
    Else
     docType = swModel.GetType
     If (docType = swDocASSEMBLY) Then
        Set swConf = swModel.GetActiveConfiguration
        Set swRootComp = swConf.GetRootComponent
        'Traverse assembly
        TraverseComponent swRootComp
        
 
        Else
        MsgBox ("File is not assembly")
        End 'Exit if file is not assembly
            'Code for parts and drawings to be added later
            

    End If
End If

End Sub

Sub TraverseComponent(swComp As SldWorks.Component2)
    'this recursively traverses all of the components in an assembly
    Dim vChildComp                  As Variant
    Dim swChildComp                 As SldWorks.Component2
    Dim ModName                     As String
    

    vChildComp = swComp.GetChildren

    For i = 0 To UBound(vChildComp)
        Set swChildComp = vChildComp(i)
        ModName = swChildComp.Name

        Set Part = swChildComp.GetModelDoc()
        Call StartForm

    Next i
End Sub

Sub StartForm()

Load frmCustomProperties()
frmCustomProperties.Show
frmCustomProperties.txtTitle.SetFocus

End Sub

The problem now is how to transmit "Part" trough StartForm() to Form_Load() in form code. I declared "Public Part as Object" but the Form_Load() still doesn't see it.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor