Drawing PDF Export: How to access the new export manager?

JuergenGe
Enthusiast
Enthusiast

Drawing PDF Export: How to access the new export manager?

JuergenGe
Enthusiast
Enthusiast

Hi,

 

i was very exited about the new Drawing PDF export API function, since this will bring more automation to our internal workflow.

 

But my first attempts to use it where quite unsuccessful.

 

Not sure how to properly access the functionality.

 

Starting point:

  • A fusion 3D document is open in fusion 360
  • This document has one referenced drawing
  • I am using Python

 

What i want to do:

  • Run a script via "Tools->Add-Ins"
  • The script is supposed to export the associated drawing as PDF to my PC

 

Sounds simple, right?

 

So far it seems i have some technical issues to do that.

  • Exporting a drawing works if the drawing is already open in Fusion 360 and a script is executed on the drawing (accessing the drawing document). However, scripts are normally not accessible in the toolbar in the drawing itself... So its kind of strange to do it like this. And also it is strange to expect the drawing to already being open. The script should be able to react.
  • Accessing the referenced drawing via the 3D model is not really trivial. The only link that i found was by accessing and opening the dataFile (parent)references:

 

app = adsk.core.Application.get()
active = app.activeDocument
drawingDataFile = active.dataFile.parentReferences.item(0)
drawingDocument = app.documents.open(drawingDataFile)
  • To retrieve the drawing document, fusion first has to open the drawing in fusion 360 in order to use it.
  • However if i open the drawing document and then try to export the PDF, it seems, that the document is not ready yet, so the PDF export does not happen. The DrawingExportManager returns True, but there is no PDF file... So maybe this is a race condition.
  • Then i tried to use an eventhandler to determine if the document has opened and then execute the DrawingExportManager, however, the eventhandler fires before the file is completely loaded, so the same race condition occurs.

Now i am honestly out of ideas.

 

Maybe i am missing some fundamental understanding of how this is supposed to work?

 

Can anybody share an example that works or maybe point me in the right direction?

 

Thanks in advance.

 

BR

 
0 Likes
Reply
Accepted solutions (1)
2,032 Views
11 Replies
Replies (11)

kandennti
Mentor
Mentor

Hi @JuergenGe .

 

I also noticed that two more were added the other day.

1.png

I didn't notice that it had been added to "What's New".

 

I was able to export the PDF successfully.

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

_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)

def run(context):
    expPDFpath = r'c:/tmp/test.pdf'
    try:
        global _app, _ui
        _app = adsk.core.Application.get()
        _ui = _app.userInterface

        drawDoc :adsk.drawing.DrawingDocument = _app.activeDocument

        draw :adsk.drawing.Drawing = drawDoc.drawing
        pdfExpMgr :adsk.drawing.DrawingExportManager = draw.exportManager

        pdfExpOpt :adsk.drawing.DrawingExportOptions = pdfExpMgr.createPDFExportOptions(expPDFpath)
        pdfExpOpt.openPDF = True
        pdfExpOpt.useLineWeights = True

        pdfExpMgr.execute(pdfExpOpt)

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

It's pretty easy to do.
However, the personal license did not work because of its limitations.

 

I would like to see a function to check the license and some sample code.

0 Likes

JuergenGe
Enthusiast
Enthusiast

Hi @kandennti 

 

thank you for your answer and your example code. 

 

I think it is always good to have some sample implementation from somebody who knows what he/she is doing.

 

As far as i can see from your example, you are also opening the active document, implying, that the Drawing is already open in the UI, correct?

drawDoc :adsk.drawing.DrawingDocument = _app.activeDocument

 

My initial idea was to open a 3D file and then export the linked drawing (without the user having to open the drawing first). This might not be a necessary function, but now i am kind of knee deep on the issue. 😄

 

I can manage to identify the linked drawing and opening it, but no PDF file gets created.

 

I have tried some more and there is in fact a race condition.

 

drawingDoc = app.documents.open(drawingDataFile)
drExpMgr = drawingDoc.drawing.exportManager
pdfOptions = drExpMgr.createPDFExportOptions(<path>)
result = drExpMgr.execute(pdfOptions)
  • Executing this returns result=True, but no PDF is created
  • Also invoking the Eventhandler app.documentOpened.add(onDocumentOpened) does not solve this. It fires, but the file is still loading the content

