Nuke Python Scripts
Here are a few Python scripts I’ve written for Nuke:
This script creates a new menu item called “Wiggler”, in the Animation sub-menu. Useful for adding wiggles to an animation.
Click on a knob’s animation button and you’ll see a new item called “Wiggler” in the menu.
Choose “Wiggler” and a wiggler node is created, with wiggle controls for the knob who’s button you clicked.
Multiple wigglers for the same knob will add wiggles on top of wiggles. Frequency is in wiggles per second.
To create complex noise, make two Wigglers for the same knob. Set the first Wiggler to a high amplitude and low frequency. Set the second Wiggler to a low amplitude and high frequency. Check out the curve editor as you do this.
If the knob has more than one channel, an “Aberration” slider controls the amount of randomized difference between the channels. For example, if you expand the RGBA axes on a Gain knob and then apply the Wiggler, you can control the chromatic aberration of the flicker.
To wiggle a single channel on a multi-channel knob, delete the expression for the channel(s) you don’t want wiggled.
Existing keyframes and expressions are preserved. If no keyframes exist on the knob, one will be created at the current frame, to preserve the existing value.
To make the “Wiggler” menu item permanent, add this script to your menu.py file or, if you don’t already have a menu.py file, save this script as a new menu.py file in your .nuke folder (usually at C:\Users\[YourUsername]\.nuke).
# Add the Wiggler to the Animation menu def makeWiggler(): curNode = nuke.thisNode() curNodeName = curNode.name() curKnob = nuke.thisKnob() curVal = curKnob.getValue() curType = type(curVal) if curType == float: curVal = [curVal] curAnim = [] for i in range(len(curVal)): curAnim.append(curKnob.animation(i)) if curType == 'float' or 'list': wiggleNodeName = curNodeName+'Wiggler' if not nuke.exists(wiggleNodeName): wiggleNode = nuke.nodes.NoOp (name=wiggleNodeName) else: wiggleNode = nuke.toNode(wiggleNodeName) tabName = curKnob.label() + 'wiggle' tabLabel = curKnob.label() + ' wiggle' global increm increm = '' if wiggleNode.knob(tabName): increm = 2 def checkInc(): global increm if wiggleNode.knob(tabName + str(increm)): increm += 1 checkInc() checkInc() if not wiggleNode.knob('wiggles'): wiggleNode.addKnob(nuke.Tab_Knob('wiggles', 'Wiggles')) wiggleNode.addKnob(nuke.Tab_Knob(tabName + str(increm), tabLabel + ' ' + str(increm), 1)) ampKnob = nuke.Double_Knob(curKnob.name() + 'amplitude' + str(increm), 'Amplitude') ampKnob.setRange(0,10) wiggleNode.addKnob(ampKnob) freqKnob = nuke.Double_Knob(curKnob.name() + 'frequency' + str(increm), 'Frequency (per second)') freqKnob.setRange(0,nuke.Root().knob('fps').value()) wiggleNode.addKnob(freqKnob) seedKnob = nuke.Double_Knob(curKnob.name() + 'seed' + str(increm), 'Random Seed') seedKnob.setRange(0,10) wiggleNode.addKnob(seedKnob) if len(curVal) > 1: aberKnob = nuke.Double_Knob(curKnob.name() + 'aberration' + str(increm), 'Aberration') aberKnob.setRange(0,1) aberKnob.setValue(1) wiggleNode.addKnob(aberKnob) dissableKnob = nuke.Boolean_Knob(curKnob.name() + 'dissable' + str(increm), 'Dissable') wiggleNode.addKnob(dissableKnob) dissableKnob.setFlag(nuke.STARTLINE) for i in range(len(curVal)): if curKnob.hasExpression(i): existing = curKnob.animation(i).expression() else: existing = 'value' if i == 0: curKnob.setExpression('(%(1)s)+noise(parent.%(2)s.%(3)sseed%(4)s,frame*parent.%(2)s.%(3)sfrequency%(4)s/fps)*parent.%(2)s.%(3)samplitude%(4)s*((-1*parent.%(2)s.%(3)sdissable%(4)s)+1)' % \ {'1':existing,'2':wiggleNodeName,'3':curKnob.name(),'4':str(increm)}, i) else: curKnob.setExpression('(%(1)s)+noise((parent.%(2)s.%(3)saberration%(4)s*%(5)s)+parent.%(2)s.%(3)sseed%(4)s,frame*parent.%(2)s.%(3)sfrequency%(4)s/fps)*parent.%(2)s.%(3)samplitude%(4)s*((-1*parent.%(2)s.%(3)sdissable%(4)s)+1)' % \ {'1':existing,'2':wiggleNodeName,'3':curKnob.name(),'4':str(increm),'5':str(i)}, i) if not curAnim[i]: curKnob.setValue(curVal[i], i) nuke.show(wiggleNode) del increm else: nuke.message('Cant wiggle that knob type.') nuke.menu( 'Animation' ).addCommand( 'Wiggler', "makeWiggler()", index=100 ) |
# Add the Wiggler to the Animation menu def makeWiggler(): curNode = nuke.thisNode() curNodeName = curNode.name() curKnob = nuke.thisKnob() curVal = curKnob.getValue() curType = type(curVal) if curType == float: curVal = [curVal] curAnim = [] for i in range(len(curVal)): curAnim.append(curKnob.animation(i)) if curType == 'float' or 'list': wiggleNodeName = curNodeName+'Wiggler' if not nuke.exists(wiggleNodeName): wiggleNode = nuke.nodes.NoOp (name=wiggleNodeName) else: wiggleNode = nuke.toNode(wiggleNodeName) tabName = curKnob.label() + 'wiggle' tabLabel = curKnob.label() + ' wiggle' global increm increm = '' if wiggleNode.knob(tabName): increm = 2 def checkInc(): global increm if wiggleNode.knob(tabName + str(increm)): increm += 1 checkInc() checkInc() if not wiggleNode.knob('wiggles'): wiggleNode.addKnob(nuke.Tab_Knob('wiggles', 'Wiggles')) wiggleNode.addKnob(nuke.Tab_Knob(tabName + str(increm), tabLabel + ' ' + str(increm), 1)) ampKnob = nuke.Double_Knob(curKnob.name() + 'amplitude' + str(increm), 'Amplitude') ampKnob.setRange(0,10) wiggleNode.addKnob(ampKnob) freqKnob = nuke.Double_Knob(curKnob.name() + 'frequency' + str(increm), 'Frequency (per second)') freqKnob.setRange(0,nuke.Root().knob('fps').value()) wiggleNode.addKnob(freqKnob) seedKnob = nuke.Double_Knob(curKnob.name() + 'seed' + str(increm), 'Random Seed') seedKnob.setRange(0,10) wiggleNode.addKnob(seedKnob) if len(curVal) > 1: aberKnob = nuke.Double_Knob(curKnob.name() + 'aberration' + str(increm), 'Aberration') aberKnob.setRange(0,1) aberKnob.setValue(1) wiggleNode.addKnob(aberKnob) dissableKnob = nuke.Boolean_Knob(curKnob.name() + 'dissable' + str(increm), 'Dissable') wiggleNode.addKnob(dissableKnob) dissableKnob.setFlag(nuke.STARTLINE) for i in range(len(curVal)): if curKnob.hasExpression(i): existing = curKnob.animation(i).expression() else: existing = 'value' if i == 0: curKnob.setExpression('(%(1)s)+noise(parent.%(2)s.%(3)sseed%(4)s,frame*parent.%(2)s.%(3)sfrequency%(4)s/fps)*parent.%(2)s.%(3)samplitude%(4)s*((-1*parent.%(2)s.%(3)sdissable%(4)s)+1)' % \ {'1':existing,'2':wiggleNodeName,'3':curKnob.name(),'4':str(increm)}, i) else: curKnob.setExpression('(%(1)s)+noise((parent.%(2)s.%(3)saberration%(4)s*%(5)s)+parent.%(2)s.%(3)sseed%(4)s,frame*parent.%(2)s.%(3)sfrequency%(4)s/fps)*parent.%(2)s.%(3)samplitude%(4)s*((-1*parent.%(2)s.%(3)sdissable%(4)s)+1)' % \ {'1':existing,'2':wiggleNodeName,'3':curKnob.name(),'4':str(increm),'5':str(i)}, i) if not curAnim[i]: curKnob.setValue(curVal[i], i) nuke.show(wiggleNode) del increm else: nuke.message('Cant wiggle that knob type.') nuke.menu( 'Animation' ).addCommand( 'Wiggler', "makeWiggler()", index=100 )
List the file paths for all read nodes that are upstream of the selected nodes. Useful for finding the file dependencies for specific nodes.
#List all file paths for all read nodes, upstream of selected nodes readNodes = [] def checkit(curnode): for nd in curnode.dependencies(): if nd.Class() == 'Read': readNodes.append(nd) checkit(nd) checkit(nuke.selectedNode()) for rn in set(readNodes): print rn.knob('file').getValue() |
#List all file paths for all read nodes, upstream of selected nodes readNodes = [] def checkit(curnode): for nd in curnode.dependencies(): if nd.Class() == 'Read': readNodes.append(nd) checkit(nd) checkit(nuke.selectedNode()) for rn in set(readNodes): print rn.knob('file').getValue()
Converts any Unix path to Windows. Converts any Windows path to Unix.
#Converts Windows Path to Unix #Converts Unix Path to Windows path = nuke.getInput('Paste a path', 'source path') if path: if '/' in path: if not '\\' in path: path = path.replace('/', '\\') else: path = 'Not a valid source path.' else: if '\\' in path: path = path.replace('\\', '/') else: path = 'Not a valid source path.' nuke.message(path) |
#Converts Windows Path to Unix #Converts Unix Path to Windows path = nuke.getInput('Paste a path', 'source path') if path: if '/' in path: if not '\\' in path: path = path.replace('/', '\\') else: path = 'Not a valid source path.' else: if '\\' in path: path = path.replace('\\', '/') else: path = 'Not a valid source path.' nuke.message(path)
This script sets the selected Read nodes “missing frames” knob to “nearest frame”. Handy when you purposefully have missing frames because, for example, the CG element goes off screen.
# Set selected Read nodes to "nearest frame" import nuke for i in nuke.selectedNodes(): if i.Class() == 'Read': i.knob('on_error').setValue(3) |
# Set selected Read nodes to "nearest frame" import nuke for i in nuke.selectedNodes(): if i.Class() == 'Read': i.knob('on_error').setValue(3)
This script automatically changes the frame range of each Read node to match the frame range of it’s image sequence, for all selected Read nodes.
# Automatically set first and last frame for selected Read nodes import glob import os import re import nukescripts errFlg = 0 for node in nuke.selectedNodes(): if node.Class() == 'Read': filePath = nukescripts.replaceHashes(node.knob('file').getValue()).replace('%d', '%01d') padRe = re.compile('%0(\d+)d') padMatch = padRe.search(filePath) padSize = int(padMatch.group(1)) if padMatch: frameList = sorted(glob.iglob(padRe.sub('[0-9]' * padSize, filePath))) if frameList: missing = 0 firstVal = os.path.splitext(frameList[0])[0][-padSize:] lastVal = os.path.splitext(frameList[-1])[0][-padSize:] node.knob('first').setValue(int(firstVal)) node.knob('last').setValue(int(lastVal)) node.knob('origfirst').setValue(int(firstVal)) node.knob('origlast').setValue(int(lastVal)) else: missing = 1 errFlg = 1 if missing: print 'Image sequence could not be found: ' + filePath if not errFlg: print 'All selected Read nodes were reset.' |
# Automatically set first and last frame for selected Read nodes import glob import os import re import nukescripts errFlg = 0 for node in nuke.selectedNodes(): if node.Class() == 'Read': filePath = nukescripts.replaceHashes(node.knob('file').getValue()).replace('%d', '%01d') padRe = re.compile('%0(\d+)d') padMatch = padRe.search(filePath) padSize = int(padMatch.group(1)) if padMatch: frameList = sorted(glob.iglob(padRe.sub('[0-9]' * padSize, filePath))) if frameList: missing = 0 firstVal = os.path.splitext(frameList[0])[0][-padSize:] lastVal = os.path.splitext(frameList[-1])[0][-padSize:] node.knob('first').setValue(int(firstVal)) node.knob('last').setValue(int(lastVal)) node.knob('origfirst').setValue(int(firstVal)) node.knob('origlast').setValue(int(lastVal)) else: missing = 1 errFlg = 1 if missing: print 'Image sequence could not be found: ' + filePath if not errFlg: print 'All selected Read nodes were reset.'
This script changes the frame range for the selected Read nodes to explicit values. First change the numbers in the script after “firstVal” and “lastVal”, to your new starting and ending frame values. Then select the Read nodes and run the script.
# Set first and last frame for selected Read nodes firstVal = 1001 lastVal = 1100 import nuke for i in nuke.selectedNodes(): if i.Class() == 'Read': i.knob('first').setValue(firstVal) i.knob('last').setValue(lastVal) i.knob('origfirst').setValue(firstVal) i.knob('origlast').setValue(lastVal) |
# Set first and last frame for selected Read nodes firstVal = 1001 lastVal = 1100 import nuke for i in nuke.selectedNodes(): if i.Class() == 'Read': i.knob('first').setValue(firstVal) i.knob('last').setValue(lastVal) i.knob('origfirst').setValue(firstVal) i.knob('origlast').setValue(lastVal)