Community
Fusion API and Scripts
Got a new add-in to share? Need something specialized to be scripted? Ask questions or share what you’ve discovered with the community.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

SaveAs not working as expected

6 REPLIES 6
SOLVED
Reply
Message 1 of 7
MattPerez314
216 Views, 6 Replies

SaveAs not working as expected

Hi all, I think this is my first post in the API forum and I have exhausted the search function but nothing seems to answer my question.  I am hoping it is simple for you all with lots of experience.

 

The problem (short): SaveAs after a model change isn't actually saving anywhere and I am not sure why.

 

The problem (long):  I have a script that opens a CSV file that contains Length,Width values.  Running the script will update the user parameters in the design of the same name.  That all works fine.  Where it seems to fall apart is on the Save As.  The intent is to SaveAs with a common name "Door -" and pull in the values from the csv into the file name.  Where things are not seeming to work is after it runs through the first line, updates the parameters/model and should save the design just has an * showing it in an unsaved state.  I have tried using the current folder the active design is in as well as the root project folder with no success.  The script will run through all the lines in the CSV if I don't require the save, but that is kind of the whole point of the script 🙂  Also there are a few things in the code that aren't being used in its current state like folder = adsk.core.DataFolder, and also note i removed the file location from the code, but that isn't how it is coded to run.

 

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

def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        design = adsk.fusion.Design.cast(app.activeProduct)
        document = adsk.core.Document.cast(app.activeDocument)
        folder = adsk.core.DataFolder

        #CSV File with the format of length,width
        file = open('...Test2.csv')
        #for loop that will run for each line in the CSV file
        #loop will pull out each piece and assign it to Length and Width
        for line in file:
            pieces = line.split(',')
            Length = pieces[0]
            Width = pieces[1]
            ui.messageBox('New Length = ' + Length + ' and New Width = ' + Width)
            
            #Set up the user parameters by calling them by name and setting the expression
            #To Length and Width from the CSV
            csvParams = design.userParameters
            csvParams.itemByName('Length').expression = Length
            csvParams.itemByName('Width').expression = Width
            
            #Get the Path and Root from the parent document and tell the user what they are
            path = design.parentDocument.dataFile.parentFolder
            #pathRoot = design.parentDocument.dataFile.parentProject
            ui.messageBox('Path is ' + path.name + ' ' + path.id)
            
            #Create a new file name using Cabinet plus the values of length and width from the CSV
            newFileName = 'Cabinet - ' + Length + ' x ' + Width
            
            #Save As using the new file name, path from the current active document
            document.saveAs(newFileName,path,'','')
            
            if app.activeDocument.isSaved:
                ui.messageBox('Success')
                return
            else: 
                ui.messageBox('Fail')

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

 

 

So the first time through it will send a messageBox "fail" stating the isSaved is coming back False.  then when it runs through the second line of the CSV the file is in an unsaved state and throws a warning that I haven't coded to account for.  If I toss in a Save, it will want me to manually use the saveAs dialog.  So it is behaving as if a SaveAs was invoked but I feel like it just might be missing something.  

 

All the samples I have come across doing the same or similar don't seem to have anything else supporting the SaveAs so I can't seem to figure out why the save is failing.  Some of the samples were from 2015 or so and I am hoping there is just a missing piece.

 

Thoughts?

 

Thanks for your time! 

6 REPLIES 6
Message 2 of 7
MattPerez314
in reply to: MattPerez314

I made a little progress but a few more things are happening.

 

Using design.parentDocument.dataFile.parentFolder is returning the current folder of the active design, but it isn't accepting that for the dataFile in SaveAs.  When I traverse the project and folders I am able to save to the folder fine.

 

So why doesn't the data returned by dataFile.parentFolder work for saveAs?

        #Get the project named LEAD
        proj: adsk.core.DataProject
        projFilesProject: adsk.core.DataProject = None
        for proj in activeHub.dataProjects:
            if proj.name == 'LEAD':
                projFilesProject = proj
                break
        #Get the folder named Save Test
        projFolder = projFilesProject.rootFolder.dataFolders.itemByName('API')

        SaveTestFolder = projFolder.dataFolders.itemByName('Save Test')

 

