API, Assembly traversal, grab children attribute
API, Assembly traversal, grab children attribute
(OP)
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
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






RE: API, Assembly traversal, grab children attribute
RE: API, Assembly traversal, grab children attribute
childlevel_temp = ModDoc.GetCustomInfoValue("", Level)
Hope this gets it for you!
RE: API, Assembly traversal, grab children attribute
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
RE: API, Assembly traversal, grab children attribute
Two possible fixes:
CODE
Dim childlevel_temp As Long
CODE
RE: API, Assembly traversal, grab children attribute
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
RE: API, Assembly traversal, grab children attribute
CODE
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.
RE: API, Assembly traversal, grab children attribute
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 PropName as String
PropName = "Level"
PropValue =swChildComp.GetCustomInfoValue("", PropName)
Also keep in mind that property names are case-sensitive.
RE: API, Assembly traversal, grab children attribute
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
RE: API, Assembly traversal, grab children attribute
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!
RE: API, Assembly traversal, grab children attribute
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
RE: API, Assembly traversal, grab children attribute
RE: API, Assembly traversal, grab children attribute
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
RE: API, Assembly traversal, grab children attribute
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?
RE: API, Assembly traversal, grab children attribute
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).
RE: API, Assembly traversal, grab children attribute
RE: API, Assembly traversal, grab children attribute
I have posted the working version of my macro for you to sample if you need it.
-Shaggy
CODE
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
RE: API, Assembly traversal, grab children attribute
RE: API, Assembly traversal, grab children attribute
RE: API, Assembly traversal, grab children attribute
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.
RE: API, Assembly traversal, grab children attribute
The question now is: In TraverseComponent I want to pass control to a form and stop execution until form is updated and processed. Then return to TraverseComponent for next component. What happens now is the form is loaded and shown then without waiting or input, the execution returns to TraverseComponent and continues the loop until the end.
How do I keep the control to the form, until I hit an "Ok" button or so.
RE: API, Assembly traversal, grab children attribute
frmCustomProperties.txtTitle.SetFocus
While frmCustomProperties.Visible
DoEvents
Wend
Of course, the final line of the "OK" button's OnClick event would be Me.Hide, which hides the form, turning its "Visible" property to "False".
Alternately, in the properties of the form itself you can specify its Modality. I believe you can make it either Application Modal, which freezes all SW functionality while it's visible, or System Modal, which freezes your system until it's hidden.
RE: API, Assembly traversal, grab children attribute
From MSDN Library:
So you are right and you deserve another star.
RE: API, Assembly traversal, grab children attribute
Bradley
RE: API, Assembly traversal, grab children attribute
CODE
If (Not bExclude) Then Call StartForm
My expectation was that when the boolean bExclude returns True, the Call StartForm will not be executed. But it is!!!
If I modify the code like this to explicitly set bExclude value,
CODE
bExclude = True
If (Not bExclude) Then Call StartForm
the Call StartForm is skipped, as expected
RE: API, Assembly traversal, grab children attribute
Obviously there's another hurdle. My "Part" in the code above is a ModelDoc2 type (or so I think). So
ModelDoc2.DeleteCustomInfo2..
ModelDoc2.AddCustomInfo3..
ModelDoc2.CustomInfo2...
should work. The last one does, the first one doesn't. I don't get an error, it's just doesn't delete the properties. It's not a "case" issue.