https://www.bilibili.com/video/BV1bm4y1R7X1/?spm_id_from=333.788&vd_source=b1de3fe38e887eb40fc55a5485724480

为Nuke添加内置python编辑器

github地址


令脚本编辑器中显示在nuke中进行的操作对应的代码

首先按shift+s进入首选项设置界面,然后再Panels-Script Editor中勾选 echo python commands to output window


显示节点的详细信息(方便找节点属性名字)

首先选中节点,然后按键盘的i键即可弹出一个窗口,在里面可以找到节点的详细信息.


后台执行脚本

首先通过nuke.env[‘ExecutablePath’]得到nuke程序的路径
然后如果是当前python脚本想要调用令一个python脚本的话,就通过’{}/另一个python脚本的名字.py’.format(os.path.dirname(file))找到要调用的另一个python脚本的路径,或者就直接用绝对路径.
然后创建command命令:command = ‘“{nuke}” -t -x {script} {要传入的额外参数}’
传入的额外参数可以在要调用的python脚本中通过sys.argv[1],sys.argv[2]… 来得到
最后通过subprocess.Popen(command, shell=True)来使用命令行
后台执行脚本的要点就是命令里记得添加-t和-x即可,不添加就是前台调用了


官方入门文档

官方入门文档地址
从这个文档可以快速的了解到如何通过代码创建节点,设置节点属性,窗口的制作等.可以直接看,代码的英文直译很容易就能明白对应的意思.


常用的命令

通过节点类型得到对应节点: nuke.allNodes(‘Read’)
得到选择的节点并设置节点的属性:
select_node = nuke.selectedNode()
select_node[‘file’].setValue()
nuke消息窗口的显示: nuke.message(“消息窗口内容”)
得到当前工程名字:nuke.root().name()
打开项目:nuke.scriptOpen()
得到nuke程序的路径: nuke.env[‘ExecutablePath’]
导入其他nuke文件: nuke.nodePaste()
保存nuke的文件:nuke.scriptSaveAs(prjPath)
保存当前nuke工程:nuke.scriptSave(“”)
清理当前nuke工程:nuke.scriptClear()
得到nuke程序的路径:nuke.env[‘ExecutablePath’]

第一节

初始设置

在这个路径下C:\Users\用户名.nuke,创建gizmos文件夹,python文件夹,init.py文件,menu.py文件。
init.py文件用来为nuke新增插件识别路径(这样就不需要每个文件夹都加一个__init__.py文件了),都统一加到外面这个init.py文件。
menu.py文件用来控制nuke启动时自动加载的功能
其中init.py文件中内容是

1
2
nuke.pluginAddPath('./gizmos')
nuke.pluginAddPath('./python')

克服因操作平台的不同而导致的.nuke文件夹路径不同的问题

在menu.py文件中输入:
其中platform可以用来得到当前的操作平台
image.png

第二节

image.png

设置创建节点时的默认值

nuke.knobDefault(‘Tracker4.shutteroffset’,“centered”) # 设置Tracker节点的shutteroffset的默认值为centered
nuke.knobDefault(‘Tracker4.label’, “Motion: [value transform]\nRef Frame: [value reference_frame]”) # 设置Tracker节点的label(节点的显示文本)为Motion: [value transform]\nRef Frame: [value reference_frame]也就是image.png。加入后的前后对比:image.png变成了image.png。其中label中框号中的内容是属性值。
image.png 在创建节点时当节点类型为Tracker时设置这个节点的reference_frame的值为nuke的时间滑块的frame值。

自定义menu和gizmosmenu

image.png
如何添加自定义菜单:

image.png

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# coding:utf-8
import PythonEditor
PythonEditor.nuke_menu_setup(nuke_menu=True, node_menu=True, pane_menu=True)
import nuke
import platform
import nukescripts

# ----------------------------------------------------------------------------------------------------------------------
# 设置节点的默认设置
# ----------------------------------------------------------------------------------------------------------------------

nuke.knobDefault('Tracker4.shutteroffset', "centered")
nuke.knobDefault('Tracker4.label', "Motion: [value transform]\nRef Frame: [value reference_frame]")
nuke.addOnUserCreate(lambda: nuke.thisNode()['reference_frame'].setValue(nuke.frame()), nodeClass='Tracker4')


