I don't understand English, so I use a translation site to create my sentences.
So, please understand that there may be inappropriate expressions.
This is a continuation of our efforts here.
https://forums.autodesk.com/t5/fusion-360-api-and-scripts/use-textcommands/m-p/9645717#M10758
I myself have a better understanding than before.
There are a number of commands that are not provided as an API, but can be used in conjunction with TextCommands.
One of them is a group of bodies.
This is illustrated by the data in this state.
Make the Bodies of RootComponent (marked in red) selected and execute the following TextCommands.
Commands.Start FusionCreateSurfaceGroupCommand
You can make a group without problems.
Then execute the TextCommands in the same way with the "Component1:1" Bodies (marked in blue) selected.
This one is also created without problems.
To perform these same processes, we created the following script.
# occ
occ :adsk.fusion.Occurrence = root.occurrences[0]
if occ.nativeObject:
print('{} has nativeObject'.format(occ.name))
else:
print('{} has not nativeObject'.format(occ.name))
createBodiesGroup(occ)
except:
if _ui:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def createBodiesGroup(
root_occ :adsk.core.Base
):
# select bodies
sels :adsk.core.Selections = _ui.activeSelections
sels.clear()
sels.add(root_occ.bRepBodies)
# execute TextCommands
_app.executeTextCommand('Commands.Start FusionCreateSurfaceGroupCommand')
Unfortunately, RootComponent creates it, but Occurrence(Component1:1) results in an error and cannot be created.
I do not know the cause of this error.
(Maybe it's because I don't understand NativeObject and Proxy correctly.)
However, I found that you can select correctly by using the TextCommands here.
Selections.Add <Paths>
API's Selections.Add method can only accommodate what is provided as an object in the API, but
In the case of the TextCommands is possible to state selected, even in entity that is not provided as an object in the API.
Eventually you need to get the <paths>.
To verify the <paths>, select the Bodies of "Component1:1" in the GUI and execute the following text command.
Selections.List
The numbers displayed may be different.
Each of these numbers seems to be represented as <EntityRef><EntityID>.
We could use this one to see the properties of the entity.
PEntity.Properties <EntityRef>
Let's try it out with "57".
The return value seems to be in JSON format.
Since it is tedious to check them one by one, we will create a script like this one to check them.
# Fusion360API Python script
import adsk.core, adsk.fusion, traceback
import json
import os
import neu_server
_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)
def run(context):
try:
global _app, _ui
_app = adsk.core.Application.get()
_ui = _app.userInterface
# check select count
sels :adsk.core.Selections = _ui.activeSelections
if sels.count < 1:
_ui.messageBox('Pre-select the elements to examine.')
return
if sels.count > 1:
_ui.messageBox('There should be only one entity to choose.')
return
dumpMsg('-- select info --')
# get paths
linesep = os.linesep
paths :str = _app.executeTextCommand(u'Selections.List').rstrip(linesep)
dumpMsg('<paths> : {}\n'.format(paths))
# split paths
ids = paths.split(':')
if len(ids) < 1:
return
# loop all ID
for id in ids:
try:
dumpMsg('prop -> : {}'.format(id))
# get property
# prop_txt = _app.executeTextCommand(u'PEntity.Properties {}'.format(id))
prop_txt = neu_server.get_entity_properties(id)
if len(prop_txt) < 1: continue
# to json
prop = json.dumps(prop_txt, indent=2)
dumpMsg(str(prop) + '\n')
except:
pass
except:
if _ui:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def dumpMsg(msg :str):
_ui.palettes.itemById('TextCommands').writeText(str(msg))
In my environment
prop_txt = _app.executeTextCommand(u'PEntity.Properties {}'.format(id))
with the same function because the output was not clean in
import neu_server
・・・
prop_txt = neu_server.get_entity_properties(id)
to use.
Since it is not practical to write out all the information because there is a lot of information to be output, this is what happens when you only write out the "interfaceId" key.
Also, I would like to add my prediction based on the name of the key.
57 : "Ns::Comp::ComponentInstance" - Occurrence?
3 : "Na::FusionComponent" - Component?
4 : "Ns::Comp::ComponentInstances" - Occurrences?
224 : "Ns::Comp::ComponentInstance" - Occurrence?
173 : "Na::FusionComponent" - Component?
176 : "Ns::BREP::Bodies" - bodies?
I didn't expect there to be a kind of Occurrence in the RootComponent as well, but it feels more natural.
(If you select only the root component and run the script, it only returns "57")
Also, if you look for the following entityID from each property, you will find it with a key like this
57 : "Ns::Comp::ComponentInstance"['rTargetComponent']['entityId'] -> 3
3 : "Na::FusionComponent"['rComponentBaseInstances']['entityId'] -> 4
4 : "Ns::Comp::ComponentInstances"['children']['entityId'] -> 224
224 : "Ns::Comp::ComponentInstance"['rTargetComponent']['entityId'] -> 173
173 : "Na::FusionComponent"['rBodies']['entityId'] -> 176
176 : "Ns::BREP::Bodies"
This way you have relative information.
The image in my head looks like this.
I could be wrong.
Red is the elements that can be selected in the GUI (mainly occlusion) and green is practically not shown.
Reference source (component). A collection of each element that is not even shown in blue.
I was surprised to find that the root component also has an occurrence.
Such a command also exists.
Also, entityID is
PAsset
PBody
PComponent
PDocument
PEntity
PInterfaces
PInstance
These commands are also available. (I'm sure there are others)
Now the main preparation is ready.
Select using the API.
Selections.Add <bodies>
Then get the <paths> using the TextCommands.
Selections.List
In TextCommands, we can use this <paths> to make a selection, but as shown in the first part, we cannot select the body of Occurrence(Component1:1) in the API selection.
So we select a selectable Occurrence(Component1:1) and get the <paths>.
Selections.List
57:3:4:224
Based on the entityID of "224", you can run "PEntity.Properties" to get the next required entityID.
・・・
"rOwningDeriveFeature": {},
"rTargetComponent": {
"segId": 25,
"entityId": 173,
"rootId": ""
},
"transform":
・・・
For TargetComponent, it is also valid here.
Based on these, let's fix the erroneous script.
# Fusion360API Python script
import adsk.core, adsk.fusion, traceback
import json
import os
import neu_server
_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)
def run(context):
try:
global _app, _ui
_app = adsk.core.Application.get()
_ui = _app.userInterface
des :adsk.fusion.Design = _app.activeProduct
root :adsk.fusion.Component = des.rootComponent
# root
createBodiesGroup(root)
# occ
occ :adsk.fusion.Occurrence = root.occurrences[0]
if occ.nativeObject:
print('{} has nativeObject'.format(occ.name))
else:
print('{} has not nativeObject'.format(occ.name))
createBodiesGroup(occ)
except:
if _ui:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def createBodiesGroup(
root_occ :adsk.core.Base
):
try:
global _app, _ui
# get Selections
sels :adsk.core.Selections = _ui.activeSelections
sels.clear()
# select root or occ
sels.add(root_occ)
# get root_occ <paths>
linesep = os.linesep
occPaths = _app.executeTextCommand(u'Selections.List').rstrip(linesep)
# get root_occ entityID
occId = str(occPaths).split(':')[-1]
# get root_occ property
propTxt = neu_server.get_entity_properties(occId)
prop = json.loads(json.dumps(propTxt))
# get target component entityID
compId = prop['rTargetComponent']['entityId']
# get component property
propTxt = neu_server.get_entity_properties(compId)
prop = json.loads(json.dumps(propTxt))
# get bodies entityID
bodiesId = prop['rBodies']['entityId']
# create bodies paths
bodiesPaths = '{}:{}:{}'.format(occPaths, compId, bodiesId)
# select bodies
sels.clear()
_app.executeTextCommand(u'Selections.add {}'.format(bodiesPaths))
# execute TextCommands
_app.executeTextCommand('Commands.Start FusionCreateSurfaceGroupCommand')
except:
if _ui:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
When we did this, the group was created successfully.
So far, we've served our purpose by preselecting the objects provided by the API and executing the commands. Next, let's look at an example using an object not provided by the API.
Since there have been inquiries on the forum in the past, we decide to make and modify the circular pattern of the sketch.
The actual operation in the GUI. First make a sketch and draw one circle.
Select the circle and the sketch origin to make the circle a circular pattern with the sketch origin as the center point.
If we call the circular pattern command in this state, unfortunately, both of them are captured as objects.
You can see that "preselect + command invocation" fails, but now we know how to make it work.
〇Find the CommandID
First, check the command ID.
You can also check it by the method previously described here.
https://forums.autodesk.com/t5/fusion-360-api-and-scripts/use-textcommands/m-p/9645688
This time, I will use this add-in that I made myself.
https://github.com/kantoku-code/Fusion360_Small_Tools_for_Developers/tree/master/CommandLogger
It's a simple matter of displaying the "Command Name : Command ID" called by the GUI operation in the TextCommands palette after launching the add-in, but I like it because I can check it as I go along.
The command ID for the circular pattern of the sketch is "CircularSketchPatternCommand".
〇Find the InputID of a dialog.
We then examine the InputID of each of the command dialogs.
Previously, we used this one from TextCommands, but some items were not displayed.
UI.CurrentCommandInfo
I searched for it and found that this command also displays the InputID.
Toolkit.cmdDialog
We can see that the InputID used in this case looks like this
Objects -> Geometries
Center -> CircularAxis
Quantity -> infoCircularGridEntryCount
〇Set the entity or value to a dialog
The way to do this without pre-selection was introduced here in the past using "ONK".
https://forums.autodesk.com/t5/fusion-360-api-and-scripts/use-textcommands/m-p/9645717#M10758
I have found another way to do it.
UI.EnableCommandInput <InputID>
Using this function, you can move the focus to the specified InputID.
(However, it is limited to the Input for selection)
Move the focus to an arbitrary Input and add
userInterface.activeSelections.add(<API Object>)
to select the desired element, and you have assigned it to the Input.
# get selections
sels :adsk.core.Selections = _ui.activeSelections
sels.clear()
・・・
# set Objects
_app.executeTextCommand(u'UI.EnableCommandInput {}'.format('Geometries'))
sels.add(tragetEntity)
The "Objects" and "Center Point" is resolved in this.
As for the "Quantity," we already solved that last time, so here it is.
# set Quantity
_app.executeTextCommand(u'Commands.SetDouble infoCircularGridEntryCount 5')
After that, just run it and the circular pattern will be created successfully.
〇Find the circular pattern entityID.
From here, we will modify the circular pattern created in the above steps.
The procedure to modify is "preselect + call modify command".
The command ID for the modification was done using the same add-in as in the previous step.
The CommandID is "SketchPatternCircularEdit".
Next, we need to select the "Circular Pattern", which unfortunately is not provided as an object in the API.
We need to find <paths> as we did when we selected bodies.
I used the following procedure to find it.
First, select "Circular Pattern" in the GUI, then use the TextCommands to get <paths>.
It will be ①.
Selections.List
57:3:12:173:211
(The numbers themselves may be different from yours)
The entityID of the "Circular Pattern" is "211". We can also see that its parent entityID is "173".
For now, we'll check the "211" property and find "173", which means we can get the parent entity with the "rParentNode" key. It will be ②.
Next, let's check the "173" property." interfaceId" is "SKs::Geometry::MSketch".
So, this is probably the sketch. It will be ③.
Find out "211" from this property. It will be ④.
You can see that it is in the "constraints" key. It will be ⑤.
# select Sketch
sels.clear()
sels.add(skt)
# Get <paths> from Sketch
linesep = os.linesep # Get the line feed code for each os.
sktPaths = _app.executeTextCommand(u'Selections.List').rstrip(linesep)
# Get entityID from Sketch
oktId = str(sktPaths).split(':')[-1] # The last number is the entityID.
# Get Property
propTxt = neu_server.get_entity_properties(oktId)
sktProp = json.loads(json.dumps(propTxt))
# Get CircularSketchPattern entityID
# Normally, you should check "interfaceId" and so on, but we have simplified it.
circularSketchPatternId = sktProp['constraints'][-1]['entityId']
There is only one "circular pattern" in this sketch, so it is displayed as shown in the image, but the "constraints" key is essentially a list, and multiple values can exist.
If you create another circular pattern and look at the properties, you will see the output like this.
〇Select a circular pattern.
Once you get to this point, the selection itself is easy.
Just attach the circular pattern entityID to the sketch <paths> and use the text command Selections.add.
# CircularSketchPattern paths
circularSketchPatternPaths = sktPaths + ':' + str(circularSketchPatternId)
# Circular Pattern
sels.clear()
_app.executeTextCommand(u'Selections.add {}'.format(circularSketchPatternPaths))
〇Modify the settings
After that, call "SketchPatternCircularEdit" to display the command dialog.
You can modify the Input as you did when you created the circular pattern.
〇summary
The whole process can be summarized as follows.
# Fusion360API Python script
import adsk.core, adsk.fusion, traceback
import os
import json
import neu_server
_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)
def run(context):
try:
global _app, _ui
_app = adsk.core.Application.get()
_ui = _app.userInterface
des :adsk.fusion.Design = _app.activeProduct
# get active sketch
skt :adsk.fusion.Sketch = des.activeEditObject
if not skt:
return
# get entity
tragetEntity :adsk.fusion.SketchEntity = skt.sketchCurves.sketchCircles[0]
circularAxis :adsk.fusion.SketchPoint = skt.originPoint
# get selections
sels :adsk.core.Selections = _ui.activeSelections
sels.clear()
# call CircularSketchPatternCommand
_app.executeTextCommand(u'Commands.Start CircularSketchPatternCommand')
# set Objects
_app.executeTextCommand(u'UI.EnableCommandInput {}'.format('Geometries'))
sels.add(tragetEntity)
# set Center Point
_app.executeTextCommand(u'UI.EnableCommandInput {}'.format('CircularAxis'))
sels.add(circularAxis)
# set Quantity
_app.executeTextCommand(u'Commands.SetDouble infoCircularGridEntryCount 5')
# Comimit Command
_app.executeTextCommand(u'NuCommands.CommitCmd')
# Message
_ui.messageBox('Modify the Circular Pattern.')
# -- Start editing --
# select Sketch
sels.clear()
sels.add(skt)
# Get <paths> from Sketch
linesep = os.linesep # Get the line feed code for each os.
sktPaths = _app.executeTextCommand(u'Selections.List').rstrip(linesep)
# Get entityID from Sketch
oktId = str(sktPaths).split(':')[-1] # The last number is the entityID.
# Get Property
propTxt = neu_server.get_entity_properties(oktId)
sktProp = json.loads(json.dumps(propTxt))
# Get CircularSketchPattern entityID
# Normally, you should check "interfaceId" and so on, but we have simplified it.
circularSketchPatternId = sktProp['constraints'][-1]['entityId']
# CircularSketchPattern paths
circularSketchPatternPaths = sktPaths + ':' + str(circularSketchPatternId)
# Circular Pattern
sels.clear()
_app.executeTextCommand(u'Selections.add {}'.format(circularSketchPatternPaths))
# Call Edit Circular Pattern command
_app.executeTextCommand(u'Commands.Start SketchPatternCircularEdit')
# Change quantity
_app.executeTextCommand(u'Commands.SetDouble infoCircularGridEntryCount 7')
# Comimit Command
_app.executeTextCommand(u'NuCommands.CommitCmd')
except:
if _ui:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
It is much more time consuming than API development alone, but it also allows you to select entity that are not provided as objects in the API, allowing you to create things that you could not create with the API alone.
Thank you for reading my long explanation until the end. I sincerely hope it will be useful for any add-ins you create.
It is thanks to @thomasa88 that I was able to deepen my understanding so far.
Thank you very much for the great hint you gave me.
I found out how to change the value of "eDropDownButton", which I hadn't known how to do for a long time.
"eDropDownButton" is a CommandInputs like this.
The final step is to modify the pipe command here.
https://forums.autodesk.com/t5/fusion-360-api-and-scripts/use-textcommands/m-p/9645688
Find out the ID of Inputs in advance.
Execute this command while the command dialog is displayed.
Toolkit.cmdDialog
For more information about "Toolkit.cmdDialog", please refer to here.
https://forums.autodesk.com/t5/fusion-360-api-and-scripts/use-textcommands2/m-p/9937199#M12120
You will see that the Inputs ID looks like this.
The value of the "eDropDownButton" can be changed with the following command
Commands.SetString <InputsID> <MenuItems ID>
For example, if you want the "section" to be a square, you would use
Commands.SetString infoSectionType infoRectangular
Finally, we modify the pipe commands to be more controlled.
#Fusion360API python script
#Author-kantoku
#Description-create PipeFeature sample
import adsk.core, adsk.fusion, traceback
_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)
def run(context):
try:
global _app, _ui
_app = adsk.core.Application.get()
_ui = _app.userInterface
# new doc
_app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType)
des :adsk.fusion.Design = _app.activeProduct
des.designType = adsk.fusion.DesignTypes.ParametricDesignType
root :adsk.fusion.Component = des.rootComponent
# create sketch
crv = initSktCircle(root)
# create pipe
initPipe(crv, 'infoRectangular', 'infoNewBodyType')
# create sketch
crv = initSktSpline(root)
# create pipe
initPipe(crv, 'infoTriangular', 'infoNewBodyType')
except:
if _ui:
_ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def initSktSpline(comp :adsk.fusion.Component):
skt :adsk.fusion.Sketch = comp.sketches.add(comp.xYConstructionPlane)
poss = [[-1,2,0], [2,1,0], [-3,-4,0]]
pnt3D = adsk.core.Point3D
objs = adsk.core.ObjectCollection.create()
[objs.add(pnt3D.create(x,y,z)) for (x,y,z) in poss]
crvs :adsk.fusion.SketchCurves = skt.sketchCurves
crv = crvs.sketchFittedSplines.add(objs)
return crv
def initSktCircle(comp :adsk.fusion.Component):
skt :adsk.fusion.Sketch = comp.sketches.add(comp.xYConstructionPlane)
pnt3D = adsk.core.Point3D
crvs :adsk.fusion.SketchCurves = skt.sketchCurves
crv = crvs.sketchCircles.addByCenterRadius(pnt3D.create(-5.0,-5,0), 4.0)
return crv
def initPipe(
path,
infoSectionType = 'infoCircular',
infoBooleanType = 'infoNewBodyType'):
sels :adsk.core.Selections = _ui.activeSelections
sels.clear()
sels.add(path)
txtCmds = [
u'Commands.Start PrimitivePipe', # show dialog
u'Commands.SetDouble SWEEP_POP_ALONG 1.0', # input distance
u'Commands.SetDouble SectionRadius 0.5', # input radius
u'Commands.SetString infoSectionType {}'.format(infoSectionType),
u'Commands.SetString infoBooleanType {}'.format(infoBooleanType),
u'NuCommands.CommitCmd' # execute command
]
for cmd in txtCmds:
_app.executeTextCommand(cmd)
sels.clear()
The result is shown here.
The "eDropDownButton" is used in so many command dialogs that not being able to change it would be very inconvenient.
This may allow you to incorporate functionality into your scripts/add-ins that you have previously given up on.
When I want to display a small message while a script/add-in is running, I output it to the TextCommands palette (Application.log Method) because MessageBox stops the process and ProgressDialog takes time to prepare.
However, for a long time I could not figure out how to clear the TextCommands palette.
I knew that executing the TextCommand here in the GUI would clear it.
Window.Clear
However, the executeTextCommand method does not clear it.
Recently, I found a way to clear it.
app.log(
app.executeTextCommand(u'window.Clear')
)
or
app.log(u'TextCommandWindow.Clear')
Can't find what you're looking for? Ask the community or share your knowledge.