'attributes.add' not modifying components

Justin.Krutz
Explorer Explorer
1,153 Views
6 Replies
Message 1 of 7

'attributes.add' not modifying components

Justin.Krutz
Explorer
Explorer

I am attempting to add attributes to components. It seemed to work last month but am having trouble now.

 

'attributes.add' doesn't seem to be actually modifying components. It acts as if it does, but as soon as the stop function runs it forgets the attribute. The attribute should be saved to the component so it should still exist after the program has ended.

When included code runs and I select a component, it outputs the following into the debug console:

 

MyCommandInputChangedHandler
selectedComp:
<adsk.fusion.Component; proxy of <Swig Object of type 'adsk::core::Ptr< adsk::fusion::Component > *' at 0x0000021727748C90> >
selectedComp.attributes.count:
1
selectedComp.attributes.groupNames:
('Test groupName',)
selectedComp.attributes.itemByName("Test groupName", "Test name"):
<adsk.core.Attribute; proxy of <Swig Object of type 'std::vector< adsk::core::Ptr< adsk::core::Attribute > >::value_type *' at 0x00000217277763C0> >
selectedComp.attributes.itemByName("Test groupName", "Test name").value:
Test value

 

 

When I click the 'Cancel' button it gives the following error in a pop-up dialog window:

 

Failed:
Traceback (most recent call last):
  File "C:/Users/agent/Desktop/Justin/Code/Fusion 360/CustomModelParametersAddin/CustomModelParametersAddin.py", line 129, in stop
    print('selectedComp.attributes.itemByName("Test groupName", "Test name").value: ' + str(selectedComp.attributes.itemByName("Test groupName", "Test name").value))
AttributeError: 'NoneType' object has no attribute 'value'

 

 

When I dismiss the dialog window, the following is sent to the debug console:

 

stop
selectedComp:
<adsk.fusion.Component; proxy of <Swig Object of type 'adsk::core::Ptr< adsk::fusion::Component > *' at 0x0000021727748C90> >
selectedComp.attributes.count:
0
selectedComp.attributes.groupNames:
()
selectedComp.attributes.itemByName("Test groupName", "Test name"):
None

 

 

 

Example code:

 

#Author-Justin K
#Description-

import adsk.core, adsk.fusion, adsk.cam, traceback
import json
import os, sys

app = None
ui  = None
global eventArgs
global inputs
global cmdInput
selectedComp = None
selectedCompAttributes = {}

# Global set of event handlers to keep them referenced for the duration of the command
_handlers = []

# Event handler that reacts to any changes the user makes to any of the command inputs.
class MyCommandInputChangedHandler(adsk.core.InputChangedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            global eventArgs
            global inputs
            global cmdInput
            eventArgs = adsk.core.InputChangedEventArgs.cast(args)
            inputs = eventArgs.inputs
            cmdInput = eventArgs.input
            

            global selectedComp
            global selectedCompAttributes

            selectionInput = inputs.itemById("selection")

            if cmdInput.id == "selection":
                # changeAttributes(selectionInput.selection(0).entity.component)
                selectedComp = selectionInput.selection(0).entity.component
                selectedComp.attributes.add("Test groupName", "Test name", "Test value")
                print('MyCommandInputChangedHandler')
                print('selectedComp:\n' + str(selectedComp))
                print('selectedComp.attributes.count:\n' + str(selectedComp.attributes.count))
                print('selectedComp.attributes.groupNames:\n' + str(selectedComp.attributes.groupNames))
                print('selectedComp.attributes.itemByName("Test groupName", "Test name"):\n' + str(selectedComp.attributes.itemByName("Test groupName", "Test name")))
                print('selectedComp.attributes.itemByName("Test groupName", "Test name").value:\n' + str(selectedComp.attributes.itemByName("Test groupName", "Test name").value))

        except:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))


# Event handler that reacts to when the command is destroyed. This terminates the script.            
class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            # When the command is done, terminate the script
            # This will release all globals which will remove all event handlers
            adsk.terminate()
        except:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))


# Event handler that reacts when the command definition is executed which
# results in the command being created and this event being fired.
class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            
            # Get the command that was created.
            cmd = adsk.core.Command.cast(args.command)

            # Connect to the command destroyed event.
            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            # Connect to the input changed event.           
            onInputChanged = MyCommandInputChangedHandler()
            cmd.inputChanged.add(onInputChanged)
            _handlers.append(onInputChanged)    

            selectionInput = cmd.commandInputs.addSelectionInput("selection", "Select Parametric Part", "Component to select")
            selectionInput.setSelectionLimits(1, 0)
            selectionInput.addSelectionFilter("Occurrences")

        except:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))