The only way i found to enforce the complete loading of the drawing file is to open a UI messagebox after opening the datafile. 

ui.messageBox('Sorry...you gotta wait until the drawing is loaded...', '', 0, 2) # necessary, because fusion does not wait on the file to be completely opened, except for messageBox

 

For some reason, the OK button can only be pressed once the drawing is fully loaded. Then the export is fired and also generates the desired PDF file. But this is quite a hacky workaround.

 

I did, however, not find a way to get the same signal... but i am also not the greatest programmer 🙂

 

What do you think? Can you see similar behavior?

0 Likes

kandennti
Mentor
Mentor

@JuergenGe .

 

I'm sorry, I completely misunderstood.

I've also tried documentOpened Event, but it's definitely not reliable.

 

The only other thing I'm aware of is the text command

Application.ListIdleTasks

you can see what tasks Fusion360 is doing.

 

I created a script like this to try it out.

#Fusion360API Python script
import adsk.core, adsk.fusion, traceback
import adsk.drawing
import time

_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

        # msg
        dumpMsg(_app.executeTextCommand(u'Application.ListIdleTasks'))
        dumpMsg(' -- document open -- ')

        # open doc
        # Please check and change the datafile.id beforehand.
        df = _app.data.activeProject.rootFolder.dataFiles.itemById('urn:adsk.wipprod:dm.lineage:aMijhRAqRlylyP8bFVGJXg')
        docs = _app.documents
        docs.open(df)

        # show task
        for _ in range(100):
            adsk.doEvents()
            dumpMsg(_app.executeTextCommand(u'Application.ListIdleTasks'))
            time.sleep(0.1)

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

def dumpMsg(msg :str):
    _ui.palettes.itemById('TextCommands').writeText(str(msg))

 

I can't see it clearly, but I think this is the timing when the document is completely loaded and ready for operation.

1.png

I need to look into the details, but I think it will work if I get the DrawingDocument at the timing when the "InvalidateCommandsTask" disappears.

However, there are times when the "InvalidateCommandsTask" does not occur, so I feel that some more logic is needed.

0 Likes

kandennti
Mentor
Mentor
Accepted solution

I tested it and it worked.

 

First, I modified it to make it a little easier to see the increase/decrease of tasks.
It opens the first F2D file in the root folder of the active project and dumps the tasks and events.

# Fusion360API Python script
# Dump the task of opening f2d files.
# ' << ' : Increased Tasks
# ' >> ' : IDecreased tasks
# '***' : Checked delimiters

import adsk.core, adsk.fusion, traceback
import adsk.drawing
import time

_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)
handlers = []

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

        # add event
        onDocumentOpened = MyDocumentOpenedHandler()
        _app.documentOpened.add(onDocumentOpened)
        handlers.append(onDocumentOpened)

        onDocumentOpening = MyDocumentOpeningHandler()
        _app.documentOpening.add(onDocumentOpening)
        handlers.append(onDocumentOpening)

        # msg
        dumpMsg(_app.executeTextCommand(u'Application.ListIdleTasks'))
        dumpMsg(' -- document open -- ')


        # get f2d datafile
        datafile = None
        for df in _app.data.activeProject.rootFolder.dataFiles:
            if df.fileExtension == 'f2d':
                datafile = df

        # check datafile
        if not datafile:
            _ui.messageBox('Abort because the "f2d" file cannot be found in the rootFolder of activeProject.')
            return

        # open doc
        docs = _app.documents
        docs.open(datafile)

        # show task
        state = []
        for _ in range(80):
            adsk.doEvents()

            tasks = _app.executeTextCommand(u'Application.ListIdleTasks').split('\n')
            now = [s.strip() for s in tasks[2:-1]]

            addTask = list(set(now) - set (state))
            [dumpMsg(' << {}'.format(s)) for s in addTask]

            delTask = list(set(state) - set (now))
            [dumpMsg(' >> {}'.format(s)) for s in delTask]

            dumpMsg('***')
            state = now
            time.sleep(0.1)

        # remove event
        _app.documentOpened.remove(onDocumentOpened)
        _app.documentOpening.remove(onDocumentOpening)

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

def dumpMsg(msg :str):
    _ui.palettes.itemById('TextCommands').writeText(str(msg))

