Hey guys, so this is a very simple but useful example of how to easily build an fbx exporter for maya with python. By no means, this one covers all export capabilities which maya offers but it has some nice ideas and concepts which are worth showing to you.

For this one I’ve used the built-in maya GUI. It is overall straight forward, only a few things I want to point out. First, for easier readability and accessibility, I always declare class member on the top and not just somewhere in the code by typing self.myVariable.

import maya.cmds as cmds
import maya.mel as mel
import os

##############
###
### FBX exporter class which can export multiple poly objects 
### it puts each object on the origin point, freezes the transform, 
###	deletes the construction history and exports them into a specified 
### directory with specified name
###
#############
class FBXExporterWindow:

    windowName = 'FBXExporterWindow'
    textfieldGroupName = 'directoryPathTextfield'
    columnLayoutName = 'columnLayout'
    exportButtonName = 'exportButton'
    textfieldName = 'fileNameTextfield'
    textFieldUserMessage = 'textFieldUserMessage'

    #opens the window 
    def open(self):
        # safety check for window
        self.__deleteAndResetWindow__()
        #creates the window
        exporterWindow = cmds.window(self.windowName, title="FBX exporter", iconName=self.windowName, widthHeight=(280, 120))
        #adds all ui elements
        cmds.columnLayout(self.columnLayoutName, w=375, adjustableColumn=False, parent=self.windowName )
        cmds.textField(self.textfieldName,placeholderText='Enter Optional File Name', text='',parent=self.columnLayoutName, width=150)
        cmds.textFieldButtonGrp(self.textfieldGroupName,placeholderText='Directory Path',label='Save Location', cw3=[80,190,50], text='', buttonLabel='Browse', buttonCommand=self.__openDirectoryBrowser__, parent=self.columnLayoutName)
        cmds.button(self.exportButtonName, label="Export", parent=self.columnLayoutName, width = 322, command = self.startExport)
        cmds.separator(parent=self.columnLayoutName, height=10)
        cmds.text(self.textFieldUserMessage,visible=False, parent=self.columnLayoutName) 
        #shows the window 
        cmds.showWindow(exporterWindow)

    #removes and resets the window
    def __deleteAndResetWindow__(self):
        # deletes old window and preference, if it still exists
        if(cmds.window(self.windowName, query=True, exists=True)):
            cmds.deleteUI(self.windowName)

        if(cmds.windowPref(self.windowName, query=1, exists=1)):
            cmds.windowPref(self.windowName, r=1)

This part of the code sets up the general UI elements inside of a layout. It provides basic functionality for recreating the window and voids duplicates.

    ####################
    #buttonevents
    ####################

    #sets the path of the textfield
    def __setPath__(self,directoryPath):
        cmds.textFieldButtonGrp(self.textfieldGroupName, edit=True, text=str(directoryPath))

    # exports the objects
    def __startExport__(self,path):

        selectedObjects = cmds.ls(selection=True)

        # error handling in case nothing got selected
        if len(selectedObjects) == 0:
            self.__showUserMessage__("No Objects are selected. Abbort!", False)
            return


        filePathStr = cmds.textFieldButtonGrp(self.textfieldGroupName, query=True, text=True).strip()

        # error handling in case no path has been provided
        if len(filePathStr) == 0:
            self.__showUserMessage__("No path has been chosen. Abbort!", False)
            return            

        counter = 0

        for obj in selectedObjects:

            #creating a duplicate so we do not change our object
            duplicate = cmds.duplicate(obj)

            # reseting position to center with the help of a point constraint
            self.__setPositionToOrigin__(duplicate)

            #setting up the export path
            textFieldContent = cmds.textField(self.textfieldName,query=True,text=True).strip()

            if len(textFieldContent) == 0:
                textFieldContent = obj
                
            extension = '{0}.fbx'.format(counter)
            obj_export = filePathStr + "/" + textFieldContent + extension

            counter+=1

            # freezes transform            
            self.__freezeTransform__(duplicate)

            try:
                self.__deleteConstructionHistory__(duplicate)
                # calling mel fbx export command as there is no python equivalent
                mel.eval('FBXExport -f "{}" -s'.format(obj_export))
            except:
                self.__showUserMessage__("Ignoring object named: '%s'. Export failed, probably not a polygonal object. "%(obj), False)

            #after exporting, lets delete the duplicate
            cmds.delete(duplicate)

        self.__showUserMessage__("Exporting Complete!", True)
        

    #opens the browser to pick the directory
    def __openDirectoryBrowser__(self):
        path = cmds.fileDialog2(fileMode=3, dialogStyle=1, caption='Choose Directory')

        if path != None:
            self.setPath(path[0])

Following you find three helper methods

    ####################
    ### helper methods
    ####################
    
    #sets position to origin
    def __setPositionToOrigin__(self,obj):
        temporaryObject = 'gridCenter'        
        cmds.spaceLocator(name=temporaryObject)
        cmds.select(obj)
        cmds.move(temporaryObject)
        cmds.pointConstraint(temporaryObject, obj)
        cmds.pointConstraint(remove=True)
        cmds.delete(temporaryObject)
    
    #freezes the transform setting all rotation,scales and position values to default
    def __freezeTransform__(self, obj):
        cmds.select(obj)
        #normals = 2, the normals on polygonal objects will be frozen
        cmds.makeIdentity( apply=True, translate=1, rotate=1, scale=1, normal=2 )
        
    #deletes the construction history of an object
    def __deleteConstructionHistory__(self,obj):
        #lets make sure there's only the target object selected
        cmds.select(obj, replace=True)
        #removes full construction history
        cmds.delete(constructionHistory=True)

This is a nice one. Only adding error or warning messages to the log is a bit unhandy, so I’ve prepared a field which shows errors in the window to make sure that the user always has nice feedback on his actions.

 # show a message to the user 
def __showUserMessage__(self, message, isPositiveMessage): 
    colorHighlight = [1,0,0] 
    if isPositiveMessage: 
        colorHighlight = [0,1,0] 
        cmds.text(self.textFieldUserMessage,edit=True, label=message,visible=True, backgroundColor = colorHighlight)

That’s it. Thanks for reading and have a nice day!