def run(context):
    try:
        global app, ui
        app = adsk.core.Application.get()
        ui = app.userInterface

        # Get the existing command definition or create it if it doesn"t already exist.
        cmdDef = ui.commandDefinitions.itemById("cmdInputsSample")
        if not cmdDef:
            cmdDef = ui.commandDefinitions.addButtonDefinition("cmdInputsSample", "Edit Parametric Part", "Sample to demonstrate various command inputs.")

        # Connect to the command created event.
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)

        # Execute the command definition.
        cmdDef.execute()

        # Prevent this module from being terminated when the script returns, because we are waiting for event handlers to fire.
        adsk.autoTerminate(False)


    except:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))

def stop(context):
    try:
        print('\nstop')
        print('selectedComp:\n' + str(selectedComp))
        print('selectedComp.attributes.count:\n' + str(selectedComp.attributes.count))
        print('selectedComp.attributes.groupNames:\n' + str(selectedComp.attributes.groupNames))
        print('selectedComp.attributes.itemByName("Test groupName", "Test name"):\n' + str(selectedComp.attributes.itemByName("Test groupName", "Test name")))
        print('selectedComp.attributes.itemByName("Test groupName", "Test name").value:\n' + str(selectedComp.attributes.itemByName("Test groupName", "Test name").value))

    except:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))

 

 

 

1 Like
Accepted solutions (1)
1,154 Views
6 Replies
Replies (6)
Message 2 of 7

Marco.Takx
Mentor
Mentor

Hi @Justin.Krutz,

 

This topic can better be placed on this forum.

https://forums.autodesk.com/t5/fusion-360-api-and-scripts/bd-p/22

 

@jeff_strater can you transfer this to the Fusion 360 API and Scripts forum.

 

If my post answers your question Please use Accept as Solution & Kudos This helps everyone find answers more quickly!

Met vriendelijke groet | Kind regards | Mit freundlichem Gruß

Marco Takx
CAM Programmer & CAM Consultant



0 Likes
Message 3 of 7

jeff_strater
Community Manager
Community Manager

@Marco.Takx - I've sent this over to the API team to investigate.  Sadly, I am not qualified to respond myself...


Jeff Strater
Engineering Director
2 Likes
Message 4 of 7

goyals
Autodesk
Autodesk

A similar query is submitted earlier. Please take a look at https://forums.autodesk.com/t5/fusion-360-api-and-scripts/changing-an-attribute-in-inputchangedhandl.... Thanks.



Shyam Goyal
Sr. Software Dev. Manager
2 Likes
Message 5 of 7

Shawn.Hice
Alumni
Alumni
Accepted solution

Hey all I saw your post so I decided to take the chance to make a little example of the onExecute functionality of the command interface and show how your workflow may benefit from it since you may or may not want to assign attributes during the selection change but instead on the OK.

 

I also noticed the commandInput example was missing the onExecute functionality which was odd but here are some great links to look at exactly how each of the commands work:

Commands Overview

SelectionCommandInput Object

 

(on a side note I actually wasn't experiencing the issue of the inconsistent input changed event however this is still an example of how the commandExecuteHandler event could be used if this is the desired functionality)

 

I made a few small changes to help with debug and clarify the global keyword but below works on my machine for adding attributes consistently:

 

#Author-Justin K
#Description-

import adsk.core, adsk.fusion, adsk.cam, traceback
import json, os, sys

app = None
ui  = None

# shawn - to access these globals try to declare them at the top and only assign after using the global <name> syntax
# shawn - Since you declare them here no need to specify global it's automatically derived from the context
selectedComp = None
selectedCompAttributes = {}

# Global set of event handlers to keep them referenced for the duration of the command
_handlers = []

# shawn - This is the command that gets run when you push OK
class MyCommandExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs = adsk.core.CommandEventArgs.cast(args)

            # Get the values from the command inputs. 
            inputs = eventArgs.command.commandInputs

            selectionInput = inputs.itemById("selection")

            # because you are assigning selectedComp we need to use global selectedComp here
            global selectedComp

            print("Someone clicked okay and a occurence was found")
            # changeAttributes(selectionInput.selection(0).entity.component)
            selectedComp = selectionInput.selection(0).entity.component
            selectedComp.attributes.add("Test groupName", "Test name", "Test value")
            print('MyCommandInputChangedHandler')
            print('selectedComp:\n' + str(selectedComp))
            print('selectedComp.attributes.count:\n' + str(selectedComp.attributes.count))
            print('selectedComp.attributes.groupNames:\n' + str(selectedComp.attributes.groupNames))
            print('selectedComp.attributes.itemByName("Test groupName", "Test name"):\n' + str(selectedComp.attributes.itemByName("Test groupName", "Test name")))
            print('selectedComp.attributes.itemByName("Test groupName", "Test name").value:\n' + str(selectedComp.attributes.itemByName("Test groupName", "Test name").value))

            # Destroy event handler will still be called
        except:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))
 

