I am trying to create a simple two side extrusion on a selected edge or line.
I have a preview button, and the preview handler works fine. However, when I click execute, the component is only created but fails to get the selected line.
In the snippet code (entry.py) writen below, I have 2 functions: drawSomething(args) which use the selected line/edge to create the extrusion, and drawSomething2(args) which create a component and extrusion hardcoded. One does not work while the other is OK.
Where am I wrong? After couple of days I am stuck on this issue.
import adsk.core
import adsk.fusion
import os
from ...lib import fusion360utils as futil
from ... import config
app = adsk.core.Application.get()
ui = app.userInterface
CMD_ID = f'{config.COMPANY_NAME}_{config.ADDIN_NAME}_duplicate_components'
CMD_NAME = 'Create Extrusion '
CMD_Description = 'Create a an Extrusion as new component'
IS_PROMOTED = True
# Working in DESIGN
WORKSPACE_ID = 'FusionSolidEnvironment'
PANEL_ID = 'SolidScriptsAddinsPanel' #TODO Change later# PANEL_ID = 'SolidCreatePanel'
COMMAND_BESIDE_ID = 'ScriptsManagerCommand'
ICON_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources', '')
local_handlers = []
selectedEdges = []
# constants
DROPDOWN_GROUP_ID = 'drowdown_group'
DROPDOWN_ID = 'dropdown' #TODO
LINE_SELECTION = 'lineSelection'
PREVIEW_GROUP_ID = 'preview_group'
SHOW_PREVIEW_INPUT = 'show_preview'
SHOW_PREVIEW_MANUAL_INPUT = 'show_preview_manual'
def start():
# Create a command Definition.
cmd_def = ui.commandDefinitions.addButtonDefinition(CMD_ID, CMD_NAME, CMD_Description, ICON_FOLDER)
# Add command created handler. The function passed here will be executed when the command is executed.
futil.add_handler(cmd_def.commandCreated, command_created)
# ******** Add a button into the UI so the user can run the command. ********
# Get the target workspace the button will be created in.
workspace = ui.workspaces.itemById(WORKSPACE_ID)
# Get the panel the button will be created in.
panel = workspace.toolbarPanels.itemById(PANEL_ID)
# Create the button command control in the UI after the specified existing command.
control = panel.controls.addCommand(cmd_def, COMMAND_BESIDE_ID, False)
# Specify if the command is promoted to the main toolbar.
control.isPromoted = IS_PROMOTED
# Executed when add-in is stopped.
def stop():
# Get the various UI elements for this command
workspace = ui.workspaces.itemById(WORKSPACE_ID)
panel = workspace.toolbarPanels.itemById(PANEL_ID)
command_control = panel.controls.itemById(CMD_ID)
command_definition = ui.commandDefinitions.itemById(CMD_ID)
# Delete the button command control
if command_control:
command_control.deleteMe()
# Delete the command definition
if command_definition:
command_definition.deleteMe()
def command_created(args: adsk.core.CommandCreatedEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME} Command Created Event')
# TODO Create the event handlers you will need for this instance of the command
futil.add_handler(args.command.execute, command_execute, local_handlers=local_handlers)
futil.add_handler(args.command.inputChanged, command_input_changed, local_handlers=local_handlers)
futil.add_handler(args.command.executePreview, command_preview, local_handlers=local_handlers)
futil.add_handler(args.command.validateInputs, command_validate_input, local_handlers=local_handlers)
futil.add_handler(args.command.destroy, command_destroy, local_handlers=local_handlers)
# Create the user interface for your command by adding different inputs to the CommandInputs object
# https://help.autodesk.com/view/fusion360/ENU/?contextId=CommandInputs
inputs = args.command.commandInputs
# TODO ******************************** Define your UI Here ********************************
i1 = inputs.addSelectionInput(LINE_SELECTION, 'Edge', 'Please select a straight line or edge')
i1.setSelectionLimits(0)
i1.addSelectionFilter(adsk.core.SelectionCommandInput.Edges)
i1.addSelectionFilter(adsk.core.SelectionCommandInput.SketchLines)
# Dropdown group
profileGroup = inputs.addGroupCommandInput(DROPDOWN_GROUP_ID, 'Dropdown group')
# Drop Down
profileStandardDropdownInput = profileGroup.children.addDropDownCommandInput(DROPDOWN_ID,'Dropdown',adsk.core.DropDownStyles.TextListDropDownStyle)
profileStandardDropdownInput.listItems.add('Jim',True)
profileStandardDropdownInput.listItems.add('Jack',False)
profileStandardDropdownInput.listItems.add('Jim-Jack',False)
# Preview
previewGroup = inputs.addGroupCommandInput(PREVIEW_GROUP_ID, 'Preview')
#previewGroup.isExpanded = uiState.getGroupExpandedState(PREVIEW_GROUP_ID)
previewGroup.children.addBoolValueInput(SHOW_PREVIEW_INPUT, 'Show auto update preview (slow)', True, '', False)
showPreviewManual = previewGroup.children.addBoolValueInput(SHOW_PREVIEW_MANUAL_INPUT, 'Update preview once', False, '', False)
showPreviewManual.isFullWidth = True
def command_execute(args: adsk.core.CommandEventArgs):
# General logging for debug
futil.log(f'{CMD_NAME} Command Execute Event')
inputs = args.command.commandInputs
# TODO ******************************** Your code here ********************************
drawSomething(args)
# drawSomething2(args) #drawSomething2(args) does not contain any selection and works
# This function will be called when the command needs to compute a new preview in the graphics window
def command_preview(args: adsk.core.CommandEventArgs):
inputs = args.command.commandInputs
futil.log(f'{CMD_NAME} Command Preview Event')
if is_all_input_valid(inputs):
showPreview: adsk.core.BoolValueCommandInput = inputs.itemById(SHOW_PREVIEW_INPUT)
showPreviewManual: adsk.core.BoolValueCommandInput = inputs.itemById(SHOW_PREVIEW_MANUAL_INPUT)
if showPreview.value or showPreviewManual.value:
#args.isValidResult = drawSomething(args)
args.isValidResult = drawSomething(args) #drawSomething2(args) does not contain any selection and works
showPreviewManual.value = False
else:
args.executeFailed = True
args.executeFailedMessage = "Some inputs are invalid, unable to generate preview"
# This function will be called when the user changes anything in the command dialog
def command_input_changed(args: adsk.core.InputChangedEventArgs):
changed_input = args.input
inputs = args.inputs
futil.log(f'{CMD_NAME} Input Changed Event fired from a change to {changed_input.id}')
dropdown: adsk.core.DropDownCommandInput = inputs.itemById(DROPDOWN_ID)
lineSelectionInput: adsk.core.SelectionCommandInput = inputs.itemById(LINE_SELECTION)
try:
if changed_input.id == DROPDOWN_ID:
ui.messageBox('new player selected')
if changed_input.id == LINE_SELECTION:
pass
except:
ui.messageBox('error')
# This event handler is called when the user interacts with any of the inputs in the dialog
# which allows you to verify that all of the inputs are valid and enables the OK button.
def command_validate_input(args: adsk.core.ValidateInputsEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME} Validate Input Event')
inputs = args.inputs
# Verify the validity of the input values. This controls if the OK button is enabled or not.
args.areInputsValid = is_all_input_valid(inputs)
# This event handler is called when the command terminates.
def command_destroy(args: adsk.core.CommandEventArgs):
global local_handlers
local_handlers = []
futil.log(f'{CMD_NAME} Command Destroy Event')
def is_all_input_valid(inputs: adsk.core.CommandInputs):
result = True # TODO
return result
def createNewComponent():
# Get the active design.
product = app.activeProduct
design = adsk.fusion.Design.cast(product)
rootComp = design.rootComponent
allOccs = rootComp.occurrences
newOcc = allOccs.addNewComponent(adsk.core.Matrix3D.create())
return newOcc.component
def drawSomething(args: adsk.core.CommandEventArgs):
inputs = args.command.commandInputs
lineSelectionInput: adsk.core.SelectionCommandInput = inputs.itemById(LINE_SELECTION)
try:
global newComp
newComp = createNewComponent()
if newComp is None:
ui.messageBox('New component failed to create', 'New Component Failed')
return
else:
ui.messageBox('Component created')
# Get the line or edge
selObj = lineSelectionInput.selection(0)
entity = selObj.entity
ui.messageBox('Line was selected from the selection')
lineLength = entity.length
ui.messageBox('The line length is :'+str(entity.length))
# create the normal plane
planes = newComp.constructionPlanes
planeInput = planes.createInput()
planeInput.setByDistanceOnPath(entity, adsk.core.ValueInput.createByReal(0.5))
plane = planes.add(planeInput)
ui.messageBox('Construction plane created')
# create the sketch
sketches = newComp.sketches
sketch = sketches.add(plane)
ui.messageBox('Sketch created')
# Draw the sketch #TODO replace by a defined profile sketch linked to profile name
pipeRadius = lineLength/20
center = plane.geometry.origin
center = sketch.modelToSketchSpace(center)
sketch.sketchCurves.sketchCircles.addByCenterRadius(center, pipeRadius)
profile = sketch.profiles[0]
ui.messageBox('sketch drawn')
# Extrude
feats = newComp.features
extrudeFeats = feats.extrudeFeatures
ui.messageBox('feature created')
extrudeInput = extrudeFeats.createInput(profile, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
extent_distance_1 = adsk.fusion.DistanceExtentDefinition.create(adsk.core.ValueInput.createByReal(lineLength/2))
extent_distance_2 = adsk.fusion.DistanceExtentDefinition.create(adsk.core.ValueInput.createByReal(lineLength/2))
deg5 = adsk.core.ValueInput.createByString("5 deg")
deg10 = adsk.core.ValueInput.createByString("10 deg")
extrudeInput.setTwoSidesExtent(extent_distance_1, extent_distance_2, deg5, deg10)
extrude7 = extrudeFeats.add(extrudeInput)
ui.messageBox('body extruded')
except:
if ui:
ui.messageBox('Failed to compute the extrusion: drawSomething()')
def drawSomething2(args: adsk.core.CommandEventArgs):
inputs = args.command.commandInputs
lineSelectionInput: adsk.core.SelectionCommandInput = inputs.itemById(LINE_SELECTION)
try:
# global newComp
newComp = createNewComponent()
if newComp is None:
ui.messageBox('New component failed to create', 'New Component Failed')
return
# create the normal plane
planes = newComp.constructionPlanes
planeInput = planes.createInput()
planeInput.setByOffset(newComp.xYConstructionPlane,adsk.core.ValueInput.createByReal(10))
plane = planes.add(planeInput)
# create the sketch
sketches = newComp.sketches
sketch = sketches.add(plane)
# Draw the sketch #TODO replace by a defined profile sketch linked to profile name
pipeRadius = 2 #lineLength/20
center = plane.geometry.origin
center = sketch.modelToSketchSpace(center)
sketch.sketchCurves.sketchCircles.addByCenterRadius(center, pipeRadius)
profile = sketch.profiles[0]
# Extrude
feats = newComp.features
extrudeFeats = feats.extrudeFeatures
extrudeInput = extrudeFeats.createInput(profile, adsk.fusion.FeatureOperations.NewBodyFeatureOperation)
extent_distance_1 = adsk.fusion.DistanceExtentDefinition.create(adsk.core.ValueInput.createByReal(5))
extent_distance_2 = adsk.fusion.DistanceExtentDefinition.create(adsk.core.ValueInput.createByReal(5))
deg5 = adsk.core.ValueInput.createByString("5 deg")
deg10 = adsk.core.ValueInput.createByString("10 deg")
extrudeInput.setTwoSidesExtent(extent_distance_1, extent_distance_2, deg5, deg10)
extrude7 = extrudeFeats.add(extrudeInput)
except:
if ui:
ui.messageBox('Failed to compute the Extrusion. drawSomething2()')
Solved! Go to Solution.
Solved by BrianEkins. Go to Solution.
Apparently, there is a bit different behavior when executing versus preview.
What's happening is that when you execute, the selections are cleared when you make a change to the design. Specifically, in your case, it's when you create the component. To work around this, you need to get the selection first. Here's a slightly modified version of your drawSomething function where I moved the two lines that get the selection to before the call to createNewComponent.
def drawSomething(args: adsk.core.CommandEventArgs):
inputs = args.command.commandInputs
lineSelectionInput: adsk.core.SelectionCommandInput = inputs.itemById(LINE_SELECTION)
try:
global newComp
selObj = lineSelectionInput.selection(0)
entity = selObj.entity
newComp = createNewComponent()
if newComp is None:
Another fix is if at the end of the drawSomething you add the following line:
return True
You're already using the return value of drawSomething to set the isValidResult value. If this is set to True, when the command is executed, it doesn't do anything and uses the result created from the last preview.
args.isValidResult = drawSomething(args)
Can't find what you're looking for? Ask the community or share your knowledge.