Accessing attributes inside the Application.documentOpened event

Accessing attributes inside the Application.documentOpened event

rbin_robotik
Contributor Contributor
953 Views
4 Replies
Message 1 of 5

Accessing attributes inside the Application.documentOpened event

rbin_robotik
Contributor
Contributor

Hello,

 

I had a question about accessing attributes inside the Application.documentOpened event.

 

When the notify method of the registered Application.documentOpened event is called, an instance of DocumentEventArgs becomes available. I am trying to access the attributes of the sketch points in all referenced documents (using the property DocumentEventArgs.document.allDocumentReferences)

 

I am following referencedDocument.design.rootComponent path to get the root component, so that I could access the sketches and therefore, the sketch points. Up to this point, it works perfectly fine. However, when I query for the attributes stored in the sketch point, I get a None result. I also checked SketchPoint.attributes.count and it is always zero when queried inside the instance of the Application.documentOpened event.

 

To validate if the attribute is actually saved in the sketch point, I queried again after the document is open and I was able to get the stored data on the attribute.

 

I am wondering what could be the problem here.

 

 

0 Likes
Accepted solutions (1)
954 Views
4 Replies
Replies (4)
Message 2 of 5

kandennti
Mentor
Mentor

Hi @rbin_robotik .

 

Perhaps the documentOpened event is fired at the end of the document open command, not when the document is loaded and ready to be manipulated.

In conclusion, it seems that the documentOpened event is too early to manipulate the document.

 

In the past, I created a sample of opening a document (f2d) and exporting it as PDF here.

https://forums.autodesk.com/t5/fusion-360-api-and-scripts/drawing-pdf-export-how-to-access-the-new-e... 

 

I used a text command to get the task in Fusion360 and waited until it was fully open.
I am not sure if this will work in the future.

 

Message 3 of 5

rbin_robotik
Contributor
Contributor
Accepted solution

Thanks for the reply @kandennti! This is a great solution and I've learned so much from that solution. Thanks again for sharing it.

 

I did some digging on the Fusion360 API and my code during the day. As we all know, it is always good to give it some time. I guess the reason of attributes on the references documents being not available is the following implementation detail on the Fusion360 API:

 


When a document is opened that references other documents, only the top-level document will cause the documentOpened event to be fired.

Ref (Application.documentOpened): https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-270c87f0-04d7-4bd8-9632-0e025d68df39

 

This makes sense to me because the attributes on the active document are available but attributes on the referenced documents are not available and the above statement explains a difference between active document and referenced documents.

 

I am not sure if I discussed this in my first post but I am using "Edit in Place" function to edit linked components in my document. I am adding a SketchPoint from the UI and assigning attributes using a custom add-in that I have been working on for a while.

 

I see that the SketchPoint is stored on the linked component. If open the linked component separately, I can see the SketchPoint that I added via "Edit in Place".  However, its attribute is empty for some reason. If I go back to the document which has a link to that component and query the attribute from the Design instance, I can retrieve it.

 

Therefore, I changed my implementation a little bit and I started using the attributes to get to the parents. To make it more clear, I'd like to share a code snippet below for the Application.documentOpened event:

 

import traceback
from adsk import core, fusion


class MyDocumentOpenedHandler(core.DocumentEventHandler):
    def __init__(self) -> None:
        super().__init__()

    def notify(self, args: core.DocumentEventArgs) -> None:
        try:
            # Get design
            design = args.document.design
            # Get root component
            root_component = design.rootComponent

            # Get the items with the specificied attributes
            attribs = design.findAttributes("my_group", "my_attr_name")
            for attr in attribs:
                attr_entity = attr.parent
                # Check if this attribute belongs to a SketchPoint
                if not attr_entity.objectType == fusion.SketchPoint.classType():
                    continue

                # Do whatever you like

        except:
            print("Error in {}:\n{}".format(self.__class__.__name__, traceback.format_exc()))

 

0 Likes
Message 4 of 5

kandennti
Mentor
Mentor

@rbin_robotik .

 

I thought of a simpler way to do this, without using text commands.

By using threading and monitoring the number of open documents, I was able to get when I could access a document after it was opened.

 

It was very tedious, so I created a helper class (DocOpenedEventHelper.py) that fires an arbitrary custom event only when the number increases.

 

Here is a sample that uses the helper class.
Please note that the getDataFile function only searches the root folder of the project.
Also, please close the target f3d file before executing the script.

 

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

# Importing helper classes
from .DocOpenedEventHelper import DocOpenedEventHelper

# Handler after document opened
# Inherit from CustomEventHandler
class DocOpenedEventLikeHandler(adsk.core.CustomEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args: adsk.core.CustomEventArgs):
        app: adsk.core.Application = adsk.core.Application.get()
        ui: adsk.core.UserInterface = app.userInterface
        try:
            # Get design
            design: adsk.fusion.Design = args.firingEvent.sender.activeProduct

            # Get root component
            # root_component: adsk.fusion.Component = design.rootComponent

            # Get the items with the specificied attributes
            attribs = design.findAttributes("my_group", "my_attr_name")

            app.log('-- dump Attributes --')
            for attr in attribs:
                attr_entity = attr.parent
                # Check if this attribute belongs to a SketchPoint
                if not attr_entity.objectType == adsk.fusion.SketchPoint.classType():
                    pass
                else:
                    app.log(f'{attr.groupName} : {attr.name} : {attr.value}')
        except:
            if ui:
                ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

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

        # Please handle datafiles appropriately.
        # Only the root folder of the project is searched.
        targetProjectName = 'attr_test'
        targetFileName = 'SketchPoint_Attribute_Test'

        dataFile: adsk.core.DataFile = getDataFile(
            targetProjectName,
            targetFileName
        )

        if not dataFile:
            app.log('datafile not found')
            return

        # Create an instance of the handler class.
        # The handler does not need to be assigned to a global variable.
        # The handler is referenced by the helper side.
        onOpenDoc = DocOpenedEventLikeHandler()

        # Create an instance of the helper class.
        # The parameter should be set to handler.
        helper = DocOpenedEventHelper(onOpenDoc)

        # Start monitoring
        helper.start()

        # Opening a datafile
        app.documents.open(dataFile)

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

def getDataFile(
    projectName: str,
    fileName: str) -> adsk.core.DataFile:

    app: adsk.core.Application = adsk.core.Application.get()

    projs = app.data.dataProjects.asArray()
    targetProj: adsk.core.DataProject = None
    proj: adsk.core.DataProject
    for proj in projs:
        if proj.name == projectName:
            targetProj = proj
            break

    if not targetProj:
        return None

    files = targetProj.rootFolder.dataFiles.asArray()
    targetFile: adsk.core.DataFile = None
    file: adsk.core.DataFile
    for file in files:
        if file.name == fileName:
            targetFile = file
            break

    if not targetFile:
        return None

    return targetFile

 

 

This is the result of the test using the attached f3d file.

1.png

 

Message 5 of 5

rbin_robotik
Contributor
Contributor

@kandennti, thank you so much for sharing a solution using custom events. I appreciate it. I think this is a great solution, and I'd definitely use this approach in future to implement another use case.

 

My current use case was a visual fix for custom graphics as discussed here. Using Design.findAttributes method solved my problem.