Scripting - Create a Custom Monitor Submitter

Version: Deadline 9.0

Introduction

As we saw previously in our Plug it in, Plug it in - Creating New Plugins blog entry, it is easy to make a new application plugin, but we still need a way to submit render jobs to the farm to render with this plugin.

Through this blog post, we will be going over the creation process of the SilhouetteFX (TM) Monitor submitter that was added to Deadline 9.0. By using this script as an example, we hope that it will encourage you to make your own submitters or tweak existing submitters to your liking.

Initial Setup

To start the development of a new Monitor submitter, we need to start by creating the necessary Python ® file in the Deadline Repository. First, navigate to [Repository]/custom/scripts/submission, then within this folder create the following file:

  • SilhouetteSubmission.py: This is the main submitter script that will contain the UI and submission code.

The reason we are placing this file in the Repository's custom folder is so that we don't have to modify the one that ships with Deadline. This ensures that your submission script will not be overwritten when you update Deadline in the future. Our version of this script with the same name as the shipping version will also be used by Deadline and the shipping version will be ignored. See the Scripting Overview Documentation for more information about the custom folder.

Writing the Script

Open the SilhouetteSubmission.py file that you added to your Deadline Repository, and start by defining the basic shell of the submitter:

from Deadline.Scripting import *

from DeadlineUI.Controls.Scripting.DeadlineScriptDialog import DeadlineScriptDialog

########################################################################
## Main Function Called By Deadline
########################################################################
def __main__( *args ):
    global scriptDialog
    global settings
    
    scriptDialog = DeadlineScriptDialog()
    scriptDialog.SetTitle( "Submit Silhouette Job To Deadline" )
    scriptDialog.SetIcon( scriptDialog.GetIcon( 'Silhouette' ) )
    
    scriptDialog.AddGrid()
    scriptDialog.AddHorizontalSpacerToGrid( "HSpacer1", 0, 0 )
    submitButton = scriptDialog.AddControlToGrid( "SubmitButton", "ButtonControl", "Submit", 0, 1, expand=False )
    closeButton = scriptDialog.AddControlToGrid( "CloseButton", "ButtonControl", "Close", 0, 2, expand=False )
    closeButton.ValueModified.connect(scriptDialog.closeEvent)
    scriptDialog.EndGrid()
    
    scriptDialog.ShowDialog( False )

The first thing to note here is the creation of the DeadlineScriptDialog. This will be our main UI for the submitter and what we are going to add controls to. It's documentated in a few places in our Scripting API documentation:

In the code above, you can see that we've already added some generic "Submit" and "Close" buttons to the UI. The "Submit" button doesn't do anything at the moment, but we've hooked up the "Close" button so that it closes the submitter when it's pressed.

From here we will want to add the standard Deadline Job options that are present in the majority of our submitters. In order to do this we will look at how the UI is built up in the other submitters that ship with Deadline (no need to re-invent the wheel here):

scriptDialog.AddTabControl( "Tabs", 0, 0 ) #Tabs are not required in your UI, but really help to organise many options in your submitter.

scriptDialog.AddTabPage( "Job Options" ) #Create a tab here.
scriptDialog.AddGrid() #Create a grid here.
scriptDialog.AddControlToGrid( "Separator1", "SeparatorControl", "Job Description", 0, 0, colSpan=2 )

scriptDialog.AddControlToGrid( "NameLabel", "LabelControl", "Job Name", 1, 0, "The name of your job. This is optional, and if left blank, it will default to 'Untitled'.", False )
scriptDialog.AddControlToGrid( "NameBox", "TextControl", "Untitled", 1, 1 )

scriptDialog.AddControlToGrid( "CommentLabel", "LabelControl", "Comment", 2, 0, "A simple description of your job. This is optional and can be left blank.", False )
scriptDialog.AddControlToGrid( "CommentBox", "TextControl", "", 2, 1 )

scriptDialog.AddControlToGrid( "DepartmentLabel", "LabelControl", "Department", 3, 0, "The department you belong to. This is optional and can be left blank.", False )
scriptDialog.AddControlToGrid( "DepartmentBox", "TextControl", "", 3, 1 )
scriptDialog.EndGrid() #End a grid here.

scriptDialog.AddGrid() #Create a new grid here.
scriptDialog.AddControlToGrid( "Separator2", "SeparatorControl", "Job Options", 0, 0, colSpan=3 )

