Nuke Python 回调函数
使用下文描述的nuke.add...()函数,当有变量事件(比如,创建节点,加载脚本)时就自动调用python函数。
在init.py,menu.py里面可以随便用nuke.add...(). 这代码会被当作nuke环境的一部分,不跟着脚本变。
所有nuke.add...()函数的参数都一样, 例如:
nuke.addOnCreate( callable, args=(), kwargs={}, nodeClass='*' )
其中:
-
callable 是python调用,比如函数名之类
-
args 是参数列表,有圆括号包括着。比如:
nuke.addOnCreate( nuke.tprint, ( 'what to print' ))
-
kwargs是用k-v形式给的参数,比如:
nuke.addOnCreate( nuke.tprint, ('a', 'b'), {'sep':',' } )
-
nodeClass 仅在nuke.thisNode().nodeClass() 等于这个字符串时才调用,比如:
nuke.addOnCreate( nuke.tprint, ('Creating Write'), nodeClass='Write')
默认的*意味着可以被任何节点调用。
在callback中,有个context节点,使用nuke.thisNode()时需要检验。knobChanged,有一个可以利用nuke.thisKonb()获取的内容knob。
对于众多的nuke.add...() 函数,比如 onCreate,也有同名的knob,其分成两类:
-
可见knob-- 就是制作人员想编辑的knob。可以在属性panel的python面板看到。可见konb包括: onScriptLoad, onScriptSave, onScriptClose, beforeRender, beforeFrameRender, afterRender, afterFrameRender.
-
隐藏knob 在属性面板看不到他们,但可以用python来设置。允许定义gizmo的行为。制作人员不应该设置这些knob,因为用户设置会覆盖gizmo设置。隐藏knob包括: onCreate, onDestroy, knobChanged, updateUI, autolabel.
如果很多注册的回调,附加到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
onScriptSave
root.knob(' onSrciptSave')
nuke.addOnScriptSave( function)
nuke.removeOnScriptSave( function )
当用户保存脚本时运行。
image
onScriptClose
root.knob('onScriptClose')
nuke.addOnScriptClose( function )
nuke.removeOnScriptClose( function )
当用户退出nuke或者关闭脚本,scriptClear()函数
image
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中的showPanel、inputChange 这两个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就要重启了)。
updateUI在autolabel之前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的默认行为。 下面是一个例子:
- 插件目录中创建 init.py 如果不存在
- 文件中添加如下代码:
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 )
image
beforeFrameRender
write.knob('beforeFrameRender')
nuke.addBeforeFrameRender( function )
nuke.removeBeforeFrameRender(function )
每帧开始渲染前执行。如果抛出了错误,渲染就终止。 这个对渲染完拷贝到数字视频录像机(DVR)或跟渲染农场通信很有用。例如,beforeFrameRender knob (GUI上的 after render)就在输出节点的Render页面下。
afterFrameRender
write.knob('afterFrameRender')
nuke.addAfterFrameRender( function)
nuke.removeAfterFrameRender( function )
每帧渲染完成后执行。如果抛出了错误,渲染就终止。 这个对渲染完拷贝到数字视频录像机(DVR)或跟渲染农场通信很有用。例如,afterFrameRender knob (GUI上的 after render)就在输出节点的Render页面下。
afterRender
write.knob('afterRender')
nuke.addAfterRender( function )
nuke.removeAfterRender( function )
所有帧渲染完成后运行。如果抛出了错误,渲染就终止。 这个对渲染完拷贝到数字视频录像机(DVR)或跟渲染农场通信很有用。例如,afterRender knob (GUI上的 after render)就在输出节点的Render页面下。
imageafterBackgroundRender
nuke.addAfterBackgroundRender( function )
nuke.removeAfterBackgroundRender( function )
后台渲染后执行,调用形式必须这样:
def foo( context ):
pass
context是一个包含下面元素的字典:
- id -- 完成task的标识
请注意当前NUKE上下文对回调无效,例如,nuke.thisNode会随机返回一个节点。
afterBackgroundFrameRender
nuke.addBackgroundFrameRender( function )
nuke.removeAfterBackgroundFrameRender( function )
调用形式如下:
def foo( context ):
pass
context是一个包含下面元素的字典:
- id -- 完成task的标识
- frame -- 当前渲染的帧号
- numFrames -- 要渲染的总帧数
- framgeProgress -- 已经渲染了多少帧
请注意当前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。
autoSaveRestoreFilter
nuke.addAutoSaveRestoreFilter( function )
nuke.removeAutoSaveRestore( function )
Nuke恢复aotosave文件时修改autosave获取的文件名
函数格式如下:
def myAutoSaveRestoreFilter( filename ):
return filename
第一个参数为当前autosave 文件名,函数返回要加载的autosave文件名,没有返回None。如果aotosave恢复filter返回None这也压制了Nuke对话框询问用户是否加载一个找到的autosave。
autoSaveDeleteFilter
在nuke删除自动保存文件时修改autosave 的文件名
filter函数形式如下:
def myAutoSaveDeleteFilter( filename ):
return filename
第一个参数就是autosave的文件名。返回要删除的文件名,没有可删除的就返回None。
使用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
用户第一次创建某类节点时会触发onUserCreate(OnCreate是每个节点创建时都会调用,甚至复制,从磁盘加载也会)。既然root也是个节点,就能用nodeClass过滤下保证仅root节点创建时执行。
上面的脚本加入menu.py或init.py后,新的nuke脚本有三个view了,L(绿色),R(红色), M(黄色)。
image默认色彩空间
Nuke10中可以动态替换Read和Write节点使用的色彩空间。
默认色彩空间是特定文件的Reader和Writer对象在读取和写入文件时告诉Nuke最可能的那个色彩空间。有以下几种途径来获取:
-
文件metadata或文件头中存了colorspace的名字,Reader没准会用这个名字来设置默认色彩空间。
-
文件包含特定数据类型,8bit,16bit,浮点数或者log空间,Nuke从项目设置中读取对应格式的色彩空间。
-
Reader、Writer 会通过ID请求内置的LUT ,例如REDlog, AlexaLog, ProTune,Gamma2.2( 查看DDImage/LUT.h 中的DataType)
多亏OpenColorIO(OCIO)在Nuke中的配置,现在很容易支持Nuke搞不定的色彩空间。由于色彩空间匹配不上,就很容易获取Nuke的错误状态,尤其使用自定义OCIO配置时。下面的回调能处理这些自定义配置导致的错误。
和其他回调一样,用下面的方法可以添加和删除回调:
nuke.addDefaultColorspaceMapper(function)
nuke.removeDefaultColorspaceMapper(function)
函数形式如下:
def myDefaultColorspaceMapper(colorspaceName, dataTypeHint) :
# ... map colorspace name to desired colorspace ...
return colorspaceName
传递的对象如下:
- name -- Reader、Writer当前请求的色彩空间,可能是Nuke默认的,或其他存在,不存在的,没准不是色彩空间。
- dataTypeHint -- 如上,为下面三种情况之一
- nuke.INVALID - 仅设置了色彩空间的名字
- 链接到项目设置里面的一个值, 如下-- 色彩空间会根据项目设置来定。注意,回调是链式调用,和项目设置中的色彩空间匹配这个不可靠,因为前面执行的回调可能改了它。
- 内置LUT的ID -- 色彩空间名字为下表中的第一个。
项目设置中data type hint的值:
- nuke.INT8 色彩空间会设置成项目设置中‘8-bit files’这个值
- nuke.INT16 色彩空间会设置成项目设置中‘16-bit files’这个值
- nuke.MONITOR 色彩空间会设置成项目设置中‘monitor’这个值, 用于显示,比如,viewers和 邮票
- nuke.FLOAT 色彩空间会设置成项目设置中‘float files’这个值
- nuke.LOG 色彩空间会设置成项目设置中‘log files’这个值
DDImage/LUT.h中的其他data-type,后向兼容Reader和Writer:
- nuke.VIEWER - unused
- 6 - equivalent to DD::Image::LUT::GAMMA1_8
- 7 - equivalent to DD::Image::LUT::GAMMA2_2
- 8 - equivalent to DD::Image::LUT::GAMMA2_4
- 9 - equivalent to DD::Image::LUT::PANALOG
- 10 - equivalent to DD::Image::LUT::REDLOG
- 11 - equivalent to DD::Image::LUT::VIPERLOG
- 12 - equivalent to DD::Image::LUT::ALEXAV3LOGC
- 13 - equivalent to DD::Image::LUT::PLOGLIN
- 14 - equivalent to DD::Image::LUT::SLOG
- 15 - equivalent to DD::Image::LUT::SLOG1
- 16 - equivalent to DD::Image::LUT::SLOG2
- 17 - equivalent to DD::Image::LUT::SLOG3
- 18 - equivalent to DD::Image::LUT::CLOG
- 19 - equivalent to DD::Image::LUT::PROTUNE
例如: 想支持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 )