Occurrence Appearance Name

Occurrence Appearance Name

gvisca44
Advocate Advocate
1,067 Views
9 Replies
Message 1 of 10

Occurrence Appearance Name

gvisca44
Advocate
Advocate

Trying to write myself a script that will read a CSV file that contains a list of components - and their desired Appearances and then traverse a model to apply the desired Appearance.

 

I have hit a bit of a snag as follows:

* once the Appearance is applied to an occurrence, I attempt to read back the occurrence.appearance.name I receive an error AttributeError: 'NoneType' object has no attribute 'name'

 

Refer approx line 71 in code below.

 

The API manual suggests an occurrence has an appearance property - that returns an appearance object - which has a corresponding name.  Note the processing of setting the new apperance functions successfully.

 

 

import adsk.core, adsk.fusion, adsk.cam, traceback

app = None
ui  = None

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

        fileDialog = ui.createFileDialog()
        fileDialog.title = "Select Appearance File"
        fileDialog.isMultiSelectEnabled = False
        result = fileDialog.showOpen()
        if result != adsk.core.DialogResults.DialogOK:
            return
        else:
            filename = fileDialog.filename

        result = ui.messageBox('Yes = Create points from file\nNo = Update ponits from file', 
                                'Control Points From File', 
                                adsk.core.MessageBoxButtonTypes.YesNoCancelButtonType, 
                                adsk.core.MessageBoxIconTypes.QuestionIconType)
        if result == adsk.core.DialogResults.DialogCancel:
            return
        elif result == adsk.core.DialogResults.DialogYes:
            ProcessAppearanceFile(filename)
        elif result == adsk.core.DialogResults.DialogNo:
            #UpdatePoints(filename)
            return

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

def ProcessAppearanceFile(filename):
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        product = app.activeProduct
        design = adsk.fusion.Design.cast(product)

        root = design.rootComponent
        occs = []

        ui.palettes.itemById('TextCommands').writeText("Debugging")

        occs = root.allOccurrences
        doc = app.activeDocument.name

        if len(occs) == 0:
            ui.messageBox('There are no components in this design.')
            return

        # Open the appearances file 
        with open(filename) as f:
            for line in f:

                line = line.strip()

                appearances = line.split(',')
                componentName = appearances[0]
                componentAppearance = getAppearance(appearances[1])

                for occ in occs:
                    if occ.component.name == componentName:
                        
                        occ.appearance = componentAppearance

                        # THIS IS LINE THAT FAILS
                        ui.palettes.itemById('TextCommands').writeText("New appearance " + occ.appearance.name)
                        # END OF LINE THAT FAILS
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))    

def getAppearance(appearanceName):
    app = adsk.core.Application.get()
    ui  = app.userInterface
    materialLibs = app.materialLibraries
    appearance = None
    for materialLib in materialLibs:
        appearances = materialLib.appearances

        try:
            appearance = appearances.itemByName(appearanceName)
            ui.palettes.itemById('TextCommands').writeText(appearance.name)

        except:
            pass
        
        if appearance:
            break
    return appearance

 

 

0 Likes
Accepted solutions (1)
1,068 Views
9 Replies
Replies (9)
Message 2 of 10

kandennti
Mentor
Mentor

Hi @gvisca44 .

 

I am guessing because the CSV file was not attached, but I think that the CSV file is read as a normal text file and therefore the appearance is not being acquired properly.
(I think it is a double quotation marks issue).

 

I think you should add a filter to the dialog and use it in the csv module.
It would also be safer to check if the appearance is retrieved.

# Fusion360API Python script
import traceback
import adsk.fusion
import adsk.core
import csv

app = None
ui  = None

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

        fileDialog: adsk.core.FileDialog = ui.createFileDialog()
        fileDialog.title = "Select Appearance File"
        fileDialog.filter = 'csv files (*.csv);;'
        fileDialog.isMultiSelectEnabled = False
        result = fileDialog.showOpen()
        if result != adsk.core.DialogResults.DialogOK:
            return
        else:
            filename = fileDialog.filename

        result = ui.messageBox('Yes = Create points from file\nNo = Update ponits from file', 
                                'Control Points From File', 
                                adsk.core.MessageBoxButtonTypes.YesNoCancelButtonType, 
                                adsk.core.MessageBoxIconTypes.QuestionIconType)
        if result == adsk.core.DialogResults.DialogCancel:
            return
        elif result == adsk.core.DialogResults.DialogYes:
            ProcessAppearanceFile(filename)
        elif result == adsk.core.DialogResults.DialogNo:
            #UpdatePoints(filename)
            return

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

def ProcessAppearanceFile(filename):
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        product = app.activeProduct
        design = adsk.fusion.Design.cast(product)

        root = design.rootComponent
        occs = []

        app.log("Debugging")

        occs = root.allOccurrences
        doc = app.activeDocument.name

        if len(occs) == 0:
            ui.messageBox('There are no components in this design.')
            return

        with open(filename) as f:
            reader = csv.reader(f)
            for row in reader:
                componentName = row[0]
                componentAppearance = getAppearance(row[1])

                if not componentAppearance:
                    msg = f'Canceled because "Appearance" cannot be obtained. \n ({row[1]})'
                    ui.messageBox(msg)
                    return

                for occ in occs:
                    print(occ.component.name)
                    print(componentName)
                    if occ.component.name == componentName:
                        
                        occ.appearance = componentAppearance

                        # THIS IS LINE THAT FAILS
                        app.log("New appearance " + occ.appearance.name)
                        # END OF LINE THAT FAILS
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