scriptDialog.AddControlToGrid( "PoolLabel", "LabelControl", "Pool", 1, 0, "The pool that your job will be submitted to.", False )
scriptDialog.AddControlToGrid( "PoolBox", "PoolComboControl", "none", 1, 1 )

scriptDialog.AddControlToGrid( "SecondaryPoolLabel", "LabelControl", "Secondary Pool", 2, 0, "The secondary pool lets you specify a Pool to use if the primary Pool does not have any available Slaves.", False )
scriptDialog.AddControlToGrid( "SecondaryPoolBox", "SecondaryPoolComboControl", "", 2, 1 )

scriptDialog.AddControlToGrid( "GroupLabel", "LabelControl", "Group", 3, 0, "The group that your job will be submitted to.", False )
scriptDialog.AddControlToGrid( "GroupBox", "GroupComboControl", "none", 3, 1 )

scriptDialog.AddControlToGrid( "PriorityLabel", "LabelControl", "Priority", 4, 0, "A job can have a numeric priority ranging from 0 to 100, where 0 is the lowest priority and 100 is the highest priority.", False )
scriptDialog.AddRangeControlToGrid( "PriorityBox", "RangeControl", RepositoryUtils.GetMaximumPriority() / 2, 0, RepositoryUtils.GetMaximumPriority(), 0, 1, 4, 1 )

scriptDialog.AddControlToGrid( "TaskTimeoutLabel", "LabelControl", "Task Timeout", 5, 0, "The number of minutes a Slave has to render a task for this job before it requeues it. Specify 0 for no limit.", False )
scriptDialog.AddRangeControlToGrid( "TaskTimeoutBox", "RangeControl", 0, 0, 1000000, 0, 1, 5, 1 )
scriptDialog.AddSelectionControlToGrid( "AutoTimeoutBox", "CheckBoxControl", False, "Enable Auto Task Timeout", 5, 2, "If the Auto Task Timeout is properly configured in the Repository Options, then enabling this will allow a task timeout to be automatically calculated based on the render times of previous frames for the job." )

scriptDialog.AddControlToGrid( "ConcurrentTasksLabel", "LabelControl", "Concurrent Tasks", 6, 0, "The number of tasks that can render concurrently on a single Slave. This is useful if the rendering application only uses one thread to render and your Slaves have multiple CPUs.", False )
scriptDialog.AddRangeControlToGrid( "ConcurrentTasksBox", "RangeControl", 1, 1, 16, 0, 1, 6, 1 )
scriptDialog.AddSelectionControlToGrid( "LimitConcurrentTasksBox", "CheckBoxControl", True, "Limit Tasks To Slave's Task Limit", 6, 2, "If you limit the tasks to a Slave's task limit, then by default, the Slave won't dequeue more tasks then it has CPUs. This task limit can be overridden for individual Slaves by an administrator." )

scriptDialog.AddControlToGrid( "MachineLimitLabel", "LabelControl", "Machine Limit", 7, 0, "", False )
scriptDialog.AddRangeControlToGrid( "MachineLimitBox", "RangeControl", 0, 0, 1000000, 0, 1, 7, 1 )
scriptDialog.AddSelectionControlToGrid( "IsBlacklistBox", "CheckBoxControl", False, "Machine List Is A Blacklist", 7, 2, "" )

scriptDialog.AddControlToGrid( "MachineListLabel", "LabelControl", "Machine List", 8, 0, "Use the Machine Limit to specify the maximum number of machines that can render your job at one time. Specify 0 for no limit.", False )
scriptDialog.AddControlToGrid( "MachineListBox", "MachineListControl", "", 8, 1, colSpan=2 )

scriptDialog.AddControlToGrid( "LimitGroupLabel", "LabelControl", "Limits", 9, 0, "The Limits that your job requires.", False )
scriptDialog.AddControlToGrid( "LimitGroupBox", "LimitGroupControl", "", 9, 1, colSpan=2 )

scriptDialog.AddControlToGrid( "DependencyLabel", "LabelControl", "Dependencies", 10, 0, "Specify existing jobs that this job will be dependent on. This job will not start until the specified dependencies finish rendering.", False )
scriptDialog.AddControlToGrid( "DependencyBox", "DependencyControl", "", 10, 1, colSpan=2 )