class MyDocumentOpenedHandler(adsk.core.DocumentEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        dumpMsg('--- Document Opened Event ---')

class MyDocumentOpeningHandler(adsk.core.DocumentEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        dumpMsg('--- Document Opening Event ---')

The timing of the events is disappointing.

 

We decided to assume that the file was completely opened by the following four tasks disappearing in sequence.

'DocumentFullyOpenedTask'
'Nu::AnalyticsTask'
'CheckValidationTask'
'InvalidateCommandsTask'

 

With this in mind, the process of exporting the PDF file works well here.

# Fusion360API Python script
# Sample of exporting a PDF file from an unopened F2D file
import adsk.core, adsk.fusion, traceback
import adsk.drawing
import time

_app = adsk.core.Application.cast(None)
_ui = adsk.core.UserInterface.cast(None)
handlers = []

_exportPDFFolder = r'c:/tmp/'

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

        # get f2d datafile
        datafile = None
        for df in _app.data.activeProject.rootFolder.dataFiles:
            if df.fileExtension == 'f2d':
                datafile = df

        # check datafile
        if not datafile:
            _ui.messageBox('Abort because the "f2d" file cannot be found in the rootFolder of activeProject.')
            return

        # open doc
        docs = _app.documents
        drawDoc :adsk.drawing.DrawingDocument = docs.open(datafile)

        # Tasks to be checked.
        targetTasks = [
            'DocumentFullyOpenedTask',
            'Nu::AnalyticsTask',
            'CheckValidationTask',
            'InvalidateCommandsTask'
        ]

        # check start task
        if not targetTasks[0] in getTaskList():
            _ui.messageBox('Task not found : {}'.format(targetTasks[0]))
            return

        # Check the task and determine if the Document is Open.
        for targetTask in targetTasks:
            while True:
                time.sleep(0.1)
                if not targetTask in getTaskList():
                    break

        # export PDF
        expPDFpath = _exportPDFFolder + drawDoc.name + '.pdf'

        draw :adsk.drawing.Drawing = drawDoc.drawing
        pdfExpMgr :adsk.drawing.DrawingExportManager = draw.exportManager

        pdfExpOpt :adsk.drawing.DrawingExportOptions = pdfExpMgr.createPDFExportOptions(expPDFpath)
        pdfExpOpt.openPDF = True
        pdfExpOpt.useLineWeights = True

        pdfExpMgr.execute(pdfExpOpt)

        # close doc
        drawDoc.close(False)

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

def getTaskList():
    adsk.doEvents()
    tasks = _app.executeTextCommand(u'Application.ListIdleTasks').split('\n')
    return [s.strip() for s in tasks[2:-1]]

There may be a better way.
I may need to change the tasks to be checked in the future.

 

Also, I have not considered how to find the F2D file from the F3D file.

1 Like

JuergenGe
Enthusiast
Enthusiast

@kandennti Wow i am impressed!

 

Thank you so much for your detailed analysis and dedication 👍

 

I would have never managed to find that out.

This is really great.

 

I will try it as soon as i have capacity to do so and will give you feedback. But looking at all your analysis, i now have much more then i would have ever expected!

 

Thank you so much! This is probably the best help i have ever gotten from a forum entry.

 

BR

1 Like

JuergenGe
Enthusiast
Enthusiast

Works like a charm! Thank you for your support. I will implement this workaround in my script for the time being.

 

BR

1 Like

engineeringQCQFN
Enthusiast
Enthusiast

Has something changed since 2020? I copied the proposed code but it is throwing an error

engineeringQCQFN_0-1652761001521.png

 

0 Likes

JeromeBriot
Mentor
Mentor

@engineeringQCQFN wrote:

I copied the proposed code but it is throwing an error


@engineeringQCQFN  Did you save the design before running the script?

 

0 Likes

engineeringQCQFN
Enthusiast
Enthusiast
yes, I have a bunch of f2d files in my root folder, was trying to find a way to get them exported via API.

just tried to open the f2d file and running the script, same error. Tried also opening the f3d and running script but problem persists.
0 Likes

kandennti
Mentor
Mentor

@engineeringQCQFN .

 

I was unable to reproduce the error.

However, in many cases, the PDF file could not be exported correctly.
I still don't know how to know that the document has been fully opened.

0 Likes

engineeringQCQFN
Enthusiast
Enthusiast
hmm am I doing something wrong?

1) created new script and pasted the code
2) no document is open
3) run script
4) the first f2d file is opened and them error comes
0 Likes