def getAppearance(appearanceName):
    app = adsk.core.Application.get()
    materialLibs = app.materialLibraries
    appearance = None
    for materialLib in materialLibs:
        appearances = materialLib.appearances

        try:
            appearance = appearances.itemByName(appearanceName)
            app.log(appearance.name)

        except:
            pass

        if appearance:
            break
    return appearance
0 Likes
Message 3 of 10

gvisca44
Advocate
Advocate

Hi @kandennti 

 

I appreciate your response.  CSV file is as simple as component and desired appearance separated by comma per line.

 

I am happy that the application of the correct appearance is working because the appearances are visually present in the model I am working on.

 

I suspect (but am very happy to be corrected) something is screwy about accessing the appearance property of an occurrence object. 

 

Consider the following code.  Getting the appearance by body by component works successfully.

 

However, getting the appearance by occurrence (which I believe should work) fails.  I can certainly SET the appearance this way.  And the occurrence object has an appearance property.

import adsk.core, adsk.fusion, adsk.cam, traceback

def getAppearanceByBody():
    app = adsk.core.Application.get()
    ui  = app.userInterface
    product = app.activeProduct
    design = adsk.fusion.Design.cast(product)

    for comp in design.allComponents:
        for body in comp.bRepBodies:
            app.log('Component-' + comp.name + '\tBody-' + body.name + '\tAppearance-' + body.appearance.name)

def getAppearanceByOccurrence():
    app = adsk.core.Application.get()
    ui  = app.userInterface
    product = app.activeProduct
    design = adsk.fusion.Design.cast(product)

    root = design.rootComponent
    occs = root.allOccurrences

    for occ in occs:
        app.log('Occurrence-' + occ.name + '\tAppearance-' + occ.appearance.name)

def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        
        ui.messageBox('APPEARANCES BY COMPONENT BODY')
        app.log('**** APPEARANCES BY COMPONENT BODY ****')
        getAppearanceByBody()

        ui.messageBox('APPEARANCES BY OCCURRENCE')
        app.log('****   APPEARANCES BY OCCURRENCE   ****')
        getAppearanceByOccurrence()

    except:
        if ui:
            app.log('Failed:\n{}'.format(traceback.format_exc()))

 

Regards, 

 

Glenn.

Message 4 of 10

kandennti
Mentor
Mentor

@gvisca44 .

 

I understand what you mean.

Certainly the name property was not available in the case of Ocarens.

1.png

I think it is a bug.

How about using the id property?

0 Likes
Message 5 of 10

BrianEkins
Mentor
Mentor

The appearance property of the Occurrence object can return null. When an appearance is added to an occurrence, it overrides the colors assigned to the body and faces. Getting the occurrence appearance is getting the override color. If there isn't an override, it will return None.

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
0 Likes
Message 6 of 10

gvisca44
Advocate
Advocate

@kandennti 

I had the same thought.  id also failed.

 

 

Message 7 of 10

gvisca44
Advocate
Advocate

@BrianEkins 

yes ... after several hours of messing around I did finally get my head around the nuance.

 

However - my original statement remains ... when I apply an apperance to an occurrence it works successfully (can physically see it on the model).  When I attempt to read the appearance of that same occurrence - I receive the error. 

0 Likes
Message 8 of 10

BrianEkins
Mentor
Mentor

Here's a small standalone script that I wrote to test this. Create a new design and create a new component (Occurrence) within the design and then run the script. Each time you run it, it will change the appearance of the occurrence from gold to steel and back. I'm also able to get the appearance after it's set.

 

def run(context):
    try:
        app = adsk.core.Application.get()
        ui = app.userInterface
        des = adsk.fusion.Design.cast(_app.activeProduct)
        root = des.rootComponent

        if root.occurrences.count == 0:
            ui.messageBox('There must be at least one occurrence in the design.')
            return

        occ = root.occurrences[0]

        app.log(f'Running test on {occ.name}')
        newName = 'Gold - Polished'
        if occ.appearance is None:
            app.log(f'  The occurrence has NO appearance override.')
        else:
            app.log(f'  The current appearance override is {occ.appearance.name}')

            if occ.appearance.name == 'Gold - Polished':
                newName = 'Steel - Satin'

        newAppearance = des.appearances.itemByName(newName)
        if newAppearance is None:
            lib = app.materialLibraries.itemByName('Fusion 360 Appearance Library')
            libAppearance = lib.appearances.itemByName(newName)
            newAppearance = des.appearances.addByCopy(libAppearance, newName)

        occ.appearance = newAppearance

        app.log(f'   After setting appearance the occurrence appearance is: {occ.appearance.name}')
    except:
        ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
0 Likes
Message 9 of 10

gvisca44
Advocate
Advocate

Thanks Brian

 

There's a small error in the script as posted:

        app = adsk.core.Application.get()
        ui = app.userInterface
        des = adsk.fusion.Design.cast(_app.activeProduct)

note the underscore before app.activeProduct

But thats ok ... I think I have found the issue (more than likely in the way I am processing things)

 

Now - imagine you run that same script - but 

 

You have a design with 1 occurence (as you state)

You have a design that uses the above as a linked design (which is the way all my designs operate).

 

Now run the script again on the second design.

0 Likes
Message 10 of 10

BrianEkins
Mentor
Mentor
Accepted solution

That's it. I can reproduce it with a linked design. I'll see that a bug is filed.

---------------------------------------------------------------
Brian Ekins
Inventor and Fusion 360 API Expert
Website/Blog: https://EkinsSolutions.com
0 Likes