CgTd

Nuke Python 回调函数

2016-11-22  本文已影响0人  N景波

使用下文描述的nuke.add...()函数,当有变量事件(比如,创建节点,加载脚本)时就自动调用python函数。

在init.py,menu.py里面可以随便用nuke.add...(). 这代码会被当作nuke环境的一部分,不跟着脚本变。

所有nuke.add...()函数的参数都一样, 例如:

nuke.addOnCreate( callable, args=(), kwargs={}, nodeClass='*' )

其中:

默认的*意味着可以被任何节点调用。

在callback中,有个context节点,使用nuke.thisNode()时需要检验。knobChanged,有一个可以利用nuke.thisKonb()获取的内容knob。

对于众多的nuke.add...() 函数,比如 onCreate,也有同名的knob,其分成两类:

如果很多注册的回调,附加到knob上的代码总是优先于nuke.add...()( 例如,放入到onCreate knob的代码,要比nuke.addOnCreate()先调用)大多数情况下,nuke.add...()函数都会按顺序跟着一个对应的nodeClass。最后,带* 的,也是添加的顺序。注意addAutolabel()和addFilenameFilter()函数是例外,他们按照添加时候的逆序调用。

所有的回调函数都有对应的移除函数

OnUserCreate
nuke.addOnUserCreate( function )
nuke.removeOnUserCreate( function )

当用户在GUI模式下创建节点时就执行。启动时在root和viewer节点上也会调用。但是加载现有脚本,粘贴节点,undo delete时,不会执行。

可以用这个代码改变knob的默认值,添加user knob,执行其他行为。
nuke.addOnUserCreate 在默认值改变后立即运行。要在onCreate前面。如果没有使用nuke.addOnUserCreate(),那就会调用nuke.tcl("OnCreate" )后向兼容。

onCreate
node.knob("onCreate" )
nuke.addOnCreate(function)
nuke.removeCreate(function)

当node创建时就执行,例如,当加载脚本,粘贴节点,选择菜单选项,撤销删除等。注意,如果onCreate在脚本加载前就定义了,会为每个节点执行。这是因为节点
创建出来后,脚本也已经加载了。 创建group时(包括root节点,包含很多内部节点),内部节点先调用onCreate,group节点后调用onCreate。root节点任何脚本
加载时都会执行(查看onScriptLoad) 或 菜单中File》New被选择时也会执行。 预设和python knob panel创建时也会执行。

例如,下面的代码让每个节点创建时在终端输出OnCreate。

nuke.addOnCreate( lambda: nuke.tprint(" OnCreate called for " + nuke.thisNode().name() ))
onScriptLoad
root.knob('onScriptLoad'), nuke.addOnScriptLoad( function), nuke.removeOnScriptLoad(function)
onScriptLoad
root.knob('onScriptLoad')
nuke.addOnScriptLoad( function )
nuke.removeOnScriptLoad( function )

当脚本加载时就执行,在root的onCreate创建后立即执行。注意,其不为新脚本执行。
在Project Setting》Python 标签可以看到 onScriptLoad的knob

imageimage
onScriptSave
root.knob(' onSrciptSave')
nuke.addOnScriptSave( function)
nuke.removeOnScriptSave( function )

当用户保存脚本时运行。


imageimage
onScriptClose
root.knob('onScriptClose')
nuke.addOnScriptClose( function )
nuke.removeOnScriptClose( function )

当用户退出nuke或者关闭脚本,scriptClear()函数


imageimage
onDestroy
node.knob('onDestroy')
nuke.addOnDestroy( function )
nuke.removeOnDestroy( function )

节点删除时会执行此函数,包括撤销节点的创建。root节点关闭后也会调用,用户退出nuke,nuke崩溃后不会为Preferences。
对于group和root 此函数比所有孩子都要先运行。

knobChanged
node.knob('knobChanged')
nuke.addKnobChanged( function )
nuke.removeKnobChanged( function )

panel打开时,任何对knob数值的修改都能执行回调,不会递归调用。使用nuke.thisKnob()来查看哪个knob被改变了。
knobChanged可以制作带有knob的gizmos,knob有enabling,disabling,presettin等。nuke.thisKnob()能获取变动的那个knob。

注意:knobChanged是用来刷新panel控制面板。不能用来跟踪knobs(例如,用数据库的数据来更新所有的knobs)。因为当Properties面板关闭时,就不调用了。想跟踪knobs,用下面的updateUI

用户打开、关闭properties面板时,panel打开状态下,变动了input的连接,就可以使用knobChanged中的showPanelinputChange 这两个knobs。否则on change回调更改了knob的值,就要响应showPanel来设置正确的状态了。例如:

# if slider is zero, disable the checkmark:
n = nuke.thisNode()
k = nuke.thisKnob()
if k.name()=="slider" or k.name()=="showPanel":
  n['checkmark'].setEnable(n['slider'].getValue()!=0)

nuke.addKnobChanged 可以一次获取ColorCorrect节点的gain和gamma 滑动条,用户更改了其中一个滑动条,另一个会自动变为同样的值。获取滑动条的代码:

def gangGammaGainSliders():
 n = nuke.thisNode()
 k = nuke.thisKnob()
 if k.name() == "gamma":
   n['gain'].setValue(k.value())
 elif k.name() == "gain":
   n['gamma'].setValue(k.value())
nuke.addKnobChanged(gangGammaGainSliders, nodeClass="ColorCorrect")
updateUI
node.knob( 'updateUI')
nuke.addUpdateUI( function )
nuke.removeUpdateUI( function )

脚本变动后,每个节点都运行此回调。这个是作为空闲时间执行的低优先级进程来实现的。很可能Nuke都开始给view渲染计算了,updateUI还没执行呢。因此updateUI不能执行更改图像的脚本(否则,viewer就要重启了)。

updateUIautolabel之前knobChanged之后运行。想不让它在每个节点运行,Nuke检查updateUI看上去像啥(比如,帧,view数量)来决定是否运行它。 如果函数推测不出来帧和view数量,Nuke仅在这个node上knob变动时调用一次,frame或view变动不会调用。

autolabel
node.knob('autolabel')
nuke.addAutolabel( function )
nuke.removeAutolabel( function )

updateUI后立马运行上面脚本,并返回绘制节点的绘制字符串。如果autolabel不是空的,或返回任何非空,返回值就显示在节点上。否则,nuke.addAutolabel()会被逆序调用,使用第一个返回非None的。如果都返回None,那就用nuke.thisNode().name().

为了后向兼容,Nuke调用启动时调用addAutolabel(main.autolabel)

beforeRender
write.knob('beforeRender')
nuke.addBeforeRender( function )

这个在execute()开始渲染之前就运行了。抛出异常,就不进行后续渲染了。可以用这个特性来创建目标输出文件夹或者和农场通信,例如,beforeRender knob(GUI上是before render)就在write节点的Render页面上。

提示:默认情况下,NUKE渲染时不创建文件夹。如果Write节点的输出路径所在文件夹不存在,渲染会失败。但是,能通过nuke.addBeforeRender()注册回调函数渲染之前创建文件夹来改变nuke的默认行为。 下面是一个例子:

  1. 插件目录中创建 init.py 如果不存在
  2. 文件中添加如下代码:
def createWriteDir():
    import nuke, os, errno
    file = nuke.filename( nuke.thisNode() )
    dir = os.path.dirname( file )
    osdir = nuke.callbacks.filenameFilter( dir )
    try:
    os.makedirs( osdir )
    except OSError, e:
    if e.errno != errno.EEXIST:
    raise
    nuke.addBeforeRender( createWriteDir )
imageimage
beforeFrameRender
write.knob('beforeFrameRender')
nuke.addBeforeFrameRender( function )
nuke.removeBeforeFrameRender(function )

每帧开始渲染前执行。如果抛出了错误,渲染就终止。 这个对渲染完拷贝到数字视频录像机(DVR)或跟渲染农场通信很有用。例如,beforeFrameRender knob (GUI上的 after render)就在输出节点的Render页面下。

imageimage
afterFrameRender
write.knob('afterFrameRender')
nuke.addAfterFrameRender( function)
nuke.removeAfterFrameRender( function )

每帧渲染完成后执行。如果抛出了错误,渲染就终止。 这个对渲染完拷贝到数字视频录像机(DVR)或跟渲染农场通信很有用。例如,afterFrameRender knob (GUI上的 after render)就在输出节点的Render页面下。

imageimage
afterRender
write.knob('afterRender')
nuke.addAfterRender( function )
nuke.removeAfterRender( function )

所有帧渲染完成后运行。如果抛出了错误,渲染就终止。 这个对渲染完拷贝到数字视频录像机(DVR)或跟渲染农场通信很有用。例如,afterRender knob (GUI上的 after render)就在输出节点的Render页面下。

imageimage
afterBackgroundRender
nuke.addAfterBackgroundRender( function )
nuke.removeAfterBackgroundRender( function )

后台渲染后执行,调用形式必须这样:

def foo( context ):
    pass

context是一个包含下面元素的字典:

请注意当前NUKE上下文对回调无效,例如,nuke.thisNode会随机返回一个节点。

afterBackgroundFrameRender
nuke.addBackgroundFrameRender( function )
nuke.removeAfterBackgroundFrameRender( function )

调用形式如下:

def foo( context ):
    pass

context是一个包含下面元素的字典:

请注意当前NUKE上下文对回调无效,例如,nuke.thisNode会随机返回一个节点。

filenameFilter
nuke.addFilenameFilter( function )
nuke.removeFilenameFilter( function )

在脚本传输给系统前,给过程添加一个过滤函数来处理文件名。
为了后向兼容,如果没有添加函数,nuke运行mian.filenameFix(s). 默认版本调用nuke.tcl('filename_fix',s)
使用这个来进行路径转换,从windows到linux的转换:

添加了一个filter在Nuke脚本传输到操作系统前处理所有的文件名。这些filter能消除操作系统间的差异,也能插入需要的路径。回调函数的参数必须是一个字符串,返回值可以是字符串也可以是None(这个返回没改动的字符串是一样的)。文件名并不是一个特殊的节点,比如插件名,此函数调用nuke.thisNode()设置root节点。所有传给nuke.addFilenameFilter的函数逆序调用。

为了后向兼容,如果没有添加函数,nuke运行main.filenameFix(s) 。默认版本调用nuke.tcl('filename_fix',s)

下面的例子使用filename filter来处理win和linux混合环境下的路径问题。例如,工作站是win系统,访问共享的驱动器‘Y:’,在linux对应的挂载点是‘/mnt/y/’:

import nuke

def myFilenameFilter(filename):
    if nuke.env['LINUX']:
        filename = filename.replace( 'y:', '/mnt/y' )
        filename = filename.replace( 'x:', '/mnt/x' )
    else:
        filename = filename.replace( '/mnt/y', 'y:' )
        filename = filename.replace( '/mnt/x', 'x:' )

    return filename

nuke.addFilenameFilter(myFilenameFilter)
validateFilename
nuke.addValidateFilename( function )
nuke.removeValidateFilename( function )

验证write node中的文件名。第一个参数是文件名,返回布尔值,说明文件名是否有效。如果提供了回调函数,Write node中的Render按钮,WriteGeo节点中的Excute按钮可用与否就受回调函数结果影响。

autoSaveFilter
nuke.addAutoSaveFilter( function )
nuke.removeAutosaveFilter( function )

在nuke自动保存文件文件前,对autosave进行修改。

def myAutoSaveFilter( filename ):
return filename

第一个参数为当前autosave文件名,返回的是要保存的aotusave文件名,木有就返回None。

更多请看 使用AutoSave回调实现AutoSave回滚

autoSaveRestoreFilter
nuke.addAutoSaveRestoreFilter( function )
nuke.removeAutoSaveRestore( function )

Nuke恢复aotosave文件时修改autosave获取的文件名

函数格式如下:

def myAutoSaveRestoreFilter( filename ):
    return filename

第一个参数为当前autosave 文件名,函数返回要加载的autosave文件名,没有返回None。如果aotosave恢复filter返回None这也压制了Nuke对话框询问用户是否加载一个找到的autosave。

更多请看 使用AutoSave回调实现AutoSave回滚

autoSaveDeleteFilter

在nuke删除自动保存文件时修改autosave 的文件名

filter函数形式如下:

def myAutoSaveDeleteFilter( filename ):
    return filename

第一个参数就是autosave的文件名。返回要删除的文件名,没有可删除的就返回None。

更多请看 使用AutoSave回调实现AutoSave回滚

使用autosave的回调完成autosave回滚操作

使用三个autosave回调来完成回滚。每次调用autosave,就创建一个新的autosave,数组从1-9.例如:autosave, autosave1, autosave2....autosave9

import nuke
import glob
import time
import os

### Example that implements a rolling autosave using the autoSaveFilter callbacks
###
## autosaves roll from 0-9 eg myfile.autosave, myfile.autosave1, myfile.autosave2...
#
## To use just add 'import nukescripts.autosave' in your init.py


def onAutoSave(filename):

  ## ignore untiled autosave
  if nuke.root().name() == 'Root':
    return filename

  fileNo = 0
  files = getAutoSaveFiles(filename)

  if len(files) > 0 :
    lastFile = files[-1]
    # get the last file number

    if len(lastFile) > 0:
      try:
        fileNo = int(lastFile[-1:])
      except:
        pass

      fileNo = fileNo + 1

  if ( fileNo > 9 ):
    fileNo = 0

  if ( fileNo != 0 ):
    filename = filename + str(fileNo)

  return filename


def onAutoSaveRestore(filename):

  files = getAutoSaveFiles(filename)

  if len(files) > 0:
    filename = files[-1]

  return filename

def onAutoSaveDelete(filename):

  ## only delete untiled autosave
  if nuke.root().name() == 'Root':
    return filename

  # return None here to not delete auto save file
  return None


