Foundry’s Nuke is a premium compositing software with a rich back-end and studio level implementations that can be manipulated and enhanced using python and TCL. This is a compilation of snippets and scripts I’ve used one time or another over the past years to accomplish specific tasks, and not all of it has been recently tested.
Note that a lot of these were written before Nuke had a lot of tools that it has today. Therefore use the code below carefully, and if you find any issues, something not working – it could just be a new version of Nuke or there just may be a much better way to do it nowadays. And remember – with great power comes great responsibility.
TCL & EXPRESSIONS
Folder where the script is saved: [file root]
Root dir: [file dirname [knob [topnode].file]]
File name: [file tail [knob [topnode].file]]
File extension: [file extension [knob [topnode].file]]
Text node shot name and frame # TCL expression:
[lrange [split [file tail [knob [topnode].file]] _ ] 0 0 ] :: frame [frame]
Convert one format to another, and save it somewhere relative to the path:
[file dirname [knob [topnode].file]]/../../jpg/3200×1800/[lrange [split [file tail [knob [topnode].file]] . ] 0 0].%04d.jpg
Autowrite based on where your script is:
Providing you prefer your structure to look like this:
[project name]/[sequences]/[shot]/nuke/scripts
[project name]/[sequences]/[shot]/nuke/renders/exr
[project name]/[sequences]/[shot]/nuke/renders/mov
…
step 1: go to your settings and click the Script Directory button to set global relative path to it
step 2: put this in your render node for EXRs. Make sure you check the box to create directories on render. I’m using four numbers padding, feel free to change that to what you need:
../renders/[file rootname [file tail [value root.name] ] ]/exr/[file rootname [file tail [value root.name] ] ].%04d.exr
Random Expression:
main idea: min+random(frame)*(max-min)
example (random value between 100 & 300): rint(100+random(frame)*(300-100))
Setting up write nodes for specific frame range – different approaches:
Example 1: on the write node add expression: !inrange(frame, first_frame, last_frame)
Example 2: on the disable node add expression: ((frame < Read1.first)?true:false)||((frame > Read1.last)?true:false)
Using the Expression node to clamp specific values or generate mattes:
Clamping Values: On each color channel of the expression node: Color <High Value? Color:New Value Example: r<1.1?r:.9
Creating Mattes: On each color channel: Color >High Value ? Color :0 Example: r>1.1?r:0
Place an additional expression node below the first expression and under the alpha add r+g+b to consolidate the result into one matte
How to write tcl code inside a python script:
nuke.tcl(‘tcl expression here’)
For example:
nuke.tcl(‘file dirname [knob root.name]’)
Expression to generate an HD resolution stmap with a single expression node:
expression 1 (turn off green and blue): (x%1920)/1920
expression 2 (turn off red and blue): (y%1080)/1080
PYTHON SCRIPTS INDEX
- Check write nodes and create folders if none found
- Attach a Furnace DeNoise node to selected nodes
- Attach a Write node to selected Read nodes, and embed with expression on disable
- Change single or muliple parameters in all selected nodes
- Create Write nodes connected with all selected read nodes with correct file name
- Disable “Postage Stamps” on all nodes
- Unhide all nodes’ inputs (if received a script that hides them)
- Change the first frame to a certain number – enhanced version should change the last frame to that number + frame range
- Print a selected nodes’ methods
- Find all the Timeoffset nodes in a group (called group2) and change value of offset based on its position in an array of found time offsets
- Remove all animation from selected nodes
- Add keyframes – Animate mix parameter
- Halve the color value of all constant nodes in a script
- Find all the transform Nodes in the script and if their input is a crop set scale value X2
- Set all gain values of CC nodes X2
- Change font size of all Write nodes in the script
- Create 20 constants with incrementing color values
- Set up write nodes for specific frame range
- Use Python to define your own hotkeys
- Select all class nodes (class Read in this case)
- Write DPX, create Slate and WriteJPG generator – written for Method Pipeline
- Camera with aim TCL
- Add a text node with the value from the input node
- Multiple Read/Write nodes tweaker
- Import mocha track and create a normal and reversed corner pin out of it
- Control / smooth curves with a multiplier
- Open all property bins of all selected nodes
- Write all the read nodes file name in the script editor
- Add a text node with the value from the input node
1. Check write nodes and create folders if none found
# script that checks if write nodes have folders # and creates them if not import os import sys import os.path from os import system from os import makedirs import nuke this_filename = sys._getframe().f_code.co_filename print('Loading '+this_filename) def write_mkdir(nodes = ''): 'Make directories from write nodes' n = nuke.allNodes() for i in n: if i.Class() == "Write": i.knob("selected").setValue(True) snodes = nuke.selectedNodes() if len(snodes) == 0: nuke.message('ERROR: No Write nodes to work on') return for i in snodes: _class = i.Class() if _class == "Write": # use this to get only the filename path = nuke.filename(i) if empty continue if path is None: continue # Get Directory Structure with out file name dirPath = os.path.dirname(path) if os.path.isdir (dirPath): nuke.message('Directory already exists:\n%s' % (dirPath)) continue if os.path.isfile (dirPath): # The folder exists as a file msg = "The directory:\n%s\nexists as a file. Delete it and create folder?" % (dirPath) delete_existing_file = nuke.ask(msg) if (delete_existing_file): os.unlink (dirPath) else: return # Create directory try: makedirs(dirPath,0775) nuke.message('Created:\n%s' % (dirPath)) except: if nuke.ask('Create Path: '+ dirPath): makedirs(dirPath,0775) continue else: return else: nuke.message('ERROR: Skipping non-Write node.') continue return
2. Attach a Furnace DeNoise node to selected nodes
# script that attaches a Furnace DeNoise node # with default values to selected nodes, # and then adds a write node to that Furnace DeNoise node sn = nuke.selectedNodes() for n in sn: first = n.firstFrame() last = n.lastFrame() a = n['file'].value() b = n['name'].value() c = a.replace('.%04d.dpx','_denoise.%04d.exr') d = "DeNoise_"+b dn=nuke.nodes.F_DeNoise (name=d, plateSize="Pal Or NTSC") dn.setInput(0,n) nuke.toNode(d).knob('selected').setValue(True) wr = nuke.nodes.Write (name="Write_EXR",file=c, colorspace="linear") wr['disable'].setExpression("((frame < "+b+".first)?true:false)||((frame >"+b+".last)?true:false)") wr.setInput(0,dn)
3. Attach a Write node to selected Read nodes, and embed with expression on disable
# script that attaches a Write node with default values to selected Read nodes # and sticks an expression that reads first and last frames from the read notes sn = nuke.selectedNodes() for n in sn: first = n.firstFrame() last = n.lastFrame() a = n['file'].value() b = n['name'].value() # c = a.replace('.%04d.sgi','%04d.'+fmt) d = "DeNoise_"+b wr = nuke.nodes.Write(name="WriteFromRead",file=c, colorspace="sRGB") wr['disable'].setExpression("((frame < "+b+".first)?true:false)||((frame >" +b+".last)?true:false)") wr.setInput(0,n)
4. Change single or multiple parameters in all selected nodes
# SCRIPT THAT CHANGES SINGLE OR MULTIPLE PARAMETERS IN ALL SELECTED NODES sn = nuke.selectedNodes() for n in sn: n.knob('channels').setValue("rgba") n.knob('colorspace').setValue("sRGB") #script that adds a text node with the value from the input node. sn = nuke.selectedNodes() for i in sn: txt = nuke.nodes.Text (font ="/usr/share/fonts/bitstream-vera/Vera.ttf", message = "[file tail [knob input0.file]]", size = "35") txt.knob[transform.box].setValue(0,0,1920,1080) txt.setInput(0,i)
5. Create Write nodes connected with all selected read nodes with correct file name
# MAKE WRITE NODES CONNECTED WITH READ NODES WITH THE CORRECT FILE NAME sn = nuke.selectedNodes() for n in sn: a = n.name() f = n['name'].value() wsgi=nuke.nodes.Write(name='SGI_Write', file=f, colorspace='default') wsgi['file_type'].setValue("sgi") wsgi['channels'].setValue("rgba") wsgi.setInput(0,n)
6. Disable “Postage Stamps” on all nodes
# DISABLE "POSTAGE STAMPS" ON ALL NODES for a in nuke.allNodes(): try: a['postage_stamp'].setValue(0) except: pass
7. Unhide all nodes’ inputs (if received a script that hides them)
# "UNHIDE" ALL NODES' INPUTS - USEFUL WHEN RECEIVING A SNEAKY COMP/LIGHTING SCRIPT for a in nuke.allNodes(): try: a['hide_input'].setValue(0) except: pass
8. Change the first frame to a certain number – enhanced version should change the last frame to that number + frame range
# CHANGE THE "FIRST" FRAME OF ALL SELECTED NODES THAT ARE READ NODES # (EXAMPLE CHANGES THE FIRST FRAME TO 1001) for a in nuke.selectedNodes(): if a.Class() == 'Read': a['first'].setValue(1001)
9. Print a selected nodes’ methods
# PRINT A SELECTED NODES' METHODS import struct node = nuke.selectedNode() for a in node['lookup'].animations(): print dir(a) print inputs (dependencies) of a selected node: for a in nuke.selectedNode().dependencies(): print a.name() print outputs (dependents) of a selected node: for a in nuke.selectedNode().dependent(): print a.name()
10. Find all the Timeoffset nodes in a group (called group2) and change value of offset based on its position in an array of found time offsets
# FIND ALL THE TimeOffset NODES IN A GROUP CALLED "Group2", AND CHANGE THE VALUE # OF EACH OFFSET BASED ON ITS POSITION IN THE ARRAY OF FOUND TIME OFFSETS tos = [] for a in nuke.toNode('Group2').nodes(): if a.Class()=='TimeOffset': tos.append(a) for b in tos: b['time_offset'].setValue(tos.index(b)) set the ‘bbox’ for any selected Merge, Keymix & Copy nodes to “B” for a in nuke.selectedNodes(): classTypes = ['Merge' , 'Keymix', 'Copy', ] for n in classTypes: if n in a.Class(): for p in a['bbox'].values(): if 'B' in p: a['bbox'].setValue(a['bbox'].values().index(p))
11. Remove all animation from selected nodes
# REMOVE ALL ANIMATION FROM SELECTED NODES for a in nuke.selectedNodes(): for b in a.knobs(): a[b].clearAnimated()
12. Add keyframes – Animate mix parameter
# ADD KEYFRAMES - ANIMATE A MIX for a in nuke.selectedNodes(): a['mix'].setAnimated() a['mix'].setValueAt(1,nuke.frame()) a['mix'].setValueAt(0,(nuke.frame() - 1))
13. Halve the color value of all constant nodes in a script
# HALVE THE COLOR VALUE OF ALL THE CONSTANT NODES IN A SCRIPT for a in nuke.allNodes(): if a.Class() == "Constant": a['color'].setValue(a['color'].value()[0] / 2 , 0) a['color'].setValue(a['color'].value()[1] / 2 , 1) a['color'].setValue(a['color'].value()[2] / 2 , 2)
14. Find all the transform Nodes in the script and if their input is a crop set scale value X2
# FIND ALL THE TRANSFORM NODES IN A SCRIPT, AND IF THEIR INPUT IS A CROP SET THE SCALE # VALUE TO BE TWICE ITS CURRENT VALUE (ALSO CHECKS IF THE SCALE IS A LIST ARRAY OR A FLOAT) for a in nuke.allNodes(): if a.Class() == "Transform": if a.input(0).Class() == "Crop": x = a['scale'].value() if type(x).__name__ == 'list': a['scale'].setValue(x[0] * 2 , 0) a['scale'].setValue(x[1] * 2 , 1) if type(x).__name__ == 'float': a['scale'].setValue(x*2)
15. Set all gain values of CC nodes X2
# SET ALL THE GAIN VALUES OF ALL SELECTED COLOR CORRECT NODES TO TWICE THEIR CURRENT for a in nuke.allNodes(): if a.Class() == "ColorCorrect": a['gain'].setValue(a['gain'].value() * 2) print files with ‘mov’ in filename for a in nuke.allNodes(): if 'Read' in a['name'].value(): if 'mov' in a['file'].value(): print a['file'].value()
16. Change font size of all Write nodes in the script
# CHANGE FONT SIZE OF ALL WRITE NODES IN THE SCRIPT for a in nuke.selectedNodes(): if "Write" in a['name'].value(): a['note_font_size'].setValue(60)
17. Create 20 constants with incrementing color values
# CREATE 20 CONSTANTS WITH INCREMENTING COLOR VALUES def makeConstants(amount): for i in range(amount): a= nuke.nodes.Constant() color= float( float(i) / float(amount) ) a['color'].setValue(color)
18. Set up write nodes for specific frame range
# SET UP WRITE NODES FOR SPECIFIC FRAME RANGE sn = nuke.selectedNodes() for n in sn: first = n.firstFrame() last = n.lastFrame() nuke.render( n.name(), first, last, 1 )
19. Use Python to define your own hotkeys
#DEFINING YOUR OWN HOTKEYS def _autoplace(): n = nuke.selectedNodes() for i in n: nuke.autoplace(i) t=nuke.toolbar("Extras") t.addCommand("Auto&place", "_autoplace()", "Alt+a")
20. Select all class nodes (class Read in this case)
#SELECTING ALL CLASS NODES (READ NODES) n = nuke.allNodes() for s in n: if s.Class() == "Read": s.knob("selected").setValue(True)
21. Write DPX, create Slate and WriteJPG generator – written for Method Pipeline
#WRITE DPX, SLATE AND WRITE JPG # writeDPX, slate and writeJPG generator v0.1 alpha # 1. delete the old write_DPX, slate and write_Latest nodes # 2. click on the last node in your comp (select it) # 3. execute script a="[file dirname [value root.name]]/../../images/comp/[file rootname [file tail [value root.name]]]/[file rootname [file tail [value root.name]]].%04d.dpx" b="[file rootname [file tail [value root.name]]].[frame].dpx" c="[file dirname [value root.name]]/../../images/comp/latest/[lindex [split [value root.name] /] 4]_comp_latest.%04d.jpg" wdpx=nuke.nodes.Write(name="Write_DPX", file=a, colorspace="rec709") wdpx['file_type'].setValue("dpx") wdpx.setInput(0,nuke.selectedNode()) nuke.toNode('Write_DPX').knob('selected').setValue(True) wslate = nuke.nodes.Text (name="Slate", font="/usr/share/fonts/bitstream-vera/Vera.ttf", yjustify = "center") wslate['box'].setValue([0,0,2048,1168]) wslate['translate'].setValue([50, -550]) wslate['size'].setValue(25) wslate['message'].setValue(b) wslate.setInput(0,nuke.selectedNode()) nuke.toNode('Slate').knob('selected').setValue(True) wjpg=nuke.nodes.Write (name="Write_Latest", file=c, colorspace="rec709") wjpg['file_type'].setValue("jpg") wjpg['_jpeg_quality'].setValue([1]) wjpg.setInput(0,nuke.selectedNode())
22. Camera with aim
# camera with aim for Nuke v0.1 by Aleksandar Djordjevic n = nuke.nodes.Camera2() p = 'set lookAt [value lookObject]\n puts $lookAt\n set xX "degrees(atan2($lookAt.translate.y-translate.y,sqrt(pow($lookAt.translate.x-translate.x,2)+pow($lookAt.translate.z-translate.z,2))))"\n set yX "$lookAt.translate.z-this.translate.z >= 0 ? 180+degrees(atan2($lookAt.translate.x-translate.x,$lookAt.translate.z-translate.z)):180+degrees(atan2($lookAt.translate.x-translate.x,$lookAt.translate.z-translate.z))"\n in this.rotate.x {set_expression $xX}\n in this.rotate.y {set_expression $yX}\n' tab = nuke.Tab_Knob("Look","Camera Aim") n.addKnob(tab) k = nuke.Script_Knob("knob", "look at") n.addKnob(k) n.knob("knob").setValue(p) k.setTooltip('Press this button after you type in the aim object\'s name') m = nuke.String_Knob("lookObject", "") n.addKnob(m).po m.setTooltip('Type your aim object node name here')
23. Add a text node with the value from the input node
#script that adds a text node with the value from the input node. sn = nuke.selectedNodes() for i in sn: txt = nuke.nodes.Text (font ="/usr/share/fonts/bitstream-vera/Vera.ttf", message = "[file tail [knob input0.file]]", size = "35") txt.knob[transform.box].setValue(0,0,1920,1080) txt.setInput(0,i)
24. Multiple Read/Write node tweaker
# multi node tweaker v0.1 by Aleksandar Djordjevic # # this script creates a panel that enables the user to manipulate # several knobs inside all selected read and write nodes # you can select all nodes but it is going to change values only on reads and writes\ import nuke import os def multiNodeTweaker(): test = 0 origFileName = None replaceInFileName = None booleanCheckBox = None chanVal = 'rgb rgba alpha depth' cspace = 'default linear sRGB rec709 Cineon Gamma1.8 Gamma2.2 Panalog REDlog ViperLog REDSpace' sn = nuke.selectedNodes() # first checkpoint - is anything selected? if (len(sn) == 0): nuke.message("Select one or more Read or Write nodes") return # second checkpoint - I will work only on valid node classes for i in sn: if i.Class() != 'Read' or 'Write': nuke.message("No Read or Write nodes selected.") return o = nuke.Panel("Multi Node Tweaker") o.addSingleLineInput('Find:', origFileName) o.addSingleLineInput('Replace:', replaceInFileName) o.addEnumerationPulldown('Color Space',cspace) o.addButton("Cancel") o.addButton("Ok") # If selected nodes are of Write class, add parameter to mess with the channels for i in sn: if i.Class() == 'Write': test = 1 if test == 1: o.addEnumerationPulldown('Channels:',chanVal) o.show() # grab new values origFileName = o.value("Find:") replaceInFileName = o.value("Replace:") cspace = o.value("Color Space") chanVal = o.value("Channels:") for n in sn: filename = n['file'].value() newFileName = filename.replace(origFileName,replaceInFileName) n.knob('file').setValue(newFileName) n.knob('colorspace').setValue(cspace) if n.Class() == 'Write': n.knob('channels').setValue(chanVal)
25. Import mocha track and create a normal and reversed corner pin out of it
import nuke, os def importMocha(): filename = nuke.getFilename("Mocha tracking data", "*.txt") f = open(filename) row = -1 active = False data = [] height = 0 for l in f.readlines(): items = l.split() if len(items) < 1: active = False if l.lower().lstrip().startswith("source height"): height = float(items[2]) if active: data[row].append(items) if l.lower().lstrip().startswith("frame"): row += 1 active = True data.append([]) cornerPinNode = nuke.createNode("CornerPin2D") cornerPinReverseNode = nuke.createNode("CornerPin2D") points = ["1", "2", "4", "3"] for c in range(4): #cornerPinNode.knob("to" + str(c + 1)).setAnimated(True) toKnob = cornerPinNode.knob("to" + points[c]) fromKnob = cornerPinReverseNode.knob("from" + points[c]) for k in (toKnob, fromKnob): k.setAnimated(0, True) k.setAnimated(1, True) for (f, x, y) in data[c]: k.setValueAt(float(x), float(f), 0) k.setValueAt(height - float(y), float(f), 1)
26. Control / smooth curves with a multiplier
Create a node, let's say a camera node. Create a user tab on it, and make a floating point slider called multiplier and labeled multiplier. Then, in the expression of your x,y,z for translate, rotate, scale, whatever you want to smooth and control punch in this expression: curve-(curve * multiplier) For every frame, it will subtract that frame's value with your multiplier, and if it's 0, it's your original value, if it's not, it changes and smooths the curve by a [multiplier] factor.
27. Open all property bins of all selected nodes
# open all property bins of all selected nodes # set the max panels number to the amount of nodes you selected import nuke, os def selnode(): sn = nuke.selectedNodes() maxPanels = nuke.toNode('preferences')['maxPanels'] panelCount = 0 for i in sn: panelCount = panelCount+1 if panelCount > maxPanels.value(): maxPanels.setValue(panelCount) for n in sn: nuke.show(n)
28. Write all the read nodes file name in the script editor
# write all the read nodes file names in the script editor for a in nuke.allNodes(): if 'Read' in a['name'].value(): print a['file'].vaue()
29. Add a text node with the value from the input node
#script that adds a text node with the value from the input node. sn = nuke.selectedNodes() for i in sn: txt = nuke.nodes.Text (font ="/usr/share/fonts/bitstream-vera/Vera.ttf", message = "[file tail [knob input0.file]]", size = "35") txt.knob[transform.box].setValue(0,0,1920,1080) txt.setInput(0,i)