【NUKE教程】Nuke Python开发入门指导

10 九月, 2016
34
0

nuke_python_rumen1

作者介绍

课程介绍

本章的例子帮你初步了解Nuke Python API的使用。脚本大小写敏感,需要输入正确才能运行。拷贝时注意缩进,如报错请自己更正。创建节点设置节点控制,此部分演示如何创建节点,设置节点控制。

在用户界面创建节点

在用户界面创建类似 menu toolbar的响应节点,使用下面语法:

nuke.createNode( "nodename" )

nodename 就是你要创建节点的名字.
这句话在用户界面创建一个新的节点,会在节点图中挂接到用户选择的节点上,并打开节点的属性面板。另外,新节点上默认的knob也设置了。
想创建blur节点,用这句话,输入:

	
nuke.createNode("Blur")

创建脚本节点

为脚本,批渲染,某些后台程序使用而创建的节点,请使用下面的语法:

	
nuke.nodes.nodename(...)

其中nodename是节点的名字。
这创建一个新的node对象。在节点图中没有和其他节点相连,也没打开节点的属性面板,也不会设置默认knob。
例如,添加一个Blur节点,请输入:

nuke.nodes.Blur()

和其他节点连接:

nuke.nodes.Blur().setInput( 0, nuke.selectedNode() )

创建时添加控制

创建节点时添加必要的控制属性,语法如下:

	
nuke.nodes.node( control = value )

例子:

nuke.nodes.Blur( size = 10 )

创建一个节点,并重命名:

nuke.nodes.nodename( name="newname")

创建一个名为Projection_Cam的摄像机:

	
nuke.nodes.Camera( name="Projection_Cam" )

创建指向文件的读取节点:

	
nuke.nodes.Read( file = " filePath/filename.txt" )

赋值

既然在创建节点后想操纵它,那给其赋值就合情理。可以用变量来引用节点。
添加一个节点,并赋值给变量:

variable = nuke.nodes.nodename()
b = nuke.nodes.Blur()
b["size"].setValue( 10)

获取已存在节点

有时候需要读取节点图中已经有的节点。
通过名字去读节点:

	
nuke.toNode("dagnodename")

如果节点图里面有个叫 Blur1 的节点,使用下面的语句来读取:

	
myblurNode = nuke.toNode('Blur1')

获取选中的节点

获取当前选择的节点:

	
selectNode = nuke.selectedNode()

如果选了多个节点,nuke返回最底部的节点。想读取所有的节点,请使用返回选择的列表:

	
selectNodes = nuke.selectNodes()

添加控制

现在我们看看如何给已经存在的节点设置属性。如果你想添加一个新的控制呢? 你要使用下面的句子:

b = nuke.nodes.nodename(...)
k = nuke.Array_Knob( "name", "label")
b.addKnob(k)

假设你输入了下面语句:

b = nuke.nodes.Blur()
k = nuke.Array_Knob("myctrl", "My Control" )
b.addKnob(k)


如果你想创建滑动控制,而不是输入框,请用下面的句子( 把Array_Knob替换成 WH_Knob)

b = nuke.nodes.Blur()
k = nuke.WH_Knob("myctrl", "My Control" )
b.addKnob(k)

下面的句子,会添加一个checkbox:

b = nuke.nodes.Blur()
k = nuke.Boolean_Knob("myctrl", "My Control" )
b.addKnob(k)

现在有了控制,但没有提示,咱添一个,给tool tip添加提示 My tooltip:

k.setTooltip('My tooltip' )

python_add_knob_tooltip

隐藏、展示节点的属性panel

使用showControlPanel() hideControlPanel()函数来设置一个节点的属性面板的打开与关闭。例如,展示新节点Blur的属性面板:

n = nuke.toNode("Blur1")
n.showControlPanel()

默认情况下,当在用户界面nuke.createNode(…)创建节点,属性面板是打开的。不想属性面板在创建时打开,请给createNode()传入inPanel,并设置为false。

nuke.createNode( "Blur", inPanel = False )

连接节点,设置输入

可以用脚本来设置节点的输入。假设你想添加Read和Merge节点( 这个例子,是over节点)并将Read节点连接到over节点的A和B输入(数字1和0)。
merge_node
添加Read节点和over节点,并说明over节点输入的使用,例如,下面的语句:

r1 = nuke.nodes.Read( file="filepath/filename.ext" )
r2 = nuke.nodes.Read( file ="filePaht/filename.ext")
m = nuke.nodes.Merge( inputs=[r2, r1] )

设置控制的默认值

可以给属于同一个类的节点设置控制的默认值。当默认值设置后,所有名字相同的控制都会是这个值。

nuke.knobDefault()

例如,想设置所有Blur节点的size 控制为20:

nuke.knobDefault( "Blur.size", "20" )

把项目设置中frame范围的最后frame设置为200:

nuke.knobDefault( "Root.last_frame", "200" )

注意,节点类的首字母必须大写。
knobDefault也可以设置 file-format-specific控制。但文件名变化时,这些控制被加入了 Read, Write, 其他file-format-独立节点。说明file-format-specific默认值,使用class名字,后跟文件扩展名 和 控制名字,请有.来区分,例如:

nuke.knobDefault( "Read.exr.compression", "2")
nuke.knobDefault( "Read.exr.disable_mmap", "True")

渲染时用Write节点

用脚本添加了Write节点,现在要渲染1-35帧。
渲染一个单独的节点:

nuke.execute(" name", start, end, incr )

在我们的例子里:

nuke.execute(" Write1", 1, 35, 2) or nuke.execute("Write1, start=1, end=35,incr=2)

也可以使用

nuke.render( name, start, end, incr )

渲染多个Write 节点和范围,请输入:

nuke.executeMultiple( (variable,), ([start, end, incr], ) ) varible--Write nodes

外部程序序列化

FrameCycler是nuke给flipbooking的默认程序,你也可以用别的程序。像这样就得用python写。例子就是基于RV来实现,位于pyQtExample目录下面,如下操作:
1. 找到filpbookingExample.py 文件,根据自己配置修改环境变量
2. 将其保存到你的 .nuke目录,命名为 myflipbook.py
3. 在自己的 init.py( or menu.py )文件中,添加:

from myflipbook import *

更多信息请查看python文档

列举节点的控制

如果需要nuke可以列出节点所有的控制,操作如下:

for i in range( getNumKnobs() ):
            print knob(i).name()

列出Blur节点的控制,前面创建的并赋值给了b:

for i in range( b.getNumKnobs() ):
            print b.knob(i).name

undo和redo

nuke.undo  nuke.redo

帧导航

使用活动窗口的 frame navigation按钮,输入命令:

nuke.activeViewer().frameControl(i)
其中i是一个整型,表示了想要执行的导航按钮,可以用下面的值替换:

-6 to go to the first frame.
-5 to play the sequence backward.
-4 to go to the previous keyframe.
-3 to step back by increment.
-2 to go back to the previous keyframe or increment, whichever is * closer.
-1 to step back one frame.
0 to stop playback.
+1 to step forward one frame.
+2 to go to the next keyframe or increment, whichever is closer.
+3 to step forward by increment.
+4 to go to the next keyframe.
+5 to play the sequence forward.
+6 to go to the last frame.
也可以将其赋值给快捷键。例如,将 播放 按钮连接到 向上箭头:
在你的插件目录 创建 menu.py ,
添加如下代码:

def play():
    v = nuke.activeViewer()
    if v:
        v.play( 1 )
menubar = nuke.menu("Nuke")
m = menubar.addMenu("&File")
m.addCommand("@;Play", "play()", "Up")

设置帧范围

设置单个的帧范围:


nuke.FrameRange() 只能设置一种

frange = nuke.FrameRange(" 1-100 x 2")

要迭代上面range的frame,使用:

for f in frange:
        print f

也可以使用下面方法:

frange.setFirst( int )
frange.setLast( int )
frange.setIncrement( int )
frange.first()
frange.last()
frange.increment()
frange.frames()
frange.getFrame( int )
frange.isInRange( int )
frange.minFrame()
frange.maxFrame()
frange.stepFrame()

存储多个帧范围:

nuke.FrameRanges()

当使用这个函数时,可以按下面方式定义帧范围:
列出所有帧

frange = nuke.FrameRange( [1, 2, 3,6, 7])

使用一个字符串

franges = nuke.FrameRanges( "1-100 2-300X2" )

使用多个字符串

franges = nuke.FrameRanges( ["1-10X1", "15-18X1 30-40X2" ] )

使用多个nuke.FrameRange() :

franges = nuke.FrameRanges( [nuke.FrameRange(1, 100, 5), nuke.FrameRange(200, 250,30] )

迭代遍历所有帧ranges(这个例子中,使用franges变量):

for r in franges:
    for f in r:
        print f

也可以使用下面的函数:

franges.size()
franges.add()
franges.minFrame()
franges.maxFrame()
franges.clear()
franges.toFrameList()
franges.getRange()
franges.compact()

优化frame range表达的方式。其会移除复制的frame ranges,用更紧凑的方式表达,下面有两个例子:

franges1 = nuke.FrameRanges("10-7x-1 1-5x1 3-7x1)
franges1.compact()
print "Ranges1: " +franges

返回 Ranges1: 1-10×1
例子2:

franges2 = nuke.FrameRanges(" 10-7x-1 6-8x1 1-4x1 2-3x1")
franges2.compact()
print "Ranges2: " + franges2

返回: Ranges2: 1-4×1 6-10×1

文件名中标注帧号

nuke中,文件名里可以通过#或者表达式 %04d 来表示帧。这你就会发现 nukescripts.replaceHashes()好用。这让帧号码替换极为容易:

filename = nukescripts.replaceHashes( node['file'].value() ) % nuke.frame()

节点间拷贝动画曲线

动画曲线从 Blur1 拷贝到 Blur2:

b1= nuke.nodes.Blur()
b2= nuke.nodes.Blur()
k1 = b1['size']
k1.setAnimated()
k1.setValue( 10, time=30)
k1.setValue( 20, time=40)
k2=b2['size']
k2.copyAnimations( k1.animations() )

重载某节点的创建

例如,nuke包含两种merge节点: Merge Merge2. 默认Merge2是从工具栏上选择才能使用的。如果你更喜欢Merge,就可以重载创建,让其成为默认创建类型。按下面步骤:
创建menu.py
添加如下代码

class MyCustomNodes():
    def __getattr__(self, args):
        if args == "Merge2" : args = "Merge"
            return nuke.NodeConstructor(args)
nuke.nodes = MyCustomNodes()

用nuke.createNode也能完成同样的功能,那么就用下面的代码:

def createMyCustomNodes( node, knobs="", inpanel = True):
            if node =="Merge2" : node = "Merge"
            return nukeOriginalCreateNode( node=node, knobs=knobs, inpanel = inpanel)
nuke.createNode = createMyCustomNodes

获取正在运行的Nuke环境信息

nuke模块中,有个env对象,给出了nuke运行的环境变量。读取方式:

nuke.env["key"]
nuke.env["PluginExtension"]
NukeVersionMajor
NukeVersionMinor
NukeVersionRelease
NukeversionPhase
NukeVersionPhaseNumber
NukeVersionDate
NukeVersionString
threads
numCPUs
gui
ExecutablePath
ple
WIN32
MACOS
LINUX
64bit

打印环境变量: nuke.env

获取节点的Metadata

获取节点的元数据,并存在字典里:

nuke.toNode("Read1").metadata()

某一帧或者视口的元数据:

nuke.toNode("Read1").metadata("key", frame, "view" )

已经在一个节点里加载了双目数据,想找出左眼93帧的修改时间:

nuke.toNode("Read1").metadata("input/mtime", 93, "left")

相似,获取右眼95帧文件大小

nuke.toNode("Read1").metadata("input/filesize", 95, "right”)

获取指定元数据:

nuke.toNode("Read1").metadata("key")

例如:

nuke.toNode("Read1").metadata("input/ctime")

创建Dialog和Panel

创建带有ok和cancel按钮的对话框,请按照如下步骤:
在plug-in文件夹创建menu.py 文件(如果没有此文件的话)。
在文件中扩展python类 PythonPanel:

class ClassName( nukescripts.PythonPanel ):
        def __init__(self):
            nukescripts.PythonPanel.__init__( self, "titile", "ID" )

使用addKnob()对对话框添加控制。removeKnob()可以移除控制,knobs()返回控制列表。

writeKnobs() readKnobs()

用showModalDialog()来显示对话框。

创建模态对话框-Python对话框 例子

控制Frame的对话框
modal_dialog

import nukescripts
if nuke.env["gui"]:
# The following defines a new class called ModalFramePanel.
   class ModalFramePanel( nukescripts.PythonPanel ):
# The following function creates a dialog titled 'Go to Frame' with the optional ID uk.co.thefoundry.FramePanel. It aso adds an integer control called
'frame' to the dialog. This control is set to the value nuke.frame() which is the
current frame on the timeline.
      def __init__( self ):
         nukescripts.PythonPanel.__init__( self, "Go to Frame", "uk.co.thefoundry.FramePanel" )
         self.frame = nuke.Int_Knob( "frame", "Frame:" )
         self.addKnob( self.frame )
         self.frame.setValue( nuke.frame() )
# The next function shows the dialog as a modal dialog. Doing this automatically adds the 'OK' and 'Cancel' buttons to the dialog.
      def showModalDialog( self ):
         result = nukescripts.PythonPanel.showModalDialog( self )
         if result:
            nuke.frame( self.frame.value() )
# The following function is called testModalPanel and tests whether the dialog works.
   def testModalPanel():
      return ModalFramePanel().showModalDialog()
# This last line calls the test function that then displays the dialog.
   testModalPanel()
# If you want to add the dialog to a menu item, you can also do the following:
menubar = nuke.menu("Nuke")
menubar.addCommand("&File/Show My Panel", testModalPanel)

创建非模态对话框-非模态对话框例子

非模式标签可以停靠在窗口里,并被保存下来。创建步骤:
1.如果没有menu.py就在plug-in文件夹创建。
2.添加如下代码:

class ClassName( nukescripts.PythonPanel ):
        def __init__( self ):
            nukescripts.PythonPanel.__init__( self, "titile", "ID" )

1.使用addKnob()添加控制
2.编写第二个创建panel的函数。使用addToPanel()将其添加到pane。nuke.thisPane()来显示。
3.创建菜单来调用前面的函数。
4.如果想要panel和窗口格局一起保存,那就必须使用init.py注册自己的panel,因为此文件的读取要早于格局的保存:

nukescripts.registerPanel( ID, function)

注销:

nukescripts.unregisterPanel( ID, funciton)

例子:从content 菜单 Pane》Frame Panel可以选择此菜单。

import nukescripts
class FramePanel( nukescirtps.PythonPanel ):
    def __init__(self):
        nukescripts.PythonPanel.__init__( self, "Go to frame", "uk.co.thefoundry.FramePanel")
        self.frame = nuke.Int_knob("frame", "Frame:" )
        self.addKnob(self.frame )
        self.frame.setValue( nuke.frame() )
    def knobChanged( self, knob ):
        if knob == self.frame:
            nuke.frame( self.frame.value() )
    def testPanel():
        return FramePanel().addToPane()
menu = nuke.menu("Pane")
menu.addCommand( "Frame Panel", testPanel)
nukescripts.registerPanel( "uk.co.thefoundry.FramePanel", testPanel )

创建进度条dialog

import threading
import time
def selfDestruct():
    task = nuke.ProgressTask("self destructing" )
    task.setMessage( "deleting files")
    for i in xrange( 0, 100):
        if task.isChanged():
            nuke.executeInMainThread( nuke.message, args= ("Phew!") )
            break
        task.setProgress(i)
        time.sleep( 0.5 )
threading.Thread(None, selfdestruct ).start()

注意:
需要循环,因为不知道何时任务会被取消
不能再nuke的主线程里面运行循环,那会阻塞nuke直到进程结束。
因此,需要开始新线程来控制进度条不能在线程里面调用nuke.message,因此要用nuke.executeInMainThread()来显示消息。

清空当前Nuke(.nk)脚本

	
nuke.scriptClear()

给立体项目创建View

nuke.Root().addView(" name" )

添加左目:

nuke.Root().addView(" left" )

调整立体项目中的控制值

项目中有left right 的view

n = nuke.nodes.Blur()
n['size'].splitView()
n['size'].setValue( 10, view = "left" )
n['size'].setValue( 20, view = "right")

添加了blur节点,将size的控制分开,左边10,右边20