Source code for nuke_internal.callbacks

# callbacks.py
#
# Callbacks from Nuke to user-defined Python.
# Nuke actually calls "nuke.onCreate()" but users will normally use
# the default versions of these functions and use "nuke.addOnCreate()"
# to add to the list of callbacks that the default calls.

import types
import nuke_internal as nuke


def _addCallback(_dict, call, args, kwargs, nodeClass, node=None):
  if not callable(call):
    raise ValueError("call must be a callable")
  if type(args) != tuple:
    args = (args,)
  if type(kwargs) != dict:
    raise ValueError("kwargs must be a dictionary")
  if nodeClass in _dict:
    list = _dict[nodeClass]
    # make it appear only once in list
    try:
      list.remove((call,args,kwargs,node))
    except:
      pass
    list.append((call,args,kwargs,node))
  else:
    _dict[nodeClass] = [(call,args,kwargs,node)]

def _removeCallback(_dict, call, args, kwargs, nodeClass, node=None):
  if type(args) != tuple:
    args = (args,)
  if nodeClass in _dict:
    list = _dict[nodeClass]
    try:
      list.remove((call,args,kwargs,node))
    except:
      pass

def _doCallbacks(_dict, node=None):
  list = _dict.get(nuke.thisClass())
  node = nuke.thisNode()
  if list:
    for f in list:
      if f[3] == None or f[3] is node:
        f[0](*f[1],**f[2])
  list = _dict.get('*')
  if list:
    for f in list:
      if f[3] == None or f[3] is node:
        f[0](*f[1],**f[2])

