Announcements
Attention for Customers without Multi-Factor Authentication or Single Sign-On - OTP Verification rolls out April 2025. Read all about it here.

Why does this crash fusion??

thomas_vijf
Participant

Why does this crash fusion??

thomas_vijf
Participant
Participant

Hi All,

I have made a script that easily exports all selected body's as a .step file. Now i am making that script into a add-in with a nice button in a custom panel along with some other tools that i created. The export tool copy's the body's one by one to a new document, exports it, and closes the new document and goes to the next body til all the selected body's are exported. The script always worked excellent but now fusion crashes one the 'newDoc.colse(False)' line. If i comment that line out the script exports all selected body's as it should but i am left with several unsaved new open documents.

Does anybody have a clue on what might be the problem? I am kinda stuck here. if i place the close line after the 'for loop' it closes the last document without crashing but when it is part of the 'for loop' it causes problems.

This is the problem part of my code:

class sExportCommandExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs = adsk.core.CommandEventArgs.cast(args)

            # Code to react to the event.
            app = adsk.core.Application.get()
            ui  = app.userInterface
            des = adsk.fusion.Design.cast(app.activeProduct) 

            # Code to react to the event.
            selInput1 = adsk.core.SelectionCommandInput.cast(eventArgs.command.commandInputs.itemById('selectionInput1'))
            ui.messageBox('There are ' + str(selInput1.selectionCount) + ' items selected.')

            folderDlg = ui.createFolderDialog()
            folderDlg.title = 'Selecteer een map om de .step bestanden neer te zetten.' 
            
            if folderDlg.showDialog() == adsk.core.DialogResults.DialogOK:
                path = folderDlg.folder
                ui.messageBox('Je .step bestanden worden hier neergezet: \n\n' + path + '\n\nAls een bestand al bestaat word deze overschreven!')       
                
                # Get the total number of bodies to use in the progress bar.
                bodyTotal = selInput1.selectionCount
                comp = adsk.fusion.Component.cast(None)
        
                progDialog = ui.createProgressDialog()
                progDialog.isBackgroundTranslucent = False
                progDialog.isCancelButtonShown = True
                progDialog.show('Body Export', 'Exporting body %v of %m...', 0, bodyTotal, 0)            
                
                bodyCount = 0

                #while bodyCount < bodyTotal:
                for X in range(bodyTotal):
                    bodyy = selInput1.selection(bodyCount)
                    bodyEntity = bodyy.entity
                    progDialog.progressValue = bodyCount+1
                    
                    if progDialog.wasCancelled:
                        ui.messageBox('Het exporteren is ge-canceled!') 
                        break       
                    
                    #ui.messageBox('exporting body' + str(bodyCount + 1)) 

                    # Create a new direct modeling design.
                    newDoc = app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType, True)
                    des = adsk.fusion.Design.cast(newDoc.products.itemByProductType('DesignProductType'))                
                    des.designType = adsk.fusion.DesignTypes.DirectDesignType

                    # Copy the body in the new design.
                    newRoot = des.rootComponent
                    bodyEntity.copyToComponent(newRoot)
                    comp = bodyEntity.parentComponent                
                    Filename = comp.name + ' - ' + bodyEntity.name 
                    
                    design = adsk.fusion.Design.cast(app.activeProduct)
                    option = design.exportManager.createSTEPExportOptions(path + '/' + Filename + '.step', design.rootComponent)
                    design.exportManager.execute(option)
                
                    newDoc.close(False) #sluit zonder save ################this line causes problems

                    bodyCount += 1

                ui.messageBox('Het exporteren is gelukt!')
                progDialog.hide()

        except:
            if ui:
                ui.messageBox(_('Panel command created failed: {}').format(traceback.format_exc()))

This is the entire code:

#Author- Thomas Vijf
#Description- Collctie handige tools die het leven leuker maken.

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