scriptDialog.AddControlToGrid( "OnJobCompleteLabel", "LabelControl", "On Job Complete", 11, 0, "If desired, you can automatically archive or delete the job when it completes.", False )
scriptDialog.AddControlToGrid( "OnJobCompleteBox", "OnJobCompleteControl", "Nothing", 11, 1 )
scriptDialog.AddSelectionControlToGrid( "SubmitSuspendedBox", "CheckBoxControl", False, "Submit Job As Suspended", 11, 2, "If enabled, the job will submit in the suspended state. This is useful if you don't want the job to start rendering right away. Just resume it from the Monitor when you want it to render." )
scriptDialog.EndGrid() #End the grid here.

scriptDialog.EndTabPage() #End the current tab here.

scriptDialog.EndTabControl() #End ALL tab controls here.

The first thing to note here are all of the calls to AddControlToGrid. This function can be found in the Scripting Documentation but is best explained in our User Manual under the section: Grid Layout Example Script. After reading this section, you should be well versed on how you can use a Grid to layout your preferred UI of your Monitor submitter to your liking.

Testing the Submitter

Now that we have a basic submitter we can test it to make sure it will open properly. To do this we can run the submitter using the Submit menu in the Deadline Monitor. If your new submitter does not appear in the submit menu, you can use the Synchronize Scripts and Plugins tool found in the Tools Menu to ensure it is up to date.

In our case this will give us the following UI.

It's highly recommended to always have the Console panel visible in your Monitor during testing. Any debug print() statements you add to your script, as well as any error messages, will appear here.

Additional UI Controls

Now that we have the main Job properties set up, we can start adding Silhouette specific Job properties. To do this, we will start by adding some additional controls to the same tab, including controls for the scene file, the Silhouette version, and the frame list:

scriptDialog.AddGrid()
scriptDialog.AddControlToGrid( "Separator3", "SeparatorControl", "Silhouette Options", 0, 0, colSpan=3 )

scriptDialog.AddControlToGrid( "SceneLabel", "LabelControl", "Silhouette File", 1, 0, "The project file to be rendered.", False )
scriptDialog.AddSelectionControlToGrid( "SceneBox", "FileBrowserControl", "", "Silhouette Files (\*.sfx)", 1, 1, colSpan=3 )

scriptDialog.AddControlToGrid( "VersionLabel","LabelControl","Silhouette Version", 2, 0, "The version of Silhouette to render with.", False )
scriptDialog.AddComboControlToGrid( "VersionBox","ComboControl","5",("5",), 2, 1 )
scriptDialog.AddSelectionControlToGrid( "SubmitSceneBox","CheckBoxControl",False,"Submit Silhouette File",2, 2, "If this option is enabled, the project file will be submitted with the job, and then copied locally to the Slave machine during rendering." )

scriptDialog.AddControlToGrid( "FramesLabel", "LabelControl", "Frame List", 3, 0, "The list of frames to render.", False )
scriptDialog.AddControlToGrid( "FramesBox", "TextControl", "", 3, 1 )
scriptDialog.AddControlToGrid( "ChunkSizeLabel", "LabelControl", "Frames Per Task", 3, 2, "This is the number of frames that will be rendered at a time for each job task.", False )
scriptDialog.AddRangeControlToGrid( "ChunkSizeBox", "RangeControl", 1, 1, 1000000, 0, 1, 3, 3 )

scriptDialog.EndGrid()

From here we will make a new tab with the remaining Silhouette specific options to match what we have in our Silhouette submitter:

scriptDialog.AddTabPage( "Advanced Options" )
scriptDialog.AddGrid()
scriptDialog.AddControlToGrid( "Separator4", "SeparatorControl", "Render Overrides", 0, 0, colSpan=4 )

scriptDialog.AddControlToGrid( "SessionLabel", "LabelControl", "Session", 1, 0, "Make the specified session active in the project. If empty the currently selected session will be used to render.", False )
scriptDialog.AddControlToGrid( "SessionBox", "TextControl", "", 1, 1, colSpan=3 )

scriptDialog.AddControlToGrid( "NodeLabel", "LabelControl", "Node", 2, 0, "The specified node in the active session to render. If empty the currently selected node will be used to render.", False )
scriptDialog.AddControlToGrid( "NodeBox", "TextControl", "", 2, 1, colSpan=3 )

scriptDialog.AddControlToGrid( "OutputDirectoryLabel", "LabelControl", "Output Directory", 3, 0, "The output directory to use. If empty the output directory defined in the session will be used.", False )
scriptDialog.AddSelectionControlToGrid( "OutputDirectoryBox", "FolderBrowserControl", "", "", 3, 1, colSpan=3 )