def getAutoSaveFiles(filename):
  date_file_list = []
  files = glob.glob(filename + '[1-9]')
  files.extend( glob.glob(filename) )

  for file in files:
      # retrieves the stats for the current file as a tuple
      # (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)
      # the tuple element mtime at index 8 is the last-modified-date
      stats = os.stat(file)
      # create tuple (year yyyy, month(1-12), day(1-31), hour(0-23), minute(0-59), second(0-59),
      # weekday(0-6, 0 is monday), Julian day(1-366), daylight flag(-1,0 or 1)) from seconds since epoch
      # note:  this tuple can be sorted properly by date and time
      lastmod_date = time.localtime(stats[8])
      #print image_file, lastmod_date   # test
      # create list of tuples ready for sorting by date
      date_file_tuple = lastmod_date, file
      date_file_list.append(date_file_tuple)

  date_file_list.sort()
  return [ filename for _, filename in date_file_list ]


nuke.addAutoSaveFilter( onAutoSave )
nuke.addAutoSaveRestoreFilter( onAutoSaveRestore )
nuke.addAutoSaveDeleteFilter( onAutoSaveDelete )

### As an example to remove the callbacks use this code
#nuke.removeAutoSaveFilter( onAutoSave )
#nuke.removeAutoSaveRestoreFilter( onAutoSaveRestore )
#nuke.removeAutoSaveDeleteFilter( onAutoSaveDelete )
在root上使用回调来添加双目设置

添加一段脚本,将新项目设置成stereo/multi view项目,操作如下:

# if necessary import the module that holds the script you want to run on startup:
import examples
# prepping the argument for this particular script
views = [('L', (0,.5,0)), ('R',(.5,0,0)), ('M',(.5,.5,0))]
# add script to the callback
nuke.addOnUserCreate( examples.setUpMultiView, views, nodeClass='Root' )

更多setupMultiView脚本,请看Setting Up Stereo

用户第一次创建某类节点时会触发onUserCreateOnCreate是每个节点创建时都会调用,甚至复制,从磁盘加载也会)。既然root也是个节点,就能用nodeClass过滤下保证仅root节点创建时执行。

上面的脚本加入menu.pyinit.py后,新的nuke脚本有三个view了,L(绿色),R(红色), M(黄色)。

imageimage
默认色彩空间

Nuke10中可以动态替换Read和Write节点使用的色彩空间。

默认色彩空间是特定文件的Reader和Writer对象在读取和写入文件时告诉Nuke最可能的那个色彩空间。有以下几种途径来获取:

多亏OpenColorIO(OCIO)在Nuke中的配置,现在很容易支持Nuke搞不定的色彩空间。由于色彩空间匹配不上,就很容易获取Nuke的错误状态,尤其使用自定义OCIO配置时。下面的回调能处理这些自定义配置导致的错误。

和其他回调一样,用下面的方法可以添加和删除回调:

nuke.addDefaultColorspaceMapper(function)
nuke.removeDefaultColorspaceMapper(function)

函数形式如下:

def myDefaultColorspaceMapper(colorspaceName, dataTypeHint) :
  # ... map colorspace name to desired colorspace ...
  return colorspaceName

传递的对象如下:

项目设置中data type hint的值:

DDImage/LUT.h中的其他data-type,后向兼容Reader和Writer:

例如: 想支持ACES 1.0.1 预览设置,添加如下回调函数来保证R3D文件的修正函数。

import nuke

def r3d_aces101_default_colorspace(name, dataTypeHint) :
  """ finds appropriate R3D colorspaces in the aces 101 config """

  usePrefs = (dataTypeHint >= 0) and (dataTypeHint <= nuke.FLOAT) #  5 = LUT::FLOAT
  if usePrefs:
    return name

  assert "/" not in name, "family name unexpectedly found in colorspace name"
  allColorspaces = nuke.colorspaces.getColorspaceList( nuke.thisNode().knob('colorspace') )[1:]
  exactMatch = name in allColorspaces
  if exactMatch:
    # if there's an exact name-match in the config use that
    return name

  nukeRedLog = (dataTypeHint == 10) # LUT::REDLOG
  if nukeRedLog:
    # first use the Nuke-side data-type hint
    aces101RedLog = "Input - RED - Curve - REDlogFilm"
    if aces101RedLog in allColorspaces :
      # we have the aces101 specific RED-log space, return that
      return aces101RedLog

  if name == "rec709":
    acesRec709 = "Output - Rec.709"
    if acesRec709 in allColorspaces:
      return acesRec709
  elif name == "REDSpace":
    acesRedSpace = "Input - RED - REDlogFilm - REDcolor4"
    if acesRedSpace in allColorspaces:
      return acesRedSpace


# add the mapper function to NUKE
nuke.addDefaultColorspaceMapper( r3d_aces101_default_colorspace )
上一篇 下一篇

猜你喜欢

热点阅读