# Global list to keep all event handlers in scope.
# This is only needed with Python.
handlers = []

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

        # Get the CommandDefinitions collection.
        cmdDefs = ui.commandDefinitions

        # Get all workspaces:
        allWorkspaces = ui.workspaces
        # Get the Design workspace:
        designWorkspace = allWorkspaces.itemById('FusionSolidEnvironment')
        if (designWorkspace):
            # Get all the tabs for the Design workspace:
            allDesignTabs = designWorkspace.toolbarTabs
            if (allDesignTabs.count > 0):
                # get the solid tab
                solidTab = allDesignTabs.itemById('SolidTab')

                if(solidTab):
                    allNewDesignTabPanels = solidTab.toolbarPanels

                    # Has the panel been added already?
                    # You'll get an error if you try to add this more than once to the tab.              
                    brandNewDesignPanel = None
                    brandNewDesignPanel = allNewDesignTabPanels.itemById('buitelaarToolsPanel')
                    if brandNewDesignPanel is None:
                        # We have not added the panel already.  Go ahead and add it.
                        brandNewDesignPanel = allNewDesignTabPanels.add('buitelaarToolsPanel', 'Buitelaar Tools')

                    if brandNewDesignPanel:
                        # We want this panel to be visible:
                        brandNewDesignPanel.isVisible = True
                        # Access the controls that belong to the panel:
                        newPanelControls = brandNewDesignPanel.controls

                        # voeg knop toe in pannel
                        ##radius edit
                        # Create a button command definition.
                        buttonSample = cmdDefs.addButtonDefinition('MyButtonDefIdPython', 
                                                                'Radius Edit', 
                                                                'Eenvoudig de binnen of buiten radius aanpassen.',
                                                                './/Resources/radius')
                        
                        # Connect to the command created event.
                        sampleCommandCreated = radiusCommandCreatedEventHandler()
                        buttonSample.commandCreated.add(sampleCommandCreated)
                        handlers.append(sampleCommandCreated)
                        
                        # Get the ADD-INS panel in the model workspace. 
                        #addInsPanel = ui.allToolbarPanels.itemById('SolidModifyPanel')
                        addInsPanel = ui.allToolbarPanels.itemById('buitelaarToolsPanel')
                        
                        # Add the button to the bottom of the panel.
                        buttonControl = addInsPanel.controls.addCommand(buttonSample)

                        #buttonControl.isPromotedByDefault = True
                        buttonControl.isPromoted = True

                        ##quick export
                        buttonSample = cmdDefs.addButtonDefinition('MyButtonDefIdPython2', 
                                                   'Quick export', 
                                                   'Snel exporteren van Step bestanden. Bestanden die al bestaan worden overschreven!',
                                                   './/Resources/quickexport')
        
                        # Connect to the command created event.
                        sampleCommandCreated = qExportCommandCreatedEventHandler()
                        buttonSample.commandCreated.add(sampleCommandCreated)
                        handlers.append(sampleCommandCreated)
                        
                        # Get the ADD-INS panel in the model workspace. 
                        addInsPanel = ui.allToolbarPanels.itemById('buitelaarToolsPanel')
                        
                        # Add the button to the bottom of the panel.
                        buttonControl = addInsPanel.controls.addCommand(buttonSample)

                        #buttonControl.isPromotedByDefault = True
                        buttonControl.isPromoted = True

                        ##selected export
                        buttonSample = cmdDefs.addButtonDefinition('MyButtonDefIdPython3', 
                                                   'Export selected', 
                                                   'Exporteer alle geselecteerde bodys als losse step files.',
                                                   './/Resources/exportselected')
        
                        # Connect to the command created event.
                        sampleCommandCreated = sExportCommandCreatedEventHandler()
                        buttonSample.commandCreated.add(sampleCommandCreated)
                        handlers.append(sampleCommandCreated)
                        
                        # Get the ADD-INS panel in the model workspace. 
                        addInsPanel = ui.allToolbarPanels.itemById('buitelaarToolsPanel')
                        
                        # Add the button to the bottom of the panel.
                        buttonControl = addInsPanel.controls.addCommand(buttonSample)

                        #buttonControl.isPromotedByDefault = True
                        buttonControl.isPromoted = True
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


# Event handler for the commandCreated event.
class radiusCommandCreatedEventHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        eventArgs = adsk.core.CommandCreatedEventArgs.cast(args)
        cmd = eventArgs.command

        # Connect to the execute event.
        onExecute = radiusCommandExecuteHandler()
        cmd.execute.add(onExecute)
        handlers.append(onExecute)

class qExportCommandCreatedEventHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        eventArgs = adsk.core.CommandCreatedEventArgs.cast(args)
        cmd = eventArgs.command

        # Connect to the execute event.
        onExecute = qExportCommandExecuteHandler()
        cmd.execute.add(onExecute)
        handlers.append(onExecute)

# 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()))

class sExportCommandCreatedEventHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs = adsk.core.CommandCreatedEventArgs.cast(args)

            # Get the command
            cmd = eventArgs.command

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

            # Get the CommandInputs collection to create new command inputs.            
            inputs = cmd.commandInputs

            # Create a selection input.
            selInput1 = inputs.addSelectionInput('selectionInput1', 'Select inside radius', 'Select inside radius')
            selInput1.addSelectionFilter('SolidBodies')
            selInput1.setSelectionLimits(1)

            # Connect to the execute event.
            onExecute = sExportCommandExecuteHandler()
            cmd.execute.add(onExecute)
            handlers.append(onExecute)

            #ui.messageBox(_('Panel command created successfully'))
        except:
            if ui:
                ui.messageBox(_('Panel command created failed: {}').format(traceback.format_exc()))


