Nuke: Coding, Tips & Tricks

I have been compiling this document for the past several years of various Python & TCL expressions that I’ve made use of during my many years doing the job I am doing – being a digital compositor and compositing supervisor. I’ve found coding to be a skill that is not as important relative to having a good eye for what is right and beautiful – but damn functional never the less. Fact that I’ve been crunched through the gruelsome eastern-european educational system that throws natural sciences on future artists as much as on any average electrical engineer has always made me a little bit more open to the world of coding, and sooner than later I realized that getting to results faster than average does and always will rely on a capability to write or use that little script that does just what you need. This document, which will grow exponentially with my mileage in VFX is an attempt to have a reliable resource of stuff that we as artists actually need. Well, at least on an occasion, if not most of the time. There might be some typos here and there, there is a good amount of borrowed code from people that have written better code for some things I needed than I had – it’s a jungle below this introduction. But – hopefully – a very useful and easy to navigate jungle. Enjoy and send your feedback if you feel there is some more stuff that I need to add, or are in need of some advice on any of the current scripts. Thanks for reading, and I hope you find this stuff useful.

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]
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

  1. Check write nodes and create folders if none found
  2. Attach a Furnace DeNoise node to selected nodes
  3. Attach a Write node to selected Read nodes, and embed with expression on disable
  4. Change single or muliple parameters in all selected nodes
  5. Create Write nodes connected with all selected read nodes with correct file name
  6. Disable “Postage Stamps” on all nodes
  7. Unhide all nodes’ inputs (if received a script that hides them)
  8. Change the first frame to a certain number – enhanced version should change the last frame to that number + frame range
  9. Print a selected nodes’ methods
  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
  11. Remove all animation from selected nodes
  12. Add keyframes – Animate mix parameter
  13. Halve the color value of all constant nodes in a script
  14. Find all the transform Nodes in the script and if their input is a crop set scale value X2
  15. Set all gain values of CC nodes X2
  16. Change font size of all Write nodes in the script
  17. Create 20 constants with incrementing color values
  18. Set up write nodes for specific frame range
  19. Use Python to define your own hotkeys
  20. Select all class nodes (class Read in this case)
  21. Write DPX, create Slate and WriteJPG generator – written for Method Pipeline
  22. Camera with aim TCL
  23. Add a text node with the value from the input node
  24. Multiple Read/Write nodes tweaker
  25. Import mocha track and create a normal and reversed corner pin out of it
  26. Control / smooth curves with a multiplier
  27. Open all property bins of all selected nodes
  28. Write all the read nodes file name in the script editor
  29. 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)