Second issue I am running into.  my CSV has 3 lines. the for loop goes through each line in the file and updates the model fine, but it only saves the last design.  I should end up with 3 files in the folder called

"Cabinet - 30 x 18"

"Cabinet - 28 x 18"

"Cabinet - 26 x 18" < this is the only one it saves, the last line of the loop through.

 

        for line in file:
            pieces = line.split(',')
            Length = pieces[0]
            Width = pieces[1]
            ui.messageBox('Length = ' + Length + ' and Width = ' + Width)
            
            #Set up the user parameters by calling them by name and setting the expression
            #To Length and Width from the CSV
            csvParams = design.userParameters
            csvParams.itemByName('Length').expression = Length
            csvParams.itemByName('Width').expression = Width
            
            #Create a new file name using Cabinet plus the values of length and width from the CSV
            newFileName = 'Cabinet - ' + Length + ' x ' + Width
            
            #Save As using the new file name, path from the current active document
            document.saveAs(newFileName,SaveTestFolder,'Description','Tag')
            #app.activeDocument.saveAs(newFileName,SaveTestFolder,'Description','Tag')
            ui.messageBox(newFileName + ' Saved?')
            ui.messageBox(SaveTestFolder.name)

 

 

And the last problem.  After it runs through the program and the only design left open(because I haven't added anything to close it yet) the dataPanel still shows the original design as "reserved" by me.  If I open then close it the file is happy, but why doesn't saveAs take care of this?  The design is saved before the code is run and then once parameters are changed a SaveAs is run.  Why would the original still think it is reserved/open?  Is it best to reOpen that design by name/id after everything is run and then close it?

 

Thanks!

Message 3 of 7
MattPerez314
in reply to: MattPerez314

A little more progress but still haven't solved everything.

path = design.parentDocument.dataFile.parentFolder

calling the dataFile.parentFolder does work for SaveAs.  It wasn't working before because I was calling it each time in the for loop and it didn't like that.  When calling and setting the path before the for loop off the active design it works fine for saving in and out of the loop.

 

What I still can't figure out is why saveAs doesn't save each time through the for loop for each line in the CSV file.  It does loop through each line because the model does update.  I also had a counter in the loop making sure it was iterating through ok.  For whatever reason it goes through however many lines in the CSV file, updates the model, will send my ui.messageBox that i put before(to display what the file name is going to be) and after the saveAs, but will only save after the last line in the CSV despite it having a unique filename each time through and the model changes on the screen.  I have tried including adsk.doEvents() before and after as I found Brian had mentioned (in 2015) that there was an issue with saveAs back then.  No change.  I also tried other loop variations like a while loop with no change.  It is 100% looping through that line of code each time through the CSV data but not saving until it jumps out of the loop that last time.

 

So I guess the main open question is why is the saveAs only working on the last time through the for loop?  Is there some event or trigger that needs to happen in the for loop.  none of the sample programs or answers on here have had such a thing.

 

Thanks again for anyone who has read this and made it this far 🙂

Message 4 of 7
kandennti
in reply to: MattPerez314

Hi @MattPerez314 .

 

We were able to reproduce the phenomenon, but had difficulty finding the cause.

 

I found that your process does not create any files except the last one, because the file name contains a newline.

 

After correcting it this way, I think you are getting the process you want.

# Fusion360API Python script

import traceback
import adsk.fusion
import adsk.core
import csv

def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        design = adsk.fusion.Design.cast(app.activeProduct)
        document = adsk.core.Document.cast(app.activeDocument)
        folder = adsk.core.DataFolder

        #CSV File with the format of length,width
        csvPath = '...Test2.csv'
        with open(csvPath) as f:
            reader = list(csv.reader(f))

        #Get the Path and Root from the parent document and tell the user what they are
        path = design.parentDocument.dataFile.parentFolder

        #for loop that will run for each line in the CSV file
        #loop will pull out each piece and assign it to Length and Width
        for csvData in reader:
            Length = csvData[0]
            Width = csvData[1]
            # ui.messageBox('New Length = ' + Length + ' and New Width = ' + Width)
            
            #Set up the user parameters by calling them by name and setting the expression
            #To Length and Width from the CSV
            csvParams = design.userParameters
            csvParams.itemByName('Length').expression = Length
            csvParams.itemByName('Width').expression = Width

            #pathRoot = design.parentDocument.dataFile.parentProject
            # ui.messageBox('Path is ' + path.name + ' ' + path.id)
            
            #Create a new file name using Cabinet plus the values of length and width from the CSV
            newFileName = 'Cabinet - ' + Length + ' x ' + Width
            
            #Save As using the new file name, path from the current active document
            res = document.saveAs(newFileName,path,'','')
            if res:
                app.log(f'{newFileName} - Success')
            else:
                app.log(f'{newFileName} - Fail')

            # if app.activeDocument.isSaved:
            #     ui.messageBox('Success')
            #     return
            # else: 
            #     ui.messageBox('Fail')

    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
Message 5 of 7
MattPerez314
in reply to: MattPerez314

Thank you so much @kandennti !  I tried out your code and it does work as expected, but I can't say that I am 100% sure why.  If you don't mind can you clarify a few things?

 

For the CSV opening you are doing this.

        csvPath = 'Test2.csv'
        with open(csvPath) as f:
            reader = list(csv.reader(f))

Is there a benefit to setting the csv path then using with open vs doing it this way.

        file = open('Test2.csv')
        with file as f:
            reader = list(csv.reader(f))

I was using file = open('...csv')  and then using for line in file: to loop through.  I was pulling all the information from the CSV just fine before.  Can you explain logically why you are reading the CSV into a list and using the loop to go through the list vs reading the csv lines each time?  This seemed to be the thing that fixed the program was loading the csv to a list before the for loop but I am curious about file = open() vs with open(file)?

 

Next you said the reason it is only creating the last file is because there was a new line in my file name.  The code you have here that works and my code has the same file name except you are checking the bool.  I am trying to understand where the new line part of this was/is.  If I understand you are just using this True/False to log a message with Fusion, but it doesn't affect the save happening, just records if it does or does not.

 

So the only thing programmatically I can see different here is dumping the CSV data into a list before the loop, and then looping through the list rather than the CSV.  I was able to use file = open and loop through the csv populating a list before the for loop and the program works as well, so I just want to understand the "why".  This is where some of the help files are tough because they work one way but it may be that way is intended for a specific application.

 

Thanks again for your help!!!!! I appreciate your thorough responses in all posts, not just this one.  I have been digging through api forum help for a long time before posting.

 

Message 6 of 7
kandennti
in reply to: MattPerez314

@MattPerez314 .

 

As for the merits of "with open", since I am Japanese, I can only find sites that explain it in Japanese. The only site I could find is this one.

https://stackoverflow.com/questions/2738365/whats-the-advantage-of-using-with-as-statement-in-python 

The difference is whether close() is required or not.

 

 

As for the new line, I noticed it when outputting the file name.

# Fusion360API Python script

import traceback
import adsk.core
import csv

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

        csvPath = '...Test2.csv'

        app.log('**** csv module not used ****')
        file = open(csvPath)
        for line in file:
            pieces = line.split(',')
            Length = pieces[0]
            Width = pieces[1]

            newFileName = 'Cabinet - ' + Length + ' x ' + Width
            app.log(f'++ {newFileName} ++')


        app.log('**** Use csv module ****')
        with open(csvPath) as f:
            reader = list(csv.reader(f))

        for csvData in reader:
            Length = csvData[0]
            Width = csvData[1]

            newFileName = 'Cabinet - ' + Length + ' x ' + Width
            app.log(f'++ {newFileName} ++')

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

 

Message 7 of 7
MattPerez314
in reply to: kandennti

Thank you again!  I am not sure I understand the file = open any better but that is ok.  Thank you again for your help!!

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report