# Event handler for the execute event.
class radiusCommandExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        eventArgs = adsk.core.CommandEventArgs.cast(args)

        # Code to react to the event.
        app = adsk.core.Application.get()
        ui  = app.userInterface
        
        # Get the root component of the active design
        product = app.activeProduct
        design = adsk.fusion.Design.cast(product)
        rootComp = design.rootComponent
    
        mainBodyFace = adsk.fusion.BRepFace.cast(ui.selectEntity('Selecteer de radius.', 'Faces').entity)
        mainBody = mainBodyFace.body

        faceCount = 0
        smallRadius = 0
        
        selected = ui.activeSelections
        selected.clear()

        tangentialFaces = mainBodyFace.tangentiallyConnectedFaces
        for face in tangentialFaces:
            #ui.messageBox("test")
            if face.geometry.objectType == adsk.core.Cylinder.classType():
                #ui.messageBox("got radius")
                selected.add(face)
            
        alignCommand = ui.commandDefinitions.itemById('FusionPressPullCommand')
        alignCommand.execute()

class qExportCommandExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        eventArgs = adsk.core.CommandEventArgs.cast(args)

        # Code to react to the event.
        app = adsk.core.Application.get()
        ui  = app.userInterface
        des = adsk.fusion.Design.cast(app.activeProduct) 

        file = app.activeDocument.dataFile
        folder = file.parentFolder

        folderDlg = ui.createFolderDialog()
        folderDlg.title = 'Selecteer een map om de .step bestanden neer te zetten.' 
        
        if folderDlg.showDialog() == adsk.core.DialogResults.DialogOK:
            path = folderDlg.folder
            
    #        dialogResult = ui.messageBox('Je .step bestanden worden hier neergezet: \n\n' + path + '\n\nAls een bestand al bestaat word deze overschreven!','info tekst', 1)       
                       
    #        if dialogResult == adsk.core.DialogResults.DialogCancel:            
    #            return
            # Get the total number of bodies to use in the progress bar.

            Filename = des.rootComponent.name

            progDialog = ui.createProgressDialog()
            progDialog.isBackgroundTranslucent = False
            progDialog.isCancelButtonShown = True
            progDialog.show('STEP voor Buislaser', 'Exporting design %m...', 0, 1, 0) 

            option = des.exportManager.createSTEPExportOptions(path + '/' + Filename + '.step', des.rootComponent)
            des.exportManager.execute(option)
            
            app.activeDocument.save('saved_by_exportscript')
            #app.activeDocument.close(False)
            des = adsk.fusion.Design.cast(app.activeProduct)
                
            progDialog.hide()

class sExportCommandExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs = adsk.core.CommandEventArgs.cast(args)

            # Code to react to the event.
            app = adsk.core.Application.get()
            ui  = app.userInterface
            des = adsk.fusion.Design.cast(app.activeProduct) 

            # Code to react to the event.
            selInput1 = adsk.core.SelectionCommandInput.cast(eventArgs.command.commandInputs.itemById('selectionInput1'))
            ui.messageBox('There are ' + str(selInput1.selectionCount) + ' items selected.')

            folderDlg = ui.createFolderDialog()
            folderDlg.title = 'Selecteer een map om de .step bestanden neer te zetten.' 
            
            if folderDlg.showDialog() == adsk.core.DialogResults.DialogOK:
                path = folderDlg.folder
                ui.messageBox('Je .step bestanden worden hier neergezet: \n\n' + path + '\n\nAls een bestand al bestaat word deze overschreven!')       
                
                # Get the total number of bodies to use in the progress bar.
                bodyTotal = selInput1.selectionCount
                comp = adsk.fusion.Component.cast(None)
        
                progDialog = ui.createProgressDialog()
                progDialog.isBackgroundTranslucent = False
                progDialog.isCancelButtonShown = True
                progDialog.show('Body Export', 'Exporting body %v of %m...', 0, bodyTotal, 0)            
                
                bodyCount = 0

                #while bodyCount < bodyTotal:
                for X in range(bodyTotal):
                    bodyy = selInput1.selection(bodyCount)
                    bodyEntity = bodyy.entity
                    progDialog.progressValue = bodyCount+1
                    
                    if progDialog.wasCancelled:
                        ui.messageBox('Het exporteren is ge-canceled!') 
                        break       
                    
                    #ui.messageBox('exporting body' + str(bodyCount + 1)) 

                    # Create a new direct modeling design.
                    newDoc = app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType, True)
                    des = adsk.fusion.Design.cast(newDoc.products.itemByProductType('DesignProductType'))                
                    des.designType = adsk.fusion.DesignTypes.DirectDesignType

                    # Copy the body in the new design.
                    newRoot = des.rootComponent
                    bodyEntity.copyToComponent(newRoot)
                    comp = bodyEntity.parentComponent                
                    Filename = comp.name + ' - ' + bodyEntity.name 
                    
                    design = adsk.fusion.Design.cast(app.activeProduct)
                    option = design.exportManager.createSTEPExportOptions(path + '/' + Filename + '.step', design.rootComponent)
                    design.exportManager.execute(option)
                
                    newDoc.close(False) #sluit zonder save

                    bodyCount += 1

                ui.messageBox('Het exporteren is gelukt!')
                progDialog.hide()

        except:
            if ui:
                ui.messageBox(_('Panel command created failed: {}').format(traceback.format_exc()))