scriptDialog.AddControlToGrid( "OutputFilenameLabel", "LabelControl", "Output Filename", 4, 0, "The output prefix to use. If empty the output prefix defined in the session will be used.", False )
scriptDialog.AddControlToGrid( "OutputFilenameBox", "TextControl", "", 4, 1, colSpan=3 )

scriptDialog.AddSelectionControlToGrid( "FormatCheck", "CheckBoxControl", False, "Override Format", 5, 0, "Override the image file format to be rendered." )
scriptDialog.AddComboControlToGrid( "FormatBox","ComboControl","OpenEXR",( "Cineon","DPX","IFF","JPEG","OpenEXR","PNG","SGI","TIFF","Targa" ), 5, 1, colSpan=3 )

scriptDialog.AddControlToGrid( "ResolutionLabel","LabelControl","Resolution", 6, 0, "The resolution to render with.", False )
scriptDialog.AddComboControlToGrid( "ResolutionBox", "ComboControl", "full",( "full","half","third","quarter" ), 6, 1, colSpan=3 )

scriptDialog.AddControlToGrid( "FieldsLabel","LabelControl","Fields", 7, 0, "Various options to handle fields.", False )
scriptDialog.AddComboControlToGrid( "FieldsBox", "ComboControl", "none",( "none","interlace","aa","bb","bc","cd","dd" ), 7, 1, colSpan=3 )

scriptDialog.AddControlToGrid( "DominanceLabel","LabelControl","Dominance", 8, 0, "Various options to handle fields.", False )
scriptDialog.AddComboControlToGrid( "DominanceBox", "ComboControl", "even",( "even","odd" ), 8, 1, colSpan=3 )

scriptDialog.AddControlToGrid( "AdditionalOptionsLabel", "LabelControl", "Additional Options", 9, 0, "Any additional options to use while rendering.", False )
scriptDialog.AddControlToGrid( "AdditionalOptionsBox", "TextControl", "", 9, 1, colSpan=3 )

scriptDialog.AddControlToGrid( "Separator5", "SeparatorControl", "Script Options", 10, 0, colSpan=4 )

scriptDialog.AddSelectionControlToGrid( "RunScript", "CheckBoxControl", False, "Run Script", 11, 0, "Run Python script after loading the project." )
scriptDialog.AddSelectionControlToGrid( "DisableRender", "CheckBoxControl", False, "Disable Rendering", 11, 1, "If this option is enabled, rendering will be disabled, which might be useful for certain Python script jobs." )
scriptDialog.AddSelectionControlToGrid( "SubmitScriptBox", "CheckBoxControl", False, "Submit Python Script File", 11, 2, "If this option is enabled, the Python script file will be submitted with the job to the Deadline Repository." )

scriptDialog.AddControlToGrid( "ScriptFileLabel", "LabelControl", "Python Script File", 12, 0, "The Python script file to be executed.", False )
scriptDialog.AddSelectionControlToGrid( "ScriptFileBox", "FileBrowserControl", "", "Python Files (\*.py)", 12, 1, colSpan=3 )

scriptDialog.EndGrid()
scriptDialog.EndTabPage()

At this point we now have all of our controls set up in the UI, so we are now able to look into adding logic to these UI controls.

We can start by adding some logic to be able to disable certain controls. The first place that we want to do this is to disable the FormatBox when Override Format is Disabled. To do this we need to create the Python function that we would like to control the logic:

def OverrideFormatChanged( *args ):
    global scriptDialog
    enabled = scriptDialog.GetValue( "FormatCheck" )
    scriptDialog.SetEnabled( "FormatBox", enabled )

We can now connect this function to the FormatCheck UI CheckBoxControl:

overrideFormatBox = scriptDialog.AddSelectionControlToGrid( "FormatCheck", "CheckBoxControl", False, "Override Format", 5, 0, "Override the image file format to be rendered." )
overrideFormatBox.ValueModified.connect( OverrideFormatChanged )

Submitting Jobs

We are now ready to set up the actual submission of the Job. To do this we will be creating a function that will be broken into 3 main sections.

The first section is warning and error checks. In this section we want to check for anything that we are sure won't work when rendering, such as no scene file, or no valid frame list provided. We also want to warn the user of anything that should work but may have unexpected consequences such as rendering scenes using local paths:

def SubmitButtonPressed( *args ):
    global scriptDialog
    
    paddedNumberRegex = Regex( "\\$F([0-9]+)" )
    
    # Check if Silhouette files exist.
    sceneFile = scriptDialog.GetValue( "SceneBox" )
    sceneFileName = Path.GetFileName( sceneFile )
    
    if not os.path.exists( sceneFile ):
        scriptDialog.ShowMessageBox( "Silhouette file: %s does not exist" % sceneFile, "Error" )
        return
    elif sceneFileName == "project.sfx":
        scriptDialog.ShowMessageBox( "Please ensure you select the top-level Silhouette \*.sfx file and NOT the underlying 'project.sfx' file", "Error" )
        return
    elif (PathUtils.IsPathLocal( sceneFile ) and not scriptDialog.GetValue( "SubmitSceneBox" ) ):
        result = scriptDialog.ShowMessageBox( "Silhouette file: " + sceneFile + " is local.Are you sure you want to continue?", "Warning",("Yes","No") )
        if( result=="No" ):
            return

    # Check if a valid frame range has been specified.
    frames = scriptDialog.GetValue( "FramesBox" )
    if( not FrameUtils.FrameRangeValid( frames ) ):
        scriptDialog.ShowMessageBox( "Frame range %s is not valid" % frames, "Error" )
        return

The next section that we want to work on is creating the Job Info and Plugin Info files, which are used to create the Job. These are the same files that we manually created in the Plugin Creation blog post:

# Create job info file.
jobInfoFilename = Path.Combine( GetDeadlineTempPath(), "silhouette_job_info.job" )
writer = StreamWriter( jobInfoFilename, False, Encoding.Unicode )
writer.WriteLine( "Plugin=Silhouette" )
writer.WriteLine( "Name=%s" % jobName )
writer.WriteLine( "Comment=%s" % scriptDialog.GetValue( "CommentBox" ) )
writer.WriteLine( "Department=%s" % scriptDialog.GetValue( "DepartmentBox" ) )
writer.WriteLine( "Pool=%s" % scriptDialog.GetValue( "PoolBox" ) )
writer.WriteLine( "SecondaryPool=%s" % scriptDialog.GetValue( "SecondaryPoolBox" ) )
writer.WriteLine( "Group=%s" % scriptDialog.GetValue( "GroupBox" ) )
writer.WriteLine( "Priority=%s" % scriptDialog.GetValue( "PriorityBox" ) )
writer.WriteLine( "TaskTimeoutMinutes=%s" % scriptDialog.GetValue( "TaskTimeoutBox" ) )
writer.WriteLine( "EnableAutoTimeout=%s" % scriptDialog.GetValue( "AutoTimeoutBox" ) )
writer.WriteLine( "ConcurrentTasks=%s" % scriptDialog.GetValue( "ConcurrentTasksBox" ) )
writer.WriteLine( "LimitConcurrentTasksToNumberOfCpus=%s" % scriptDialog.GetValue( "LimitConcurrentTasksBox" ) )

writer.WriteLine( "MachineLimit=%s" % scriptDialog.GetValue( "MachineLimitBox" ) )
if( bool(scriptDialog.GetValue( "IsBlacklistBox" )) ):
    writer.WriteLine( "Blacklist=%s" % scriptDialog.GetValue( "MachineListBox" ) )
else:
    writer.WriteLine( "Whitelist=%s" % scriptDialog.GetValue( "MachineListBox" ) )

writer.WriteLine( "LimitGroups=%s" % scriptDialog.GetValue( "LimitGroupBox" ) )
writer.WriteLine( "JobDependencies=%s" % scriptDialog.GetValue( "DependencyBox" ) )
writer.WriteLine( "OnJobComplete=%s" % scriptDialog.GetValue( "OnJobCompleteBox" ) )

if( bool(scriptDialog.GetValue( "SubmitSuspendedBox" )) ):
    writer.WriteLine( "InitialStatus=Suspended" )

writer.WriteLine( "Frames=%s" % frames )
writer.WriteLine( "ChunkSize=%s" % scriptDialog.GetValue( "ChunkSizeBox") )

outputDirectory = scriptDialog.GetValue( "OutputDirectoryBox" )
if not outputDirectory == "":
    writer.WriteLine( "OutputDirectory0=%s" % outputDirectory )

writer.Close()

# Create plugin info file.
pluginInfoFilename = Path.Combine( GetDeadlineTempPath(), "silhouette_plugin_info.job" )
writer = StreamWriter( pluginInfoFilename, False, Encoding.Unicode )