# ----------------------------------------------------------------------------------------------------------------------
# 自定义菜单
# ----------------------------------------------------------------------------------------------------------------------

utilitiesMenu = nuke.menu('Nuke').addMenu('Utilities')

utilitiesMenu.addCommand('Autocrop', 'nukescripts.autocrop()')

myGizmosMenu = nuke.menu('Nodes'.addMenu('myGizmos')

myGizmosMenu.addCommand('Autocrop', 'nukescripts.autocrop()')


# ----------------------------------------------------------------------------------------------------------------------
# 自定义快捷键
# ----------------------------------------------------------------------------------------------------------------------

nuke.menu('Nodes').addCommand("Transform/Tracker", "nuke.createNode('Tracker4)","ctrl+alt+t", icon="Tracker.png", shortcutContext=2')

nuke自带的图标路径

image.png可以在图中的路径处找到nuke自带的图标的名字
然后添加menu时icon参数如果想要是nuke自带的图标那么就可以直接填图标的名字加后缀名,nuke会自动找到
image.png

第三节

image.png

创建节点

nuke.createNode()

创建节点的同时设置属性值(不属于课程,之前自己搜的)

举例:nuke.nodes.Shuffle(inputs=[texO], red=“red”, green=“black”, blue=“black”, alpha=“white”)

为节点创建预设与快捷键

image.png

更改节点的属性值

举例,设置选择的节点的’bbox’属性值为’B’:nuke.selectedNode()[‘bbox’].setValue(“B”)
举例,自定义某一节点的属性值: nuke.toNode(‘Merge1’)[‘bbox’].setValue(“B”)

通过for循环批量更改某一类型的节点属性值

设置所有merge2类型节点的属性值:image.png

通过代码得到节点的类型名

print nuke.selectedNode().Class()

第四节

image.png

介绍

image.png
针对shuffle节点制作一些功能

shuffleShortcuts.py文件

在.nuke\python\shuffleShortcuts文件夹下创建个shuffleShortcuts.py文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import nuke


def createCustomShuffle(in_channel, out_channel, set_channel, rColor, gColor, bColor):
my_shuffle = nuke.createNode("Shuffle")

my_shuffle['in'].setValue(in_channel)
my_shuffle['out'].setValue(out_channel)

my_shuffle['red'].setValue(set_channel)
my_shuffle['green'].setValue(set_channel)
my_shuffle['blue'].setValue(set_channel)
my_shuffle['alpha'].setValue(set_channel)

my_shuffle['tile_color'].setValue(int('%02x%02x%02x%02x' % (rColor * 255, gColor * 255, bColor * 255, 1), 16))

my_shuffle['label'].setValue("[value red] > [value out]")


def shuffleRGBchannels():
select_node = nuke.selectedNode()

select_node_x_pos = select_node['xpos'].value()
select_node_y_pos = select_node['ypos'].value()

createCustomShuffle('rgba', 'rgba', 'red', 1, 0, 0)
red_shuffle = nuke.selectedNode()
createCustomShuffle('rgba', 'rgba', 'green', 0, 1, 0)
green_shuffle = nuke.selectedNode()
createCustomShuffle('rgba', 'rgba', 'blue', 0, 0, 1)
blue_shuffle = nuke.selectedNode()

red_shuffle.setInput(0, select_node)
red_shuffle['xpos'].setValue(select_node_x_pos - 150)
red_shuffle['ypos'].setValue(select_node_y_pos + 150)

green_shuffle.setInput(0, select_node)
green_shuffle['xpos'].setValue(select_node_x_pos)
green_shuffle['ypos'].setValue(select_node_y_pos + 150)

blue_shuffle.setInput(0, select_node)
blue_shuffle['xpos'].setValue(select_node_x_pos + 150)
blue_shuffle['ypos'].setValue(select_node_y_pos + 150)


nuke.menu('Nodes').addCommand("Channel/Shuffle (Red to All)",
"shuffleShortcuts.createCustomShuffle('rgba', 'rgba', 'red', 1, 0, 0)",
"ctrl+shift+r", shortcutContext=2)
nuke.menu('Nodes').addCommand("Channel/Shuffle (Green to All)",
"shuffleShortcuts.createCustomShuffle('rgba', 'rgba', 'green', 0, 1, 0)",
"ctrl+shift+g", shortcutContext=2)
nuke.menu('Nodes').addCommand("Channel/Shuffle (Blue to All)",
"shuffleShortcuts.createCustomShuffle('rgba', 'rgba', 'red', 0, 0, 1)",
"ctrl+shift+b", shortcutContext=2)
nuke.menu('Nodes').addCommand("Channel/Shuffle (Alpha to All)",
"shuffleShortcuts.createCustomShuffle('rgba', 'rgba', 'red', 1, 1, 1)",
"ctrl+shift+a", shortcutContext=2)
nuke.menu('Nodes').addCommand("Channel/Shuffle (Alpha to 0)",
"shuffleShortcuts.createCustomShuffle('rgba', 'rgba', 'red', 0, 0, 0)",
shortcutContext=2)
nuke.menu('Nodes').addCommand("Channel/Shuffle (Alpha to 1)",
"shuffleShortcuts.createCustomShuffle('rgba', 'rgba', 'red', 1, 1, 1)",
shortcutContext=2)
nuke.menu('Nodes').addCommand("Channel/Shuffle (Split RGB channels)",
"shuffleShortcuts.shuffleRGBchannels()",
"ctrl+shift+s", shortcutContext=2)

然后在menu.py文件中导入这个模块
image.png

init.py

init.py文件中定义文件夹路径
image.png

第五节

image.pngimage.png

得到项目路径

nuke.root()[‘name’].value()


定位字符串的特定值

举例字符串: Checkerboard_Small_v0002.png 输出 Checkerboard_Smal

1
2
3
name_ext = "Checkerboard_Small_v0002.png"
name = name_ext[0:name_ext.find('_v')]
print name # Checkerboard_Small

第六节

image.pngimage.png

弹出输入框让用户输入

image.png
inputBox = nuke.getInput(“My First Window”, “default text”)
如果点击Cancel按钮,那么inputBox的值为None

在nuke菜单下放置一个让用户输入所选节点label的工具

首先按照课程的文件夹排列,我们的流程就是,在.nuke\python\shuffleShortcuts文件夹下创建一个新的.py工具文件(主要是因为init.py文件定义了这个文件夹为插件加载路径)
然后去menu.py文件中导入这个新的.py工具文件,这样nuke就能够调用.py文件了。
image.png

shortcut_NodeComment.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import nuke


def shortcut_NodeComment():
selected_node = nuke.selectedNode()

old_comment = selected_node['label'].value()

input_box = nuke.getInput("Please enter a node label", old_comment)

if not input_box:
nuke.message("Node label will remain as " + old_comment)
else:
selected_node['label'].setValue(input_box)


nuke.menu('Nuke').addCommand('Edit/Shortcuts/Add Comment to Node', 'shortcut_NodeComment.shortcut_NodeComment()',
'ctrl+alt+c', shortcutContext=2)

1
import shortcut_NodeComment

init.py

1
nuke.pluginAddPath('./python/shuffleShortcuts')

(扩展版)在nuke菜单下放置一个让用户输入所选节点label的工具

image.png
不仅可以设置内容,也可以设置显示knob属性,也可以设置节点颜色
内嵌的panel写法举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import nukescripts.panels
class my_panel(nukescripts.panels.PythonPanel):
def __init__(self):
super(my_panel, self).__init__('my_panel')
selected_Node = nuke.selectedNode()
old_comment = selected_Node['label'].value()
knob_list = []

for i in selected_Node.knobs():
knob_list.append(i)

self.old_comment_slt = nuke.String_Knob("Comment", "Comment", old_comment)
self.addKnob(self.old_comment_slt)
self.knob_list = nuke.Enumeration_Knob("Knob","Knob", knob_list)
self.addKnob(self.knob_list)
self.colour_bool = nuke.Boolean_Knob("Change Node Colour?", "Change Node Colour?", False)
self.addKnob(self.colour_bool)


p = my_panel()
p.show()

教程中的panel写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import nuke


def short_NodeCustomizer():
selected_Node = nuke.selectedNode()
old_comment = selected_Node['label'].value()
knob_list = []

for i in selected_Node.knobs():
knob_list.append(i)

knob_list.sort()
knob_list.insert(0, 'None')

knob_list_string = " ".join(knob_list)

panel = nuke.Panel("Node Customizer")

panel.addSingleLineInput("Comment", old_comment)
panel.addEnumerationPulldown("Knob", knob_list_string)
panel.addBooleanCheckBox("Change Node Colour?", False)

if not panel.show():
return

comment_input = panel.value("Comment")
knob_choice = panel.value("Knob")
node_label = comment_input + "\n" + knob_choice + ": [value " + knob_choice + "]"

if comment_input == "" and panel.value("Knob") == "None" and not panel.value("Change Node Colour?"):
nuke.message("Please enter a node label")
return
elif knob_choice == "None":
selected_Node['label'].setValue(comment_input)
elif comment_input == "":
selected_Node['label'].setValue(knob_choice + ": [value " + knob_choice + "]")
else:
selected_Node['label'].setValue(node_label)

if panel.value("Change Node Colour?"):
selected_Node['tile_color'].setValue(nuke.getColor())
else:
return

nuke.menu('Nuke').addCommand('Utilities/Node Customizer', 'shortcut_NodeCustomizer.shortcut_NodeCustomizer()')

第八节

TCL的使用,链接属性

moblur_controller.py

image.png
创建NoOp节点配合TCL表达式,使只通过控制NoOp节点的数值,即可控制所有nuke节点网络图中带有对应属性的属性值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# coding:utf-8
import nuke


def moblur_controller():
node_list = []

for n in nuke.allNodes():
if n.knob('motionblur') or n.knob('samples'):
node_list.append(n)

NoOp = nuke.createNode('NoOp') # 使用TCL表达式需要的节点
NoOp['name'].setValue("GLOBAL_MOTIONBLUR_CONTROLLER")

NoOp['tile_color'].setValue(255)
NoOp['note_font'].setValue("Bold")

NoOp.addKnob(nuke.Int_Knob('global_motionblur', "motionblur"))
NoOp.addKnob(nuke.Double_Knob('global_shutter', "shutter"))
NoOp.addKnob(nuke.Boolean_Knob('global_disable_moblur', "disable motionblur"))

NoOp['global_motionblur'].setValue(1)
NoOp['global_shutter'].setValue(0.5)

NoOp['global_disable_moblur'].setFlag(nuke.STARTLINE) # 设置将这个控件另起一行放置

for node in node_list:
if node.knob('motionblur'):
node['motionblur'].setExpression(
'GLOBAL_MOTIONBLUR_CONTROLLER.global_disable_moblur == 0 ? GLOBAL_MOTIONBLUR_CONTROLLER.global_motionblur : 0')

node['shutter'].setExpression('GLOBAL_MOTIONBLUR_CONTROLLER.globsl_shutter')

elif node.knob('samples'):
node['samples'].setExpression(
'GLOBAL_MOTIONBLUR_CONTROLLER.global_disable_moblur == 0 ? GLOBAL_MOTIONBLUR_CONTROLLER.global_motionblur : 1')
node['shutter'].setExpression('GLOBAL_MOTIONBLUR_CONTROLLER.globsl_shutter')

# 当节点删除时执行这个函数功能,删除所有表达式链接,并将节点值设置回默认值
def deleteExpressions():

for node in node_list:
if node.knob('motionblur'):
node['motionblur'].clearAnimated()
node['motionblur'].setValue(0)
node['shutter'].clearAnimated()
node['shutter'].setValue(0.5)

elif node.knob('samples'):
node['samples'].clearAnimated()
node['samples'].setValue(1)
node['shutter'].clearAnimated()
node['shutter'].setValue(0.5)

nuke.addOnDestroy(deleteExpressions) # 当节点删除时执行


nuke.menu('Nuke').addCommand('Utilities/Global Motionblur Controller', 'moblur_controller.moblur_controller()')

第九节

使用nuke自带的自定义界面工具来扩展节点

在节点的属性界面右键点击image.png
然后就可以在这里自定义界面
image.png
其中divider line是分割线
然后UI可以附带代码

addNodes

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
node_list = []
for node in nuke.selectedNodes():
node_list.append(node.name())

nuke.thisNode().knob('addMoreNodes').setVisible(True)
nuke.thisNode().knob('addNodes').setVisible(False)

node_list_cleaned = '\n·'.join(node_list)

nuke.thisNode()['txtknob_node_list'].setValue("·"+node_list_cleaned)

def disableNodesInList():
for i in node_list:
if nuke.toNode(i).knob('disable'):
nuke.toNode(i).knob('disable').setValue(nuke.thisNode().knob('disable').value())
else:
print "-" + i + "does not have a 'disable' knob Ignoring..."
nuke.toNode("NODE_DISABLER").knob("knobChanged").setValue('disableNodesInList()')

addMoreNodes

1
2
3
4
5
6
7
8
9
10
for node in nuke.selectedNodes():
if node.name() in node_list:
print node.name()+" is already in the list"
else:
node_list.append(node.name())

node_list_cleaned = '\n·'.join(node_list)

nuke.thisNode()['txtknob_node_list'].setValue("·"+node_list_cleaned)

clearList

1
2
3
4
5
6
node_list = []

nuke.thisNode().knob('addNodes').setVisible(True)
nuke.thisNode().knob('addMoreNodes').setVisible(False)

nuke.thisNode()['txtknob_node_list'].setValue("None")

将通过这种方法自定义的节点保存成gizmo

在.nuke\gizmos文件夹中新建gizmo文件
image.png
然后在nuke中选择节点按ctrl+c
然后进入gizmo文件按ctrl+v即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
set cut_paste_input [stack 0]
version 10.5 v4
push $cut_paste_input
NoOp {
name NODE_DISABLER
knobChanged disableNodesInList()
tile_color 0xff
label "\[expr \{ \[value disable] == true ? \"Nodes Disabled\" : \"Nodes Enabled\" \}]"
selected true
xpos -180
ypos -86
addUserKnob {20 User}
addUserKnob {26 ""}
addUserKnob {22 addNodes l "Add Selected Nodes To List" T "node_list = \[]\nfor node in nuke.selectedNodes():\n node_list.append(node.name())\n\nnuke.thisNode().knob('addMoreNodes').setVisible(True)\nnuke.thisNode().knob('addNodes').setVisible(False)\n\nnode_list_cleaned = '\\n·'.join(node_list)\n\nnuke.thisNode()\['txtknob_node_list'].setValue(\"·\"+node_list_cleaned)\n\ndef disableNodesInList():\n for i in node_list:\n if nuke.toNode(i).knob('disable'):\n nuke.toNode(i).knob('disable').setValue(nuke.thisNode().knob('disable').value())\n else:\n print \"-\" + i + \"does not have a 'disable' knob Ignoring...\"\nnuke.toNode(\"NODE_DISABLER\").knob(\"knobChanged\").setValue('disableNodesInList()')" +STARTLINE}
addUserKnob {22 addMoreNodes l "Add More Selected Nodes To List" +HIDDEN T "for node in nuke.selectedNodes():\n if node.name() in node_list:\n print node.name()+\" is already in the list\"\n else:\n node_list.append(node.name())\n\nnode_list_cleaned = '\\n·'.join(node_list)\n\nnuke.thisNode()\['txtknob_node_list'].setValue(\"·\"+node_list_cleaned)\n" +STARTLINE}
addUserKnob {26 ""}
addUserKnob {22 clearList l "Clear List" T "node_list = \[]\n\nnuke.thisNode().knob('addNodes').setVisible(True)\nnuke.thisNode().knob('addMoreNodes').setVisible(False)\n\nnuke.thisNode()\['txtknob_node_list'].setValue(\"None\")" +STARTLINE}
addUserKnob {26 spacer l " " -STARTLINE T " "}
addUserKnob {6 disable -STARTLINE}
addUserKnob {26 ""}
addUserKnob {26 txtknob_node_list l "NODE LIST:" T None}
addUserKnob {26 ""}
}