def stop(context):
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        
        # Clean up the UI.
        cmdDef = ui.commandDefinitions.itemById('MyButtonDefIdPython')
        cmdDef2 = ui.commandDefinitions.itemById('MyButtonDefIdPython2')
        cmdDef3 = ui.commandDefinitions.itemById('MyButtonDefIdPython3')
        if cmdDef:
            cmdDef.deleteMe() # delete command
        if cmdDef2:
            cmdDef2.deleteMe() # delete command
        if cmdDef3:
            cmdDef3.deleteMe() # delete command
            
        addinsPanel = ui.allToolbarPanels.itemById('buitelaarToolsPanel')
        cntrl = addinsPanel.controls.itemById('MyButtonDefIdPython')
        cntrl2 = addinsPanel.controls.itemById('MyButtonDefIdPython2')
        cntrl3 = addinsPanel.controls.itemById('MyButtonDefIdPython3')
        if cntrl:
            cntrl.deleteMe() # delete de command knop in pannel
        if cntrl2:
            cntrl2.deleteMe() # delete de command knop in pannel
        if cntrl3:
            cntrl3.deleteMe() # delete de command knop in pannel

        addinsPanel.deleteMe() # delete het pannel
            
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))	

I hope someone can push me in the right direction.

 

Regards,

Thomas Vijf

 

0 Likes
Reply
687 Views
6 Replies
Replies (6)

thomas_vijf
Participant
Participant

Anyone??

I spent some more evenings on this problem but i cant seem to understand why this causes Fusion to freeze and crash.

Using 'closed = app.activeDocument.close(False)' inside a for loop in the ExecuteHandler of a add-on makes it crash.

Using this line once after the for loop makes it close the document succesfully without crashing. So i think that is kinda strange.

Does anyone know what i am doing wrong? This little thing is driving me crazy😫

 

Regards,

Thomas

 

0 Likes

goyals
Autodesk
Autodesk

Once document is closed, everything related to that either selection or entity is gone and referring them in for loop after document is closed might be leading to crash.



Shyam Goyal
Sr. Software Dev. Manager
0 Likes

thomas_vijf
Participant
Participant

I found out that it has something to do with the command inputs. If i remove the command inputs and everything that has to do with it, and select the bodies before code runs and then use the activeSelection to get the bodies i want, the new documents it creates when exporting those bodies close fine in the for loop.

 

So the so the commandInputs selectionInput in the CommandCreatedEventHandler are causing troubles with the closing of the documents in the ExecuteHandler.

 

For now i just removed the nice dialog box with the selection input and select the bodies before hand, but it would be nice if it works with the selection input.

Any idea's?

 

Regards,

Thomas

0 Likes

MichaelT_123
Advisor
Advisor

Hi Mr.Thomas Vijif,

 

... interrogate the selection content. Identify the entities there, store them, name them,... and use then as required later on.

 

Regards

MichaelT

 

MichaelT
0 Likes

thomas_vijf
Participant
Participant

Hi MichaelT,

 

I put all the bodies in a objectCollection at the beginning and get them from there when i need them but that doesn't seem to make it work.

This is what i have at the moment:

class qExportCommandExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs = adsk.core.CommandEventArgs.cast(args)

            # Code to react to the event.
            app = adsk.core.Application.get()
            ui  = app.userInterface
            des = adsk.fusion.Design.cast(app.activeProduct) 

            Filename = des.rootComponent.name

            fileDlg = ui.createFileDialog()
            fileDlg.title = 'Selecteer een map om de .step bestanden neer te zetten.' 
            fileDlg.initialFilename = Filename
            
            if fileDlg.showSave() == adsk.core.DialogResults.DialogOK:
                path = fileDlg.filename

                progDialog = ui.createProgressDialog()
                progDialog.isBackgroundTranslucent = False
                progDialog.isCancelButtonShown = True
                progDialog.show('STEP voor Buislaser', 'Exporting design %m...', 0, 1, 0) 

                option = des.exportManager.createSTEPExportOptions(path + '.step', des.rootComponent)
                des.exportManager.execute(option)
                
                # ook gelijk opslaan als het niet een nieuw document is
                if Filename != '(Unsaved)':
                    app.activeDocument.save('saved_by_exportscript')

                des = adsk.fusion.Design.cast(app.activeProduct)
                    
                progDialog.hide()

        except:
            if ui:
                ui.messageBox(_('Panel command created failed: {}').format(traceback.format_exc()))

class sExportCommandExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            eventArgs = adsk.core.CommandEventArgs.cast(args)

            # Code to react to the event.
            app = adsk.core.Application.get()
            ui  = app.userInterface
            des = adsk.fusion.Design.cast(app.activeProduct) 

            # Code to react to the event.
            selInput1 = adsk.core.SelectionCommandInput.cast(eventArgs.command.commandInputs.itemById('selectionInput1'))
            # Get the total number of bodies to use in the progress bar.
            bodyTotal = selInput1.selectionCount

            bodyCount = 0

            bodyCollection = adsk.core.ObjectCollection.create()

            for X in range(bodyTotal):
                body = selInput1.selection(bodyCount)
                bodyEntity = body.entity
                bodyCollection.add(bodyEntity)
                bodyCount += 1
            
            folderDlg = ui.createFolderDialog()
            folderDlg.title = 'Selecteer folder to put the .step files.' 
            
            if folderDlg.showDialog() == adsk.core.DialogResults.DialogOK:
                path = folderDlg.folder
                
                comp = adsk.fusion.Component.cast(None)

                progDialog = ui.createProgressDialog()
                progDialog.isBackgroundTranslucent = False
                progDialog.isCancelButtonShown = True
                progDialog.show('Body Export', 'Exporting body %v of %m...', 0, bodyTotal, 0)            

                bodyCount = 0

                for body in bodyCollection:
                    bodyEntity = body
                    progDialog.progressValue = bodyCount+1
                    
                    if progDialog.wasCancelled:
                        ui.messageBox('exporting is canceled!') 
                        break       

                    # Create a new direct modeling design.
                    newDoc = app.documents.add(adsk.core.DocumentTypes.FusionDesignDocumentType, True)
                    des = adsk.fusion.Design.cast(newDoc.products.itemByProductType('DesignProductType'))                
                    des.designType = adsk.fusion.DesignTypes.DirectDesignType

                    # Copy the body in the new design.
                    newRoot = des.rootComponent
                    bodyEntity.copyToComponent(newRoot)
                    comp = bodyEntity.parentComponent                
                    Filename = comp.name + ' - ' + bodyEntity.name 
                    
                    design = adsk.fusion.Design.cast(app.activeProduct)
                    option = design.exportManager.createSTEPExportOptions(path + '/' + Filename + '.step', design.rootComponent)
                    design.exportManager.execute(option)
                
                    closed = newDoc.close(False) #close without save ## comment out this line and it works without caching but leaves a lot of open documents.

                    bodyCount += 1

                progDialog.hide()

        except:
            if progDialog:
                progDialog.hide()

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

Can you point out what i am doing wrong? I realy dont understand why this is not working when using the selection inputs.

 

Regards,

Thomas

0 Likes

MichaelT_123
Advisor
Advisor

Hi Mr Thomas Vijf,

 

The problem is mostly due to overestimation of copyToComponent() method.

As far as I understand, it operates within the paradigm of one and only one and the current design.

The same is applied to structure of bRepBody. So the code as innocent as it looks makes attempt to break into other design universe. It is possible... but the death must come first, and this is what  you are experiencing (...virtually).

 

                    # Copy the body in the new design.
                    newRoot = des.rootComponent
                    bodyEntity.copyToComponent(newRoot)

How to avoid the later?

In order for an object to cross the border between two separate worlds, it must be sanitized first. In this particular case the natural choice of the disinfectant would be temporaryBRepManager MASTER and its paragon exportToFile()

 
I hope that the small modification to the code (with addition of bodies names recovery) should alleviate your predicament. 
 
With Regards
MichaelT
MichaelT
0 Likes