onUserCreates={}
[docs]def addOnUserCreate(call, args=(), kwargs={}, nodeClass='*'): """Add code to execute when user creates a node""" _addCallback(onUserCreates, call, args, kwargs, nodeClass)
[docs]def removeOnUserCreate(call, args=(), kwargs={}, nodeClass='*'): """Remove a previously-added callback with the same arguments.""" _removeCallback(onUserCreates, call, args, kwargs, nodeClass)
[docs]def onUserCreate(): _doCallbacks(onUserCreates) if not len(onUserCreates): nuke.tcl("OnCreate")
onCreates={}
[docs]def addOnCreate(call, args=(), kwargs={}, nodeClass='*'): """Add code to execute when a node is created or undeleted""" _addCallback(onCreates, call, args, kwargs, nodeClass)
[docs]def removeOnCreate(call, args=(), kwargs={}, nodeClass='*'): """Remove a previously-added callback with the same arguments.""" _removeCallback(onCreates, call, args, kwargs, nodeClass)
[docs]def onCreate(): _doCallbacks(onCreates)
onScriptLoads={}
[docs]def addOnScriptLoad(call, args=(), kwargs={}, nodeClass='Root'): """Add code to execute when a script is loaded""" _addCallback(onScriptLoads, call, args, kwargs, nodeClass)
[docs]def removeOnScriptLoad(call, args=(), kwargs={}, nodeClass='Root'): """Remove a previously-added callback with the same arguments.""" _removeCallback(onScriptLoads, call, args, kwargs, nodeClass)
[docs]def onScriptLoad(): _doCallbacks(onScriptLoads)
onScriptSaves={}
[docs]def addOnScriptSave(call, args=(), kwargs={}, nodeClass='Root'): """Add code to execute before a script is saved""" _addCallback(onScriptSaves, call, args, kwargs, nodeClass)
[docs]def removeOnScriptSave(call, args=(), kwargs={}, nodeClass='Root'): """Remove a previously-added callback with the same arguments.""" _removeCallback(onScriptSaves, call, args, kwargs, nodeClass)
[docs]def onScriptSave(): _doCallbacks(onScriptSaves)
onScriptCloses={}
[docs]def addOnScriptClose(call, args=(), kwargs={}, nodeClass='Root'): """Add code to execute before a script is closed""" _addCallback(onScriptCloses, call, args, kwargs, nodeClass)
[docs]def removeOnScriptClose(call, args=(), kwargs={}, nodeClass='Root'): """Remove a previously-added callback with the same arguments.""" _removeCallback(onScriptCloses, call, args, kwargs, nodeClass)
[docs]def onScriptClose(): _doCallbacks(onScriptCloses)
onDestroys={}
[docs]def addOnDestroy(call, args=(), kwargs={}, nodeClass='*'): """Add code to execute when a node is destroyed""" _addCallback(onDestroys, call, args, kwargs, nodeClass)
[docs]def removeOnDestroy(call, args=(), kwargs={}, nodeClass='*'): """Remove a previously-added callback with the same arguments.""" _removeCallback(onDestroys, call, args, kwargs, nodeClass)
[docs]def onDestroy(): _doCallbacks(onDestroys)
knobChangeds={}
[docs]def addKnobChanged(call, args=(), kwargs={}, nodeClass='*', node=None): """Add code to execute when the user changes a knob The knob is availble in nuke.thisKnob() and the node in nuke.thisNode(). This is also called with dummy knobs when the control panel is opened or when the inputs to the node changes. The purpose is to update other knobs in the control panel. Use addUpdateUI() for changes that should happen even when the panel is closed.""" _addCallback(knobChangeds, call, args, kwargs, nodeClass, node)
[docs]def removeKnobChanged(call, args=(), kwargs={}, nodeClass='*', node=None): """Remove a previously-added callback with the same arguments.""" _removeCallback(knobChangeds, call, args, kwargs, nodeClass, node)
[docs]def knobChanged(): _doCallbacks(knobChangeds)
updateUIs={}
[docs]def addUpdateUI(call, args=(), kwargs={}, nodeClass='*'): """Add code to execute on every node when things change. This is done during idle, you cannot rely on it being done before it starts updating the viewer""" _addCallback(updateUIs, call, args, kwargs, nodeClass)
[docs]def removeUpdateUI(call, args=(), kwargs={}, nodeClass='*'): """Remove a previously-added callback with the same arguments.""" _removeCallback(updateUIs, call, args, kwargs, nodeClass)
[docs]def updateUI(): _doCallbacks(updateUIs)
# autolabel is somewhat different due to it returning a string autolabels={}
[docs]def addAutolabel(call, args=(), kwargs={}, nodeClass='*'): """Add code to execute on every node to produce the text to draw on it in the DAG. Any value other than None is converted to a string and used as the text. None indicates that previously-added functions should be tried""" _addCallback(autolabels, call, args, kwargs, nodeClass)
[docs]def removeAutolabel(call, args=(), kwargs={}, nodeClass='*'): """Remove a previously-added callback with the same arguments.""" _removeCallback(autolabels, call, args, kwargs, nodeClass)
[docs]def autolabel(): list = autolabels.get(nuke.thisClass()) if list: for f in list[::-1]: s = f[0](*f[1],**f[2]) if s != None: return s list = autolabels.get('*') if list: for f in list[::-1]: s = f[0](*f[1],**f[2]) if s != None: return s
# Normal rendering callbacks beforeRenders={}
[docs]def addBeforeRender(call, args=(), kwargs={}, nodeClass='Write'): """Add code to execute before starting any renders""" _addCallback(beforeRenders, call, args, kwargs, nodeClass)
[docs]def removeBeforeRender(call, args=(), kwargs={}, nodeClass='Write'): """Remove a previously-added callback with the same arguments.""" _removeCallback(beforeRenders, call, args, kwargs, nodeClass)
[docs]def beforeRender(): _doCallbacks(beforeRenders)
beforeFrameRenders={}
[docs]def addBeforeFrameRender(call, args=(), kwargs={}, nodeClass='Write'): """Add code to execute before each frame of a render""" _addCallback(beforeFrameRenders, call, args, kwargs, nodeClass)
[docs]def removeBeforeFrameRender(call, args=(), kwargs={}, nodeClass='Write'): """Remove a previously-added callback with the same arguments.""" _removeCallback(beforeFrameRenders, call, args, kwargs, nodeClass)
[docs]def beforeFrameRender(): _doCallbacks(beforeFrameRenders)
afterFrameRenders={}
[docs]def addAfterFrameRender(call, args=(), kwargs={}, nodeClass='Write'): """Add code to execute after each frame of a render""" _addCallback(afterFrameRenders, call, args, kwargs, nodeClass)
[docs]def removeAfterFrameRender(call, args=(), kwargs={}, nodeClass='Write'): """Remove a previously-added callback with the same arguments.""" _removeCallback(afterFrameRenders, call, args, kwargs, nodeClass)
[docs]def afterFrameRender(): _doCallbacks(afterFrameRenders)
afterRenders={}
[docs]def addAfterRender(call, args=(), kwargs={}, nodeClass='Write'): """Add code to execute after any renders""" _addCallback(afterRenders, call, args, kwargs, nodeClass)
[docs]def removeAfterRender(call, args=(), kwargs={}, nodeClass='Write'): """Remove a previously-added callback with the same arguments.""" _removeCallback(afterRenders, call, args, kwargs, nodeClass)
[docs]def afterRender(): _doCallbacks(afterRenders)
renderProgresses={}
[docs]def addRenderProgress(call, args=(), kwargs={}, nodeClass='Write'): """Add code to execute when the progress bar updates during any renders""" _addCallback(renderProgresses, call, args, kwargs, nodeClass)
[docs]def removeRenderProgress(call, args=(), kwargs={}, nodeClass='Write'): """Remove a previously-added callback with the same arguments.""" _removeCallback(renderProgresses, call, args, kwargs, nodeClass)
[docs]def renderProgress(): _doCallbacks(renderProgresses)
# Callbacks for internal use only _beforeRecordings={}
[docs]def addBeforeRecording(call, args=(), kwargs={}, nodeClass='Viewer'): """Add code to execute before viewer recording""" _addCallback(_beforeRecordings, call, args, kwargs, nodeClass)
[docs]def removeBeforeRecording(call, args=(), kwargs={}, nodeClass='Viewer'): """Remove a previously-added callback with the same arguments.""" _removeCallback(_beforeRecordings, call, args, kwargs, nodeClass)
[docs]def beforeRecording(): _doCallbacks(_beforeRecordings)
_afterRecordings={}
[docs]def addAfterRecording(call, args=(), kwargs={}, nodeClass='Viewer'): """Add code to execute after viewer recording""" _addCallback(_afterRecordings, call, args, kwargs, nodeClass)
[docs]def removeAfterRecording(call, args=(), kwargs={}, nodeClass='Viewer'): """Remove a previously-added callback with the same arguments.""" _removeCallback(_afterRecordings, call, args, kwargs, nodeClass)
[docs]def afterRecording(): _doCallbacks(_afterRecordings)
_beforeReplays={}
[docs]def addBeforeReplay(call, args=(), kwargs={}, nodeClass='Viewer'): """Add code to execute before viewer replay""" _addCallback(_beforeReplays, call, args, kwargs, nodeClass)
[docs]def removeBeforeReplay(call, args=(), kwargs={}, nodeClass='Viewer'): """Remove a previously-added callback with the same arguments.""" _removeCallback(_beforeReplays, call, args, kwargs, nodeClass)
[docs]def beforeReplay(): _doCallbacks(_beforeReplays)
_afterReplays={}
[docs]def addAfterReplay(call, args=(), kwargs={}, nodeClass='Viewer'): """Add code to execute after viewer replay""" _addCallback(_afterReplays, call, args, kwargs, nodeClass)
[docs]def removeAfterReplay(call, args=(), kwargs={}, nodeClass='Viewer'): """Remove a previously-added callback with the same arguments.""" _removeCallback(_afterReplays, call, args, kwargs, nodeClass)
[docs]def afterReplay(): _doCallbacks(_afterReplays)
# Special functions to perform background callbacks as these have no node as # context. def _addBackgroundCallback(list, call, args, kwargs): if not callable(call): raise ValueError("call must be a callable") if type(args) != tuple: args = (args,) if type(kwargs) != dict: raise ValueError("kwargs must be a dictionary") # make it appear only once in list try: list.remove((call,args,kwargs)) except: pass list.append((call,args,kwargs)) def _removeBackgroundCallback(list, call, args, kwargs): if type(args) != tuple: args = (args,) try: list.remove((call,args,kwargs)) except: pass def _doBackgroundCallbacks(list, context): for f in list: f[0](context, *f[1],**f[2]) # Background rendering callbacks beforeBackgroundRenders=[]
[docs]def addBeforeBackgroundRender(call, args=(), kwargs={}): """Add code to execute before starting any background renders. The call must be in the form of: def foo(context): pass The context object that will be passed in is a dictionary containing the following elements: id => The identifier for the task that's about to begin Please be aware that the current Nuke context will not make sense in the callback (e.g. nuke.thisNode will return a random node). """ _addBackgroundCallback(beforeBackgroundRenders, call, args, kwargs)
[docs]def removeBeforeBackgroundRender(call, args=(), kwargs={}): """Remove a previously-added callback with the same arguments.""" _removeBackgroundCallback(beforeBackgroundRenders, call, args, kwargs)
[docs]def beforeBackgroundRender(context): _doBackgroundCallbacks(beforeBackgroundRenders, context)
# There is no logical place for this to be called at the moment, so don't expose it. #def addBeforeBackgroundFrameRender(call, args=(), kwargs={}): # """Add code to execute before each frame of a background render""" # _addBackgroundCallback(beforeBackgroundFrameRenders, call, args, kwargs) #def removeBeforeBackgroundFrameRender(call, args=(), kwargs={}): # """Remove a previously-added callback with the same arguments.""" # _removeBackgroundCallback(beforeBackgroundFrameRenders, call, args, kwargs) #def beforeBackgroundFrameRender(): # _doBackgroundCallbacks(beforeBackgroundFrameRenders) afterBackgroundFrameRenders=[]
[docs]def addAfterBackgroundFrameRender(call, args=(), kwargs={}): """Add code to execute after each frame of a background render. The call must be in the form of: def foo(context): pass The context object that will be passed in is a dictionary containing the following elements: id => The identifier for the task that's making progress frame => the current frame number being rendered numFrames => the total number of frames that is being rendered frameProgress => the number of frames rendered so far. Please be aware that the current Nuke context will not make sense in the callback (e.g. nuke.thisNode will return a random node). """ _addBackgroundCallback(afterBackgroundFrameRenders, call, args, kwargs)
[docs]def removeAfterBackgroundFrameRender(call, args=(), kwargs={}): """Remove a previously-added callback with the same arguments.""" _removeBackgroundCallback(afterBackgroundFrameRenders, call, args, kwargs)
[docs]def afterBackgroundFrameRender(context): _doBackgroundCallbacks(afterBackgroundFrameRenders, context)
afterBackgroundRenders=[]
[docs]def addAfterBackgroundRender(call, args=(), kwargs={}): """Add code to execute after any background renders. The call must be in the form of: def foo(context): pass The context object that will be passed in is a dictionary containing the following elements: id => The identifier for the task that's ended Please be aware that the current Nuke context will not make sense in the callback (e.g. nuke.thisNode will return a random node). """ _addBackgroundCallback(afterBackgroundRenders, call, args, kwargs)
[docs]def removeAfterBackgroundRender(call, args=(), kwargs={}): """Remove a previously-added callback with the same arguments.""" _removeBackgroundCallback(afterBackgroundRenders, call, args, kwargs)
[docs]def afterBackgroundRender(context): _doBackgroundCallbacks(afterBackgroundRenders, context)
# filenameFilter is somewhat different due to it returning a string filenameFilters={}
[docs]def addFilenameFilter(call, args=(), kwargs={}, nodeClass='*'): """Add a function to modify filenames before Nuke passes them to the operating system. The first argument to the function is the filename, and it should return the new filename. None is the same as returning the string unchanged. All added functions are called in backwards order.""" _addCallback(filenameFilters, call, args, kwargs, nodeClass)
[docs]def removeFilenameFilter(call, args=(), kwargs={}, nodeClass='*'): """Remove a previously-added callback with the same arguments.""" _removeCallback(filenameFilters, call, args, kwargs, nodeClass)
[docs]def filenameFilter(filename): global filenameFilters if filenameFilters: # Run the filename through registered callbacks, starting with class-specific # ones. There are issues with calling thisClass() here so only do it if a # class-specific callback has been registered allNodesFilter = filenameFilters.get('*', []) if len(filenameFilters) > 1 or not allNodesFilter: classFilter = filenameFilters.get(nuke.thisClass(), []) for f in classFilter[::-1]: s = f[0](filename,*f[1],**f[2]) if s != None: filename = s for f in allNodesFilter[::-1]: s = f[0](filename,*f[1],**f[2]) if s != None: filename = s else: # For back-compatibility allow user to define a filenameFix() function: import __main__ if 'filenameFix' in __main__.__dict__: return __main__.__dict__['filenameFix'](filename) # For even further back-compatibility let them define a tcl filename_fix function: return nuke.tcl("filename_fix",filename) return filename
validateFilenames={}
[docs]def addValidateFilename(call, args=(), kwargs={}, nodeClass='Write'): """Add a function to validate a filename in Write nodes. The first argument is the filename and it should return a Boolean as to whether the filename is valid or not. If a callback is provided, it will control whether the Render button of Write nodes and the Execute button of WriteGeo nodes is enabled or not.""" _addCallback(validateFilenames, call, args, kwargs, nodeClass)
[docs]def removeFilenameValidate(call, args=(), kwargs={}, nodeClass='Write'): """Remove a previously-added callback.""" _removeCallback(validateFilenames, call, args, kwargs, nodeClass)
[docs]def validateFilename(filename): import __main__ list = validateFilenames.get(nuke.thisClass()) valid = True if list: for f in list: b = f[0](filename) if b == False: valid = False list = validateFilenames.get('*') if list: for f in list: b = f[0](filename) if b == False: valid = False return valid
def _doAutoSaveCallbacks( filters, filename ): import __main__ list = filters.get( 'Root' ) if list: for f in list: s = f[0](filename) filename = s return filename autoSaveFilters={}
[docs]def addAutoSaveFilter(filter): """addAutoSaveFilter(filter) -> None Add a function to modify the autosave filename before Nuke saves the current script on an autosave timeout. Look at rollingAutoSave.py in the nukescripts directory for an example of using the auto save filters. @param filter: A filter function. The first argument to the filter is the current autosave filename. The filter should return the filename to save the autosave to.""" _addCallback(autoSaveFilters, filter, (), {}, 'Root')
[docs]def removeAutoSaveFilter(filter): """Remove a previously-added callback with the same arguments.""" _removeCallback(autoSaveFilters, filter, (), {}, 'Root')
[docs]def autoSaveFilter(filename): """Internal function. Use addAutoSaveFilter to add a callback""" return _doAutoSaveCallbacks( autoSaveFilters, filename )
autoSaveRestoreFilters={}
[docs]def addAutoSaveRestoreFilter(filter): """addAutoSaveRestoreFilter(filter) -> None Add a function to modify the autosave restore file before Nuke attempts to restores the autosave file. Look at rollingAutoSave.py in the nukescripts directory for an example of using the auto save filters. @param filter: A filter function. The first argument to the filter is the current autosave filename. This function should return the filename to load autosave from or it should return None if the autosave file should be ignored.""" _addCallback(autoSaveRestoreFilters, filter, (), {}, 'Root')
[docs]def removeAutoSaveRestoreFilter(filter): """Remove a previously-added callback with the same arguments.""" _removeCallback(autoSaveRestoreFilters, filter, (), {}, 'Root')
[docs]def autoSaveRestoreFilter(filename): """Internal function. Use addAutoSaveRestoreFilter to add a callback""" return _doAutoSaveCallbacks( autoSaveRestoreFilters, filename )
autoSaveDeleteFilters={}
[docs]def addAutoSaveDeleteFilter(filter): """addAutoSaveDeleteFilter(filter) -> None Add a function to modify the autosave filename before Nuke attempts delete the autosave file. Look at rollingAutoSave.py in the nukescripts directory for an example of using the auto save filters. @param filter: A filter function. The first argument to the filter is the current autosave filename. This function should return the filename to delete or return None if no file should be deleted.""" _addCallback(autoSaveDeleteFilters, filter, (), {}, 'Root')
[docs]def removeAutoSaveDeleteFilter(filter): """Remove a previously-added callback with the same arguments.""" _removeCallback(autoSaveDeleteFilters, filter, (), {}, 'Root')
[docs]def autoSaveDeleteFilter(filename): """Internal function. Use addAutoSaveDeleteFilter to add a callback""" return _doAutoSaveCallbacks( autoSaveDeleteFilters, filename )