# Event handler that reacts to any changes the user makes to any of the command inputs.
class MyCommandInputChangedHandler(adsk.core.InputChangedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            print("Something is changing but not set yet.\n")
        except:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))


# Event handler that reacts to when the command is destroyed. This terminates the script.            
class MyCommandDestroyHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            # When the command is done, terminate the script
            # This will release all globals which will remove all event handlers
            adsk.terminate()
        except:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))


# Event handler that reacts when the command definition is executed which
# results in the command being created and this event being fired.
class MyCommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            
            # Get the command that was created.
            cmd = adsk.core.Command.cast(args.command)

            # Connect to the command destroyed event.
            onDestroy = MyCommandDestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            # Connect to the input changed event.           
            onInputChanged = MyCommandInputChangedHandler()
            cmd.inputChanged.add(onInputChanged)
            _handlers.append(onInputChanged)

            # NEW ______________________
            # Connect to the execute event.
            onExecute = MyCommandExecuteHandler()
            cmd.execute.add(onExecute)
            _handlers.append(onExecute)
            # __________________________

            selectionInput = cmd.commandInputs.addSelectionInput("selection", "Select Parametric Part", "Component to select")
            selectionInput.setSelectionLimits(1, 0)
            selectionInput.addSelectionFilter("Occurrences")

        except:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))


def run(context):
    try:
        global app, ui
        app = adsk.core.Application.get()
        ui = app.userInterface

        # Get the existing command definition or create it if it doesn"t already exist.
        cmdDef = ui.commandDefinitions.itemById("cmdInputsSample")
        if not cmdDef:
            cmdDef = ui.commandDefinitions.addButtonDefinition("cmdInputsSample", "Edit Parametric Part", "Sample to demonstrate various command inputs.")

        # Connect to the command created event.
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)

        # Execute the command definition.
        cmdDef.execute()

        # Prevent this module from being terminated when the script returns, because we are waiting for event handlers to fire.
        adsk.autoTerminate(False)

    except:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))

def stop(context):
    try:
        print('\nstop')

        # shawn - Real fast check to see if the global variable was ever set this instance, if multiple commands are started I would assign in the run method to None.
        if selectedComp:
            # shawn - A little output to show you the result even if you don't have debug open.
            if ui:
                ui.messageBox('{} has {} test attribute , \n \tTest groupName : {}'.format(selectedComp.name, selectedComp.attributes.count, selectedComp.attributes.itemByName("Test groupName", "Test name").value))
            print('selectedComp:\n' + str(selectedComp))
            print('selectedComp.attributes.count:\n' + str(selectedComp.attributes.count))
            print('selectedComp.attributes.groupNames:\n' + str(selectedComp.attributes.groupNames))
            print('selectedComp.attributes.itemByName("Test groupName", "Test name"):\n' + str(selectedComp.attributes.itemByName("Test groupName", "Test name")))
            print('selectedComp.attributes.itemByName("Test groupName", "Test name").value:\n' + str(selectedComp.attributes.itemByName("Test groupName", "Test name").value))

    except:
        if ui:
            ui.messageBox("Failed:\n{}".format(traceback.format_exc()))

 

Thank you for your support and let me know if this is helpful,

- Shawn Hice

1 Like
Message 6 of 7

Justin.Krutz
Explorer
Explorer

Thank you so much!

This is my first Python project so I still have a lot to learn.
I started this project by modifying and building off of this example, which didn't include onExecute.

1 Like
Message 7 of 7

Shawn.Hice
Alumni
Alumni

Yeah I find it odd that the example doesn't include the onExecute functionality, perhaps we should include an example that provides functionality for each of the command handlers to showcase the functionality in the future.

 

I'm glad I could help and let me know if there are any other issues that you find or if there is anything we can do to improve the documentation readability.

1 Like