dataFile = Path.Combine( sceneFile, "project.sfx" )

if(not scriptDialog.GetValue( "SubmitSceneBox" ) ):
    writer.WriteLine( "SceneFile=%s" % dataFile )

writer.WriteLine( "Version=%s" % scriptDialog.GetValue( "VersionBox" ))
writer.WriteLine( "Session=%s" % scriptDialog.GetValue( "SessionBox" ) )
writer.WriteLine( "Node=%s" % scriptDialog.GetValue( "NodeBox" ) )
writer.WriteLine( "OutputDirectory=%s" % outputDirectory )
writer.WriteLine( "OutputFilename=%s" % scriptDialog.GetValue( "OutputFilenameBox" ) )

if scriptDialog.GetValue( "FormatCheck" ):
    writer.WriteLine( "OutputFormat=%s" % scriptDialog.GetValue( "FormatBox") )
else:
    writer.WriteLine( "OutputFormat=" )

writer.WriteLine( "AdditionalOptions=%s" % scriptDialog.GetValue( "AdditionalOptionsBox" ) )

writer.WriteLine( "Resolution=%s" % scriptDialog.GetValue( "ResolutionBox" ) )
writer.WriteLine( "Fields=%s" % scriptDialog.GetValue( "FieldsBox" ) )
writer.WriteLine( "Dominance=%s" % scriptDialog.GetValue( "DominanceBox" ) )

writer.WriteLine( "RunScript=%s" % runScript )
writer.WriteLine( "DisableRender=%s" % disableRender )

if submitScript:
    writer.WriteLine( "ScriptFile=%s" % Path.GetFileName( scriptFile ) )
else:
    writer.WriteLine( "ScriptFile=%s" % scriptFile )

writer.Close()

Once we have the files created the last thing we need to handle is submitting the Jobs to Deadline. In order to do this we will create a list containing all of the files that we want to submit, starting with the Job Info and Plugin Info files:

arguments = StringCollection()

arguments.Add( jobInfoFilename )
arguments.Add( pluginInfoFilename )

if scriptDialog.GetValue( "SubmitSceneBox" ):
    arguments.Add( dataFile )

if runScript and submitScript:
    arguments.Add( scriptFile )

jobResult = ClientUtils.ExecuteCommandAndGetOutput( arguments )

scriptDialog.ShowMessageBox( jobResult, "Submission Results" )

If we now connect this function to the submit button the same way we did with the Format Override checkbox, we have everything we need in our submission script to submit Jobs to the farm!

Adding Sticky Settings

One thing you may notice is that none of the UI settings will stick around if you close the submitter and reopen it. In order to make Deadline save the sticky settings we need to choose where we want to save the sticky settings and enable the saving and loading of sticky settings.

By default the Deadline Monitor submitters save the sticky settings into the user's Deadline settings directory, using a handy API function to easily pull this path:

def GetSettingsFilename():
    return Path.Combine( GetDeadlineSettingsPath(), "SilhouetteSettings.ini" )

In order to actually enable sticky settings, we need to compile a list of all of the control names in the UI that we want to make sticky. We then load the settings from the settings file if it exists and tell Deadline to save the settings when our UI is closed:

settings = ( "DepartmentBox","CategoryBox","PoolBox","SecondaryPoolBox","GroupBox","PriorityBox","MachineLimitBox","IsBlacklistBox","MachineListBox","LimitGroupBox","SceneBox","FramesBox","ChunkSizeBox","VersionBox","SubmitSceneBox" )
scriptDialog.LoadSettings( GetSettingsFilename(), settings )
scriptDialog.EnabledStickySaving( settings, GetSettingsFilename() )

At this point we now have a submitter that will allow users to submit a Silhouette Job to Deadline, and will save the settings that were used for submission for future use. To see the complete submission script, simply browse your Deadline Repository to [Repository]/scripts/submission and open SilhouetteSubmission.py in a text editor.

Additional Information

In addition to the information found here, the Deadline User Manual contains useful information on creating new plugins and the information available to them, and the Deadline Scripting Reference contains documentation for all of the available classes and functions that are exposed to Python scripts in Deadline.

There is also an ever growing GitHub project that contains many useful scripts that are not shipped with Deadline, including a simple example Monitor Submitter.

Silhouette and SilhouetteFX are trademarks of SilhouetteFX, LLC.

Python is a registered trademark of the Python Software Foundation.