[code]#Python.NET
########################################################################
import os, sys, time, re, webbrowser
########################################################################
from System import *
from System.Collections.Specialized import *
from System.Drawing import *
from System.IO import *
from System.Text import *
from System.Threading import *
from System.Windows.Forms import *
from Deadline.Scripting import *
########################################################################
Globals
########################################################################
scriptDialog = None
settings = None
shotgunPath = None
logBox = None
appName = “” #the app name calling the script (so we can stash sticky settings separately per application)
sgURL = “” #the base URL for the studio’s SG db
sgConfig = {} #the shotgun configuration settings (taken from shotgun.dlinit)
currentUser = None #connected user
sgProjectDict = {} #dictionary of Shotgun Projects
sgTaskDict = {} #dictionary of Shotgun Tasks
sgEntityDict = {} #dictionary of Shotgun Entities
versionTemplates = [] #list of version templates
sgVersionDict = None #dictionary of Shotgun Versions
shotgunImported = False #keeps track of whether or not we added Shotgun to the PATH
updatingUI = False #variable used to bypass event handlers when values change programmatically
statusMessage = “” #current status
errorMessage = “”
logMessages = [""]
stickySettings = {} #a dictionary of sticky settings
advancedMode = False #tracks whether or not we’re using advanced Mode
versionSelectMode = False # tracks if the control is used to select an existing version
workerThread = None #Thread to do actual work in the background
progressThread = None #Thread for updating progress bar, in order to look “busy”
########################################################################
Main Function Called By Deadline
########################################################################
def main( *args ):
global advancedMode
global versionSelectMode
global logBox
global scriptDialog
global settings
global shotgunPath
global versionTemplates
global appName
global newEntityButton
global sgURL
global sgConfig
#Get the app name from the args (if it's there)
if len( args ) > 0:
appName = args[0]
if len( args ) > 1 and args[1].lower() == "selectversion":
versionSelectMode = True
scriptDialog = DeadlineScriptEngine.GetScriptDialog()
AddShotgunToPath()
try:
#Try to import shotgun stuff; fail nicely if it doesn't work
import ShotgunUtils
except:
scriptDialog.ShowMessageBox( "An error occurred when trying to import the Shotgun API.\n\nPlease ensure that the Shotgun API has been added to your Shotgun event folder.", "Error" )
return
#Get the shotgun plugin config values
sgConfig = ShotgunUtils.GetIniFileData( shotgunPath )
#advancedMode = Boolean.Parse( shotgunConfig.get( "EnableAdvancedWorkflow", "False" ) )
advancedMode = StringUtils.ParseBooleanWithDefault( sgConfig.get( "EnableAdvancedWorkflow", "False" ), False )
versionTemplates = sgConfig.get( "VersionNameTemplates", "" ).split( ';' )
sgURL = sgConfig.get( "ShotgunURL", "" )
#append a / if there isn't one
if not IsNoneOrWhiteSpace( sgURL ) and not sgURL.endswith( "/" ):
sgURL += "/"
shotgunAPIVersion = ShotgunUtils.GetShotgunAPIVersion()
#clean out empty templates
for template in versionTemplates:
if IsNoneOrWhiteSpace( template ):
versionTemplates.remove( template )
dialogWidth = 450
labelWidth = 90
dialogHeight = 382
padding = 6
shotgunPath = Path.Combine( GetRootDirectory(), "events/Shotgun" )
scriptDialog.SetSize( dialogWidth + 14, dialogHeight )
title = "Shotgun Settings"
if not IsNoneOrWhiteSpace( shotgunAPIVersion ):
title += " (API v%s)" % shotgunAPIVersion
scriptDialog.SetTitle( title )
scriptDialog.SetIcon( Path.Combine( shotgunPath, "Shotgun.ico" ) )
scriptDialog.AddControl( "Separator1", "SeparatorControl", "Shotgun Fields", dialogWidth, -1 )
scriptDialog.AddRow()
scriptDialog.AddControl( "LoginLabel", "LabelControl", "Login Name", labelWidth, -1)
########################################################################
loginBox = scriptDialog.AddControl( "LoginBox", "TextControl", "", 175, -1 )
scriptDialog.SetValue( "LoginBox", os.environ['USERNAME'] )
########################################################################
loginBox.ValueModified += LoginNameChanged
loginButton = scriptDialog.AddControl( "LoginButton", "ButtonControl", "Connect", labelWidth, -1 )
loginButton.ValueModified += LoginButtonPressed
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "UserLabel", "LabelControl", "Connected User", labelWidth, -1 )
scriptDialog.AddControl( "UserBox", "LabelControl", "", dialogWidth - (labelWidth + padding), -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "TaskLabel", "LabelControl", "Task", labelWidth, -1 )
taskBox = scriptDialog.AddComboControl( "TaskBox", "ComboControl", "", (), dialogWidth - (labelWidth + padding) - 25, -1 )
taskBox.ValueModified += ShotgunTaskChanged
newTaskButton = scriptDialog.AddControl( "NewTaskButton", "ButtonControl", "+", 20, -1 )
newTaskButton.ValueModified += NewEntityButton_Pressed
#context menu
menu = ContextMenu()
item = MenuItem( "New Task..." )
item.Click += NewTaskMenuItem_Click
menu.MenuItems.Add( item )
newTaskButton.ContextMenu = menu
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "ProjectLabel", "LabelControl", "Project", labelWidth, -1 )
#only use a drop down if we're using advanced workflow
if advancedMode:
projectBox = scriptDialog.AddComboControl( "ProjectBox", "ComboControl", "", (), dialogWidth - (labelWidth + padding), -1 )
projectBox.ValueModified += ShotgunProjectChanged
else:
scriptDialog.AddControl( "ProjectBox", "LabelControl", "", dialogWidth - (labelWidth + padding), -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "EntityLabel", "LabelControl", "Entity", labelWidth, -1 )
#only use a drop down if we're using advanced workflow
if advancedMode:
entityBox = scriptDialog.AddComboControl( "EntityBox", "ComboControl", "", (), dialogWidth - (labelWidth + padding) - 25, -1 )
entityBox.ValueModified += ShotgunEntityChanged
newEntityButton = scriptDialog.AddControl( "NewEntityButton", "ButtonControl", "+", 20, -1 )
newEntityButton.ValueModified += NewEntityButton_Pressed
#context menu
menu = ContextMenu()
item = MenuItem( "New Asset..." )
item.Click += NewAssetMenuItem_Click
menu.MenuItems.Add( item )
item = MenuItem( "New Element..." )
item.Click += NewElementMenuItem_Click
menu.MenuItems.Add( item )
item = MenuItem( "New Shot..." )
item.Click += NewShotMenuItem_Click
menu.MenuItems.Add( item )
newEntityButton.ContextMenu = menu
else:
scriptDialog.AddControl( "EntityBox", "LabelControl", "", dialogWidth - (labelWidth + padding), -1 )
scriptDialog.EndRow()
if versionSelectMode:
scriptDialog.AddRow()
scriptDialog.AddControl( "VersionLabel", "LabelControl", "Version", labelWidth, -1 )
versionControl = scriptDialog.AddComboControl( "VersionCombo", "ComboControl", "Version", (), dialogWidth - (labelWidth + padding), -1 )
versionControl.ValueModified += ShotgunVersionChanged
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "DescriptionLabel", "LabelControl", "Description", labelWidth, -1 )
scriptDialog.AddControl( "DescriptionBox", "ReadOnlyTextControl", "", dialogWidth - (labelWidth + padding), -1 )
scriptDialog.EndRow()
else:
scriptDialog.AddRow()
scriptDialog.AddControl( "TemplateLabel", "LabelControl", "Version Template", labelWidth, -1 )
#if we have templates, use an editable combo box
if len( versionTemplates ) > 0:
templateBox = scriptDialog.AddComboControl( "TemplateBox", "EditableComboControl", "", tuple( versionTemplates ), dialogWidth - (labelWidth + padding), -1 )
templateBox.ValueModified += VersionTemplateChanged
else:
templateBox = scriptDialog.AddControl( "TemplateBox", "TextControl", "", dialogWidth - (labelWidth + padding), -1 )
templateBox.ValueModified += VersionTemplateChanged
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "PreviewLabel", "LabelControl", "Version Preview", labelWidth, -1 )
scriptDialog.AddControl( "PreviewBox", "LabelControl", "", dialogWidth - (labelWidth + padding), -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "DescriptionLabel", "LabelControl", "Description", labelWidth, -1 )
scriptDialog.AddControl( "DescriptionBox", "TextControl", "", dialogWidth - (labelWidth + padding), -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "LogLabel", "LabelControl", "Shotgun Log:", labelWidth, -1 )
scriptDialog.EndRow()
scriptDialog.AddRow()
logBox = scriptDialog.AddComboControl( "LogBox", "ListControl", "", (""), dialogWidth, 70 )
logBox.ScrollBars = ScrollBars.Vertical;
logBox.SelectionMode = SelectionMode.MultiExtended
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddRangeControl( "ProgressBar", "ProgressBarControl", 0, 0, 100, 0, 0, labelWidth, 20)
scriptDialog.AddControl( "DummyLabel", "LabelControl", "", dialogWidth - labelWidth - 200 - (3 * padding), -1)
okButton = scriptDialog.AddControl( "OKButton", "ButtonControl", "OK", 100, -1 )
okButton.ValueModified += OKButtonPressed
cancelButton = scriptDialog.AddControl( "CancelButton", "ButtonControl", "Cancel", 100, -1 )
cancelButton.ValueModified += CancelButtonPressed
scriptDialog.EndRow()
scriptDialog.AddRow()
scriptDialog.AddControl( "StatusLabel", "LabelControl", "", dialogWidth, -1 )
scriptDialog.EndRow()
scriptDialog.Shown += DialogShown
settings = ()
if versionSelectMode:
settings = ("TaskBox", "ProjectBox", "EntityBox")
else:
settings = ("TaskBox", "ProjectBox", "EntityBox", "TemplateBox", "DescriptionBox")
GetStickySettings( GetSettingsFilename() ) #loads in sticky settings for later use (our combo box items are dynamic, those won't get loaded right away)
scriptDialog.LoadSettings( GetSettingsFilename(), settings ) #loads what sticky settings can be loaded right now
scriptDialog.ShowDialog( True )
#Dialog was shown, start up a background thread to connect to shotgun
def DialogShown(*args):
global scriptDialog
errors, warnings = CheckShotgunSetup()
if len( errors ) > 0:
message = "Deadline's Shotgun integration does not seem to be configured properly.\n\nThe following errors must be rectified in the Shotgun Event Plugin configuration before proceeding:\n"
for error in errors:
message += (" ERROR: %s\n" % error)
scriptDialog.ShowMessageBox( message, "Shotgun Configuration Error" )
CancelButtonPressed( None )
else:
if len( warnings ) > 0:
message = ""
for warning in warnings:
message += (warning + "\n\n")
scriptDialog.ShowMessageBox( message.rstrip( '\n' ), "Shotgun Configurations Warning" )
#this will bring the form to the foreground
scriptDialog.TopMost = True
scriptDialog.TopMost = False
stickyUser = scriptDialog.GetValue( "LoginBox" )
#if we loaded a sticky value for the user, try connecting right away
if not IsNoneOrWhiteSpace( stickyUser ):
WriteToLogBox( "Defaulting to user: '%s'" % stickyUser )
LoginButtonPressed( None )
else:
UpdateEnabledStatus()
#Makes the progress bar fill up at a steady rate, while displaying a status message
def LookBusy():
global scriptDialog
global statusMessage
global workerThread
global errorMessage
global logMessages
UpdateEnabledStatus()
progress = 0
#Keep looping until the worker thread finishes
while workerThread.IsAlive and IsNoneOrWhiteSpace( errorMessage ):
progress = ( progress + 2 ) % 101
scriptDialog.SetValue( "ProgressBar", progress )
dots = ""
for i in range( 0, progress / 25 ):
dots = dots + "."
scriptDialog.SetValue( "StatusLabel", "%s%s" % (statusMessage, dots) )
Thread.Sleep( 50 )
if IsNoneOrWhiteSpace( errorMessage ):
#no error, rejoice!
scriptDialog.SetValue( "ProgressBar", 100 )
scriptDialog.SetValue( "StatusLabel", "Shotgun request complete" )
else:
#there was an error, display it
scriptDialog.SetValue( "ProgressBar", 0 )
scriptDialog.SetValue( "StatusLabel", "error :(" )
scriptDialog.ShowMessageBox( errorMessage, "Error" )
errorMessage = ""
UpdateEnabledStatus()
#clear the status label after a few secs
Thread.Sleep( 2500 )
try:
scriptDialog.SetValue( "StatusLabel", "" )
except:
pass
#Updates the enabled status of all controls on the form to match their status (values)
def UpdateEnabledStatus():
global scriptDialog
global workerThread
global currentUser
global advancedMode
global versionSelectMode
#Check if we're working on something in the background thread
finishedWorking = (workerThread == None or not workerThread.IsAlive)
#Check which fields are empty -- this will help us determine which controls should be enabled
connected = (currentUser != None)
taskEmpty = IsNoneOrWhiteSpace( scriptDialog.GetValue( "TaskBox" ) )
projectEmpty = IsNoneOrWhiteSpace( scriptDialog.GetValue( "ProjectBox" ) )
entityEmpty = IsNoneOrWhiteSpace( scriptDialog.GetValue( "EntityBox" ) )
#login related stuff
loginName = scriptDialog.GetValue( "LoginBox" )
loginEmpty = IsNoneOrWhiteSpace( loginName )
loginChanged = False
if connected:
loginChanged = (currentUser['login'] != loginName)
scriptDialog.SetEnabled( "LoginLabel", finishedWorking )
scriptDialog.SetEnabled( "LoginBox", finishedWorking )
scriptDialog.SetEnabled( "LoginButton", finishedWorking and not loginEmpty )
if connected and not loginChanged:
scriptDialog.SetValue( "LoginButton", "Refresh" )
else:
scriptDialog.SetValue( "LoginButton", "Connect" )
scriptDialog.SetEnabled( "UserLabel", connected and finishedWorking )
scriptDialog.SetEnabled( "UserBox", connected and finishedWorking )
scriptDialog.SetEnabled( "TaskLabel", connected and finishedWorking and ( not advancedMode or (not taskEmpty or projectEmpty) ) )
scriptDialog.SetEnabled( "TaskBox", connected and finishedWorking and ( not advancedMode or (not taskEmpty or projectEmpty) ) )
scriptDialog.SetEnabled( "NewTaskButton", connected and finishedWorking and ( not advancedMode or (not taskEmpty or projectEmpty) ) )
scriptDialog.SetEnabled( "ProjectLabel", connected and finishedWorking and (not advancedMode or taskEmpty) )
scriptDialog.SetEnabled( "ProjectBox", connected and finishedWorking and (not advancedMode or taskEmpty) )
scriptDialog.SetEnabled( "EntityLabel", connected and finishedWorking and ( not advancedMode or (taskEmpty and not projectEmpty) ) )
scriptDialog.SetEnabled( "EntityBox", connected and finishedWorking and ( not advancedMode or (taskEmpty and not projectEmpty) ) )
if advancedMode:
scriptDialog.SetEnabled( "NewEntityButton", connected and finishedWorking and taskEmpty and not projectEmpty )
if versionSelectMode:
scriptDialog.SetEnabled( "VersionLabel", connected and finishedWorking and not entityEmpty )
scriptDialog.SetEnabled( "VersionCombo", connected and finishedWorking and not entityEmpty )
else:
scriptDialog.SetEnabled( "TemplateLabel", connected and finishedWorking )
scriptDialog.SetEnabled( "TemplateBox", connected and finishedWorking )
scriptDialog.SetEnabled( "PreviewLabel", connected and finishedWorking )
scriptDialog.SetEnabled( "PreviewBox", connected and finishedWorking )
scriptDialog.SetEnabled( "DescriptionLabel", connected and finishedWorking )
scriptDialog.SetEnabled( "DescriptionBox", connected and finishedWorking )
scriptDialog.SetEnabled( "OKButton", connected and finishedWorking )
scriptDialog.SetEnabled( "CancelButton", True )
#Adds a line to the log box (in a thread-safe manner)
def WriteToLogBox( strAppending, suppressNewLine=False ):
global scriptDialog
global logBox
global logMessages
global mainThread
try:
lines = strAppending.splitlines()
for line in lines:
logMessages[ len( logMessages ) - 1 ] += line
if not suppressNewLine or lines.index(line) < len(lines) - 1:
logMessages.append( "" )
scriptDialog.SetItems( "LogBox", tuple( logMessages ) )
logBox.EnsureVisible( len( logMessages ) - 1 )
#hack to get the scrolled log box to draw properly on OSX
scriptDialog.SetValue( "LogBox", line )
except:
#the log box might just not be initialized, just supress the exception
pass
#Checks the provided Shotgun settings dictionary (taken from Shotgun.dlinit) to make sure required values have been set up
def CheckShotgunSetup():
global sgConfig
global sgURL
warnings = []
errors = []
if IsNoneOrWhiteSpace( sgURL ):
errors.append( "The Shotgun URL has not been set." )
if sgConfig.get( "ShotgunScriptName", "" ).strip() == "":
errors.append( "The Shotgun Script Name has not been set." )
if sgConfig.get( "ShotgunScriptKey", "" ).strip() == "":
errors.append( "The Shotgun Script Key has not been set." )
sgEnabled = sgConfig.get( "Enabled", "" ).strip()
if sgEnabled[:4].upper() != "TRUE":
warnings.append( "The Shotgun Event Plugin is currently disabled.\n\nThe Shotgun Event Plugin must be enabled in order for Shotgun integration to work properly." )
return errors, warnings
#Ensures Shotgun stuff is in the PATH so we can import the Shotgun modules
def AddShotgunToPath():
global shotgunPath
global shotgunImported
if shotgunPath == None:
shotgunPath = Path.Combine( GetRootDirectory(), "events/Shotgun" )
if not shotgunImported:
sys.path.append( shotgunPath )
shotgunImported = True
return shotgunPath
#Does the initial connection to shotgun
def ConnectToShotgun( userName ):
global errorMessage
global scriptDialog
global shotgunPath
global statusMessage
global stickySettings
global updatingUI
global currentUser
global advancedMode
try:
statusMessage = "connecting"
newUser = None
try:
AddShotgunToPath()
import ShotgunUtils
WriteToLogBox( "Connecting to Shotgun as '%s'... " % userName, True )
newUser = ShotgunUtils.GetUser( userName, shotgunPath )
if newUser != None:
WriteToLogBox( "done!" )
else:
WriteToLogBox( "failed." )
except:
errorMessage = "An error occurred while attempting to connect to Shotgun; see log for details."
raise
if newUser != None:
#Initialize defaults to None
defaultTask = None
defaultProject = None
defaultEntity = None
defaultVersion = None
#Load sticky settings if it's our first time through
if currentUser == None:
defaultTask = stickySettings.get( "TaskBox", None )
if advancedMode:
defaultProject = stickySettings.get( "ProjectBox", None )
defaultEntity = stickySettings.get( "EntityBox", None )
defaultVersion = stickySettings.get( "VersionBox", None )
elif currentUser == newUser:
#refreshing, default to current settings
defaultTask = scriptDialog.GetValue( "TaskBox" )
defaultProject = scriptDialog.GetValue( "ProjectBox" )
defaultEntity = scriptDialog.GetValue( "EntityBox" )
if versionSelectMode:
defaultVersion = scriptDialog.GetValue( "VersionBox" )
#update current user
currentUser = newUser
scriptDialog.SetValue( "UserBox", currentUser['name'] )
defaultedTask = GetTasksForUser( currentUser['login'], defaultTask )
#don't need to fill these out if we're not doing advanced workflow, or if we already have a task (and therefore a project/entity)
if advancedMode:
defaultedProject = GetProjectsForUser( currentUser['login'], defaultProject )
#only need to load entities if no task was defaulted
if IsNoneOrWhiteSpace( defaultedTask ):
defaultedEntity = GetEntitiesForProject( defaultedProject, defaultEntity )
if versionSelectMode:
GetVersionsForEntity( defaultedEntity, defaultVersion )
elif versionSelectMode:
GetVersionsForTask( defaultedTask, defaultVersion )
#update the version template preview
VersionTemplateChanged( None )
else:
errorMessage = "Failed to connect to Shotgun with the given login name. Please double-check your login and try again."
except:
#Make sure we set this to notify other threads that this failed
if IsNoneOrWhiteSpace( errorMessage ):
errorMessage = "An error occurred while connecting to Shotgun; see log for details."
WriteToLogBox( "UNEXPECTED ERROR:" )
WriteToLogBox( str( sys.exc_info()[0] ) )
WriteToLogBox( str( sys.exc_info()[1] ) )
WriteToLogBox( "---END ERROR INFO---" )
#Get SG Tasks for a given user
#Returns the defaulted task, if applicable
def GetTasksForUser( user, defaultTask=None ):
global errorMessage
global shotgunPath
global updatingUI
global sgTaskDict
global scriptDialog
global statusMessage
try:
tasks = []
taskNames = []
sgTaskDict = {}
#fall back to current value as a default
if defaultTask == None:
defaultTask = scriptDialog.GetValue( "TaskBox" )
statusMessage = "fetching tasks"
if user != None:
AddShotgunToPath()
import ShotgunUtils
WriteToLogBox( "Getting Task list for user '%s'... " % user, True )
tasks = ShotgunUtils.GetTasks( user, None, shotgunPath )
WriteToLogBox( "done!" )
taskNames.append( " " )
for task in tasks:
if task['project'] != None and task['entity'] != None:
taskName = task['project']['name'] + " > " + task['entity']['name'] + " > " + task['content']
sgTaskDict[ taskName ] = task
taskNames.append( taskName )
taskNames.sort()
scriptDialog.SetItems( "TaskBox", tuple( taskNames ) )
#default to the given task, if available
if not IsNoneOrWhiteSpace( defaultTask ) and defaultTask in taskNames:
pass
elif len( taskNames ) > 0:
defaultTask = taskNames[0]
else:
defaultTask = None
#Set the default task
if defaultTask != None:
if defaultTask.strip() != "":
WriteToLogBox( "Defaulting to task: '%s'" % defaultTask )
updatingUI = True
scriptDialog.SetValue( "TaskBox", defaultTask )
updatingUI = False
return defaultTask
except:
#Make sure we set this to notify other threads that this failed
if IsNoneOrWhiteSpace( errorMessage ):
errorMessage = "An error occurred while attempting to collect Task Names from Shotgun; see log for details."
WriteToLogBox( "UNEXPECTED ERROR:" )
WriteToLogBox( str( sys.exc_info()[0] ) )
WriteToLogBox( str( sys.exc_info()[1] ) )
WriteToLogBox( "---END ERROR INFO---" )
return None
#Gets SG Projects for a given user
#Returns the defaulted project, if applicable
def GetProjectsForUser( user, defaultProject=None ):
global errorMessage
global shotgunPath
global sgProjectDict
global updatingUI
global scriptDialog
global statusMessage
global advancedMode
try:
projects = []
projectNames = []
sgProjectDict = {}
#fall back on currently selected value as default
if defaultProject == None:
defaultProject = scriptDialog.GetValue( "ProjectBox" )
statusMessage = "fetching projects"
if user != None:
AddShotgunToPath()
import ShotgunUtils
WriteToLogBox( "Getting Project list for user '%s'... " % user, True )
projects = ShotgunUtils.GetProjects( shotgunPath )
WriteToLogBox( "done!" )
projectNames.append( " " )
for project in projects:
projectName = project['name']
sgProjectDict[ projectName ] = project
projectNames.append( projectName )
projectNames.sort()
if advancedMode:
scriptDialog.SetItems( "ProjectBox", tuple( projectNames ) )
#default to the previously selected project, if available
if not IsNoneOrWhiteSpace( defaultProject ) and defaultProject in projectNames:
pass
elif len( projectNames ) > 0:
defaultProject = projectNames[0]
else:
defaultProject = None
#set the default project
if defaultProject != None:
updatingUI = True
if defaultProject.strip() != "":
WriteToLogBox( "Defaulting to project: '%s'" % defaultProject )
scriptDialog.SetValue( "ProjectBox", defaultProject )
updatingUI = False
return defaultProject
except:
#Make sure we set this to notify other threads that this failed
if IsNoneOrWhiteSpace( errorMessage ):
errorMessage = "An error occurred while attempting to collect Project Names from Shotgun; see log for details."
WriteToLogBox( "UNEXPECTED ERROR:" )
WriteToLogBox( str( sys.exc_info()[0] ) )
WriteToLogBox( str( sys.exc_info()[1] ) )
WriteToLogBox( "---END ERROR INFO---" )
return None
#Gets SG Entities (Shots/Assets) for a given project
#Returns the defaulted entity, if applicable
def GetEntitiesForProject( projectName, defaultEntity=None ):
global errorMessage
global shotgunPath
global scriptDialog
global updatingUI
global sgProjectDict
global sgEntityDict
global statusMessage
try:
shotgunEntityDict = {}
entityNames = [" "]
#fall back on currently selected value as a default
if defaultEntity == None:
defaultEntity = scriptDialog.GetValue( "EntityBox" )
statusMessage = "fetching entities"
if not IsNoneOrWhiteSpace( projectName ):
shots = []
assets = []
shotNames = []
assetNames = []
elementNames = []
project = sgProjectDict[ projectName ]
AddShotgunToPath()
import ShotgunUtils
WriteToLogBox( "Getting Entity lists for project '%s'... " % projectName, True )
shots, assets, elements = ShotgunUtils.GetShotsAssetsAndElements( project['id'], shotgunPath )
WriteToLogBox( "done!" )
for shot in shots:
shotName = shot['code']
if shot.get( "sg_sequence", None ):
shotName = "%s > %s" % (shot['sg_sequence']['name'], shotName)
sgEntityDict[ shotName ] = shot
shotNames.append( shotName )
for asset in assets:
assetName = asset['code']
sgEntityDict[ assetName ] = asset
assetNames.append( assetName )
for element in elements:
elementName = element['code']
sgEntityDict[ elementName ] = element
elementNames.append( elementName )
shotNames.sort()
entityNames.extend( shotNames )
assetNames.sort()
entityNames.extend( assetNames )
elementNames.sort()
entityNames.extend( elementNames )
if advancedMode:
scriptDialog.SetItems( "EntityBox", tuple( entityNames ) )
#default to given entity if available
if not IsNoneOrWhiteSpace( defaultEntity ) and defaultEntity in entityNames:
pass
elif len( entityNames ) > 0:
defaultEntity = entityNames[0]
else:
defaultEntity = None
#set the default entity
if defaultEntity != None:
updatingUI = True
if defaultEntity.strip() != "":
WriteToLogBox( "Defaulting to entity: '%s'" % defaultEntity )
scriptDialog.SetValue( "EntityBox", defaultEntity )
updatingUI = False
return defaultEntity
except:
#Make sure we set this to notify other threads that this failed
if IsNoneOrWhiteSpace( errorMessage ):
errorMessage = "An error occurred while attempting to collect Entity Names from Shotgun; see log for details."
WriteToLogBox( "UNEXPECTED ERROR:" )
WriteToLogBox( str( sys.exc_info()[0] ) )
WriteToLogBox( str( sys.exc_info()[1] ) )
WriteToLogBox( "---END ERROR INFO---" )
return None
def GetVersionsForEntity( entityName, defaultVersion=None ):
global errorMessage
global shotgunPath
global scriptDialog
global updatingUI
global sgProjectDict
global sgEntityDict
global statusMessage
global sgVersionDict
try:
statusMessage = "fetching versions"
versionCodes = []
sgVersionDict = dict()
#fall back on currently selected value as a default
if defaultVersion == None:
defaultVersion = scriptDialog.GetValue( "VersionCombo" )
if not IsNoneOrWhiteSpace( entityName ):
AddShotgunToPath()
import ShotgunUtils
WriteToLogBox( "Getting Version list for entity '%s'... " % entityName, True )
versions = ShotgunUtils.GetVersions( sgEntityDict[entityName]['type'], sgEntityDict[entityName]['id'], shotgunPath )
WriteToLogBox( "done!" )
for version in versions :
versionCode = version['code']
#need a unique display name for each version, so append [#] if it's already in the dict
versionDisplayName = versionCode
index = 0
while versionDisplayName in sgVersionDict:
index += 1
versionDisplayName = "%s [%d]" % (versionCode, index)
versionCodes.append( versionDisplayName )
sgVersionDict[versionDisplayName] = version
if len(versionCodes) > 0 :
scriptDialog.SetItems( 'VersionCombo', Array[str](versionCodes) )
scriptDialog.SetValue( 'VersionCombo', versionCodes[0])
else :
scriptDialog.SetItems( 'VersionCombo', Array[str](['']) )
scriptDialog.SetValue( 'VersionCombo', '')
scriptDialog.SetItems( 'VersionCombo', Array[str]([]) )
#default to given version if available
if not IsNoneOrWhiteSpace( defaultVersion ) and defaultVersion in versionCodes:
pass
elif len( versionCodes ) > 0:
defaultVersion = versionCodes[0]
else:
defaultVersion = None
#set the default version
if defaultVersion != None:
updatingUI = True
if defaultVersion.strip() != "":
WriteToLogBox( "Defaulting to version: '%s'" % defaultVersion )
scriptDialog.SetValue( "VersionCombo", defaultVersion )
updatingUI = False
return defaultVersion
except:
#Make sure we set this to notify other threads that this failed
if IsNoneOrWhiteSpace( errorMessage ):
errorMessage = "An error occurred while attempting to collect Version Names from Shotgun; see log for details."
WriteToLogBox( "UNEXPECTED ERROR:" )
WriteToLogBox( str( sys.exc_info()[0] ) )
WriteToLogBox( str( sys.exc_info()[1] ) )
WriteToLogBox( "---END ERROR INFO---" )
return None
def GetVersionsForTask( taskName, defaultVersion=None ):
global errorMessage
global shotgunPath
global scriptDialog
global updatingUI
global sgTaskDict
global statusMessage
global sgVersionDict
try:
statusMessage = "fetching versions"
versionCodes = []
sgVersionDict = dict()
#fall back on currently selected value as a default
if defaultVersion == None:
defaultVersion = scriptDialog.GetValue( "VersionCombo" )
if not IsNoneOrWhiteSpace( taskName ):
AddShotgunToPath()
import ShotgunUtils
WriteToLogBox( "Getting Version list for task '%s'... " % taskName, True )
versions = ShotgunUtils.GetVersions( sgTaskDict[taskName]['entity']['type'], sgTaskDict[taskName]['entity']['id'], shotgunPath )
WriteToLogBox( "done!" )
for version in versions :
versionCode = version['code']
#need a unique display name for each version, so append [#] if it's already in the dict
versionDisplayName = versionCode
index = 0
while versionDisplayName in sgVersionDict:
index += 1
versionDisplayName = "%s [%d]" % (versionCode, index)
versionCodes.append( versionDisplayName )
sgVersionDict[versionDisplayName] = version
versionCodes.sort()
if len(versionCodes) > 0 :
scriptDialog.SetItems( 'VersionCombo', Array[str](versionCodes) )
scriptDialog.SetValue( 'VersionCombo', versionCodes[0])
else :
scriptDialog.SetItems( 'VersionCombo', Array[str](['']) )
scriptDialog.SetValue( 'VersionCombo', '')
scriptDialog.SetItems( 'VersionCombo', Array[str]([]) )
#default to given version if available
if not IsNoneOrWhiteSpace( defaultVersion ) and defaultVersion in versionCodes:
pass
elif len( versionCodes ) > 0:
defaultVersion = versionCodes[0]
else:
defaultVersion = None
#set the default version
if defaultVersion != None:
updatingUI = True
if defaultVersion.strip() != "":
WriteToLogBox( "Defaulting to version: '%s'" % defaultVersion )
scriptDialog.SetValue( "VersionCombo", defaultVersion )
updatingUI = False
return defaultVersion
except:
#Make sure we set this to notify other threads that this failed
if IsNoneOrWhiteSpace( errorMessage ):
errorMessage = "An error occurred while attempting to collect Version Names from Shotgun; see log for details."
WriteToLogBox( "UNEXPECTED ERROR:" )
WriteToLogBox( str( sys.exc_info()[0] ) )
WriteToLogBox( str( sys.exc_info()[1] ) )
WriteToLogBox( "---END ERROR INFO---" )
return None
def ShotgunTaskChanged( *args ):
global scriptDialog
global sgTaskDict
global sgProjectDict
global updatingUI
global advancedMode
global versionSelectMode
global workerThread
global progressThread
taskName = ""
if len( scriptDialog.GetItems( "TaskBox" ) ) > 0:
taskName = scriptDialog.GetValue( "TaskBox" )
#If we're updating in code, DON'T start new threads
if versionSelectMode and not updatingUI:
#Start up the worker & progress threads
statusMessage = "connecting" # initial status message for progress bar
workerThread = Thread( ParameterizedThreadStart( GetVersionsForTask ) )
workerThread.IsBackground = True
progressThread = Thread( ThreadStart( LookBusy ) )
progressThread.IsBackground = True
workerThread.Start( taskName )
progressThread.Start()
projectNames = [ ]
entityNames = [ ]
if not IsNoneOrWhiteSpace( taskName ):
task = sgTaskDict[ taskName ]
if task['project'] != None and task['entity'] != None:
projectNames.append( task['project']['name'] )
entityNames.append( task['entity']['name'] )
else:
projectNames.append( " " )
entityNames.append( " " )
if advancedMode:
for key in sgProjectDict.keys():
projectNames.append( key )
projectNames.sort()
updatingUI = True
if advancedMode:
scriptDialog.SetItems( "ProjectBox", tuple( projectNames ) )
if len( projectNames ) > 0:
scriptDialog.SetValue( "ProjectBox", projectNames[ 0 ] )
if advancedMode:
scriptDialog.SetItems( "EntityBox", tuple( entityNames ) )
if len( entityNames ) > 0:
scriptDialog.SetValue( "EntityBox", entityNames[ 0 ] )
updatingUI = False
VersionTemplateChanged( None )
UpdateEnabledStatus()
def ShotgunProjectChanged( *args ):
global scriptDialog
global updatingUI
global workerThread
global progressThread
#Make sure we don't chain events when we're updating values in code
if updatingUI:
return
if len( scriptDialog.GetItems( "ProjectBox" ) ) > 0:
projectName = scriptDialog.GetValue( "ProjectBox" )
#Start up the worker & progress threads
statusMessage = "connecting" # initial status message for progress bar
workerThread = Thread( ParameterizedThreadStart( GetEntitiesForProject ) )
workerThread.IsBackground = True
progressThread = Thread( ThreadStart( LookBusy ) )
progressThread.IsBackground = True
workerThread.Start( projectName )
progressThread.Start()
VersionTemplateChanged( None )
def ShotgunVersionChanged( *args ):
global scriptDialog
global updatingUI
global sgVersionDict
if updatingUI:
return
description = ""
if len( scriptDialog.GetItems( "VersionCombo" ) ) > 0:
versionName = scriptDialog.GetValue( "VersionCombo" )
if not IsNoneOrWhiteSpace( versionName ) and sgVersionDict.has_key( versionName ):
version = sgVersionDict[versionName]
if version.has_key( 'description' ):
description = version['description']
scriptDialog.SetValue( "DescriptionBox", description )
UpdateEnabledStatus()
#Returns the template string with placeholder values swapped out (optionally replacing {jobid} with a dummy value)
def ApplyTemplate( templateString, dummyJobID=True ):
global sgTaskDict
global sgEntityDict
global currentUser
global advancedMode
#might not be logged in for whatever reason
if currentUser != None:
#could change this to actual user name if users prefer (instead of login)
userName = currentUser['login']
templateString = re.sub( '(?i)\$\{user\}', userName, templateString )
#might not be a task selected
displayTaskName = scriptDialog.GetValue("TaskBox")
if not IsNoneOrWhiteSpace( displayTaskName ):
task = sgTaskDict[ displayTaskName ]
templateString = re.sub( '(?i)\$\{task\}', task['content'], templateString )
templateString = re.sub( '(?i)\$\{project\}', task['project']['name'], templateString )
templateString = re.sub( '(?i)\$\{shot\}', task['entity']['name'], templateString )
elif advancedMode:
#no task selected, check project/entity
projectName = scriptDialog.GetValue("ProjectBox")
if not IsNoneOrWhiteSpace( projectName ):
templateString = re.sub( '(?i)\$\{project\}', projectName, templateString )
#might have to change this later if we want {shot} to ONLY pull shots (not whatever's selected in the entity box)
displayEntityName = scriptDialog.GetValue("EntityBox")
if not IsNoneOrWhiteSpace( displayEntityName ):
entityName = sgEntityDict[ displayEntityName ]['code']
templateString = re.sub( '(?i)\$\{shot\}', entityName, templateString )
if dummyJobID:
templateString = re.sub( '(?i)\$\{jobid\}', '999_000_999_099a9aa9', templateString )
return templateString
def ShotgunEntityChanged( *args ):
global scriptDialog
global versionSelectMode
global workerThread
global progressThread
global updatingUI
#Make sure we don't chain events when we're updating values in code
if updatingUI:
return
taskName = scriptDialog.GetValue( "TaskBox" )
if versionSelectMode and IsNoneOrWhiteSpace( taskName ):
entityName = ""
if len( scriptDialog.GetItems( "EntityBox" ) ) > 0:
entityName = scriptDialog.GetValue( "EntityBox" )
#Start up the worker & progress threads
statusMessage = "connecting" # initial status message for progress bar
workerThread = Thread( ParameterizedThreadStart( GetVersionsForEntity ) )
workerThread.IsBackground = True
progressThread = Thread( ThreadStart( LookBusy ) )
progressThread.IsBackground = True
workerThread.Start( entityName )
progressThread.Start()
VersionTemplateChanged( None )
def VersionTemplateChanged( *args ):
global scriptDialog
global versionSelectMode
if not versionSelectMode:
templateString = scriptDialog.GetValue( "TemplateBox" )
scriptDialog.SetValue( "PreviewBox", ApplyTemplate( templateString ) )
def LoginNameChanged( *args ):
UpdateEnabledStatus()
return
def LoginButtonPressed( *args ):
global statusMessage
global workerThread
global progressThread
global scriptDialog
statusMessage = "connecting" # initial status message for progress bar
#ConnectToShotgun( scriptDialog.GetValue( "LoginBox" ) )
workerThread = Thread( ParameterizedThreadStart( ConnectToShotgun ) )
workerThread.IsBackground = True
progressThread = Thread( ThreadStart( LookBusy ) )
progressThread.IsBackground = True
workerThread.Start( scriptDialog.GetValue( "LoginBox" ) )
progressThread.Start()
return
def NewEntityButton_Pressed(*args):
if len( args ) > 0:
button = args[0]
button.ContextMenu.Show( button, Point(0, 0) )
def NewSGItemBrowser( itemName, additionalArgs="" ):
global sgURL
global scriptDialog
if not IsNoneOrWhiteSpace( sgURL ):
newEntityURL = sgURL + "new/" + itemName + "?show_nav=no"
projectName = scriptDialog.GetValue( "ProjectBox" )
if not IsNoneOrWhiteSpace( projectName ):
newEntityURL += '&project=' + projectName
newEntityURL += additionalArgs
webbrowser.open( newEntityURL, 1, True )
def NewAssetMenuItem_Click(*args):
NewSGItemBrowser( “Asset” )
def NewElementMenuItem_Click(*args):
NewSGItemBrowser( “Element” )
def NewShotMenuItem_Click(*args):
NewSGItemBrowser( “Shot” )
def NewTaskMenuItem_Click(*args):
#TODO: Figure out how to default users. Probably using the default={…} argument (not like project)…
#if currentUser != None:
# NewSGItemBrowser( “Task”, “&task_assignees=” + currentUser.get( “name”, “” ) )
#else:
NewSGItemBrowser( “Task” )
#reads in the sticky settings file and stores the key-value pairs in a dictionary
def GetStickySettings( fileName ):
global stickySettings
try:
WriteToLogBox( "Retrieving sticky settings... ", True )
settingsFile = open( fileName, "r" )
settingLines = settingsFile.readlines()
stickySettings = {}
for currLine in settingLines:
line = currLine.replace( "\n", "" ).replace( "\r", "" )
equalIndex = line.find( "=" )
if equalIndex >= 0:
if equalIndex < (len(line) - 1):
stickySettings[line[:equalIndex]] = line[equalIndex+1:]
else:
stickySettings[line[:equalIndex]] = ""
WriteToLogBox( "done!" )
except:
WriteToLogBox( "No sticky settings found." )
try:
#Always try to close the file
settingsFile.close()
except:
pass
def GetSettingsFilename():
global appName
return Path.Combine( GetDeadlineSettingsPath(), appName + "ShotgunSettings.ini" )
def CancelButtonPressed( *args ):
global scriptDialog
global workerThread
global progressThread
#Make sure to abort non-ui threads first, so that they don't keep running
if workerThread != None and workerThread.IsAlive:
workerThread.Abort()
if progressThread != None and progressThread.IsAlive:
progressThread.Abort()
scriptDialog.CloseDialog()
def IsNoneOrWhiteSpace( someString ):
return someString == None or someString.strip() == “”
def OKButtonPressed( *args ):
global scriptDialog
global settings
global sgConfig
global sgEntityDict
global sgProjectDict
global sgTaskDict
global sgVersionDict
global advancedMode
global versionSelectMode
global currentUser
try:
WriteToLogBox( "Validating selected values..." )
userName = ""
taskName = scriptDialog.GetValue( "TaskBox" )
projectName = scriptDialog.GetValue( "ProjectBox" )
entityName = scriptDialog.GetValue( "EntityBox" )
description = ApplyTemplate( scriptDialog.GetValue( "DescriptionBox" ), False )
if versionSelectMode:
versionName = scriptDialog.GetValue( "VersionCombo" )
else:
versionName = ApplyTemplate( scriptDialog.GetValue( "TemplateBox" ), False )
draftTemplate = ""
if not IsNoneOrWhiteSpace( taskName ):
draftTemplate = sgTaskDict[taskName].get( "draftTemplate", "" )
validationPassed = True
#Need to have a valid user logged in
if currentUser == None:
WriteToLogBox( "Validation failed on User Name" )
validationPassed = False
else:
userName = currentUser.get( "login", None )
if IsNoneOrWhiteSpace( userName ):
WriteToLogBox( "Validation failed on User Name" )
validationPassed = False
#Need to either have Project & Entity, or Task specified
if IsNoneOrWhiteSpace( taskName ) and (IsNoneOrWhiteSpace( projectName ) or IsNoneOrWhiteSpace( entityName ) or not advancedMode):
WriteToLogBox( "Validation failed on Task or Project/Entity" )
validationPassed = False
#Need to specify a version name
if IsNoneOrWhiteSpace( versionName ):
WriteToLogBox( "Validation failed on Version Name" )
validationPassed = False
if not validationPassed:
validationMessage = ""
if versionSelectMode:
validationMessage = "You must complete this form in order to select an existing Shotgun Version for this job.\n\nPlease fill in any missing info before proceeding."
else:
validationMessage = "You must complete this form in order to create a new Shotgun Version for this job.\n\nPlease fill in any missing info before proceeding."
scriptDialog.ShowMessageBox( validationMessage, "Shotgun Form Incomplete" )
return
WriteToLogBox( "Validating passed!" )
WriteToLogBox( "Building output... ", True )
ClientUtils.LogText( "VersionName=%s" % versionName )
ClientUtils.LogText( "Description=%s" % description )
ClientUtils.LogText( "UserName=%s" % userName )
if not IsNoneOrWhiteSpace( taskName ):
task = sgTaskDict[ taskName ]
ClientUtils.LogText( "TaskName=%s" % task['content'] )
ClientUtils.LogText( "ProjectName=%s" % task['project']['name'] )
ClientUtils.LogText( "EntityName=%s" % task['entity']['name'] )
ClientUtils.LogText( "TaskId=%s" % task['id'] )
ClientUtils.LogText( "ProjectId=%s" % task['project']['id'] )
ClientUtils.LogText( "EntityId=%s" % task['entity']['id'] )
ClientUtils.LogText( "EntityType=%s" % task['entity']['type'] )
else:
project = sgProjectDict[ projectName ]
entity = sgEntityDict[ entityName ]
ClientUtils.LogText( "TaskName=None" )
ClientUtils.LogText( "ProjectName=%s" % projectName )
ClientUtils.LogText( "EntityName=%s" % entityName )
ClientUtils.LogText( "TaskId=-1" )
ClientUtils.LogText( "ProjectId=%s" % project['id'] )
ClientUtils.LogText( "EntityId=%s" % entity['id'] )
ClientUtils.LogText( "EntityType=%s" % entity['type'] )
if versionSelectMode:
version = sgVersionDict[ versionName ]
pathField = sgConfig.get('VersionEntityPathToFramesField', "")
firstFrameField = sgConfig.get('VersionEntityFirstFrameField', "")
lastFrameField = sgConfig.get('VersionEntityLastFrameField', "")
ClientUtils.LogText( "VersionId=%s" % version['id'] )
ClientUtils.LogText( "PathToFrames=%s" % version.get(pathField, "") )
ClientUtils.LogText( "FirstFrame=%s" % version.get(firstFrameField, "") )
ClientUtils.LogText( "LastFrame=%s" % version.get(lastFrameField, "") )
ClientUtils.LogText( "DraftTemplate=%s" % draftTemplate )
WriteToLogBox( "done!" )
#switch the contents of the login box to be the current user, so it gets stickied properly
scriptDialog.SetValue( "LoginBox", currentUser['login'] )
WriteToLogBox( "Saving sticky settings... ", True )
scriptDialog.SaveSettings( GetSettingsFilename(), settings )
WriteToLogBox( "done!" )
scriptDialog.CloseDialog()
except:
#if an error occurred while trying to close the dialog, this UI stuff might fail -- don't want it to generate another exception
try:
WriteToLogBox( "UNEXPECTED ERROR:" )
WriteToLogBox( str( sys.exc_info()[0] ) )
WriteToLogBox( str( sys.exc_info()[1] ) )
WriteToLogBox( "---END ERROR INFO---" )
scriptDialog.ShowMessageBox( "An error occurred while preparing output; see log for details.", "ERROR!" )
except:
pass
[/code]