Nuke Basic

Nuke Intro

  • tracker node:
    • 2D for translation, rotation, scale
    • require: 1 point for translation, 2 points for rotation and scale
  • planar tracker node:
    • 2.5D for translation, rotation, scale, skew, perspective
    • require: a planar surface
  • camera tracker node:
    • 3D for volumes, spaces, planes, perspective in 3D
    • require: focal length, film back size, camera parallax movement, lens distortion removal
  1. prepare footage
    • grain/noise removal (denoise node)
    • stabilize
  2. choose clear reference frame
  3. get BBox coverage
  4. frameHold portions and paint node groups
  5. shape parts to relieve
  6. matchmove and add grain
  • solve camera in nuke or other app
  • 3D camera in nuke
  • Geometry
  • Clean Patch
  • Frozen Camera

Nuke Python Script Intro

Where to enter the script

  • x key: (File > Script Command - TCL/Python)
  • script editor: (Right click the panel bar > split vertical and choose “Script Editor” in drop down)
    • Error console

Where to view command history (like Maya “Echo all Command”)

  • Edit menu > Preference : Script Editor tab : Echo python command

Where to know the node or knob attribute name

  • right click on the attribute and click “Add Expression”, then all the internal name releaved.

How to know the node Class and Function

  • use this code
     your_node.Class() # to get Class name
    help(your_node.Class()) # to get class definition

Ultimate solution: Python Panels

  • a script based python-based floating tab panel that integrated with Nuke, not a dialog
  • common python syntax
    i=0;
    list=[]
    list.append(item)
    print list
    total = len(list) - 1
    print total
    for i in range(0, total):
        print list[i]

ref: http://www.youtube.com/watch?v=JoniI7oRDaU

for the code not working, just clean the space and use tab to re-create the tabs

Tips: if you want to know the knob name, then hold Alt+drag it to script editor to show its reference name.

  • get and feedback to user
    nuke.message("hello") # alert box
    nuke.tprint("it prints in terminal where nuke launched from")
  • get selected node
    nuke.selectedNodes() # return a list
    nuke.toNode('NodeName') # get node with the name
  • get format and node format
    node = nuke.selectedNode()
    nodeFormat = node.format()
    print nodeFormat.name()
     
    #or width
    nuke.selectedNode().width()
    nuke.selectedNode().height()
     
    # or all formats
    scriptFormats = nuke.formats()
    for f in scriptFormats:
      print f.name()
      print f.width()
      print f.height()
      print f.pixelAspect()
      print 10*'-'
  • get select node knobs (cool way for GUI programming of existing node)
    znodes=nuke.selectedNodes()
    znode=znodes[0]
    for i in range(znode.getNumKnobs()):
      print '"%d %s' % (i, znode.knob (i).name())
     
    # method 2
    help(nuke.selectedNode()) #list all node method
     
    nuke.selectedNode().Class() #print node class name
     
    # qwidget under mouse
    from PySide import QtGui,QtCore
    def getPos():
        pos = QtGui.QCursor.pos()
        widget = QtGui.qApp.widgetAt(pos)
        print(widget)
    QtCore.QTimer.singleShot(5000, getPos) # print widget under mouse after 5s 
     
    # all panel
    nuke.Double_knob
    parent_window = QtGui.QApplication.activeWindow()
    all_stacked_widgets = parent_window.findChildren(QtGui.QStackedWidget)
  • get active knob from right click on knob
    knobMenu = nuke.menu("Animation")
    knobMenu.addCommand('Set Key knob', "nuke.message(\"Knob Name: nuke.toNode('%s').knob('%s'); %s\" % (nuke.thisNode().name(), nuke.thisKnob().name(), nuke.thisKnob().value()) );keyNode=nuke.thisNode().name()+'.'+nuke.thisKnob().name();" )
     
    topMenu = nuke.menu('Nuke')
    topMenu.addCommand('Test/Key knob', "nuke.message('keying now: %s' % keyNode);keyCurrent(keyNode)", 'Ctrl+F11' )
     
    def keyCurrent(node_attr):
        # node_attr format: node.attr
        info = node_attr.split('.')
        keyNodeAttr = nuke.toNode(info[0]).knob(info[1])
        cur_frame = nuke.frame()
        cur_value = keyNodeAttr.value()
        if not keyNodeAttr.isAnimated():
            keyNodeAttr.setAnimated()
        keyNodeAttr.setValueAt(cur_value, cur_frame)


  • get or set gui value
    print znode.knob ('knobName').value()
    print znode['knobName'].value()
    znode.knob (i).setValue('theValue')
  • write to file and read from file
    import sys
    import os
    filename='/home/user/TrackConvertSrc.txt'
    fp = open(filename, 'w')
        fp.write( "0 0 0\n" )
        trackSrcX = znode.knob('to1').animation(0)
        for key in trackSrcX.keys():
            if flipY:
                fp.write( "%g %g %g\n" % (key.x, key.y, imageHeight - trackSrcY.evaluate( key.x )) )
            else:
                fp.write( "%g %g %g\n" % (key.x, key.y, trackSrcY.evaluate( key.x )) )
        fp.close()
     
    # read
    trackDst = znode.knob('to1')
    trackDst.clearAnimated(0)
    trackDst.clearAnimated(1)
    trackDst.setAnimated(0)
    trackDst.setAnimated(1)   
     
    trackX = trackDst.animation(0)
    trackY = trackDst.animation(1)
    fp = open(filename, 'r')
        for line in fp:
            if line.startswith('0'):
                continue
            try:
                tt, xx, yy = [float(x) for x in line.split()]
            except:
                continue
            trackX.setKey( tt, xx )
            if flipY:
                trackY.setKey( tt, imageHeight - yy )
            else:
                trackY.setKey( tt, yy )
        fp.close()
  • deal with 2D position animation curve (pre-baked curve required)
    # like znode as cornerPin node
    # read curve
    trackSrc = znode.knob('to1')
    if not trackSrc.isAnimated(0) or not trackSrc.isAnimated(1) :
        nuke.message('source track no keyframes!')
    trackSrcX = trackSrc.animation(0)
    trackSrcY = trackSrc.animation(1)    
     
    # write curve
  • node creation related
    # method 1
    newNode=nuke.nodes.CornerPin2D(name='newCorner')
    # method 2
    nuke.createNode('CornerPin2D')
     
    # get selected node type or class name
    znodes=nuke.selectedNodes()
    znode=znodes[0]
    znode.Class() # Tracker3
  • node attributes related
    imageHeight = znode.height()
    imageWidth = znode.width()
    format = znode.format().name()
    pixelAspect = znode.format().pixelAspect()
     
    # create attribute experssion
    # example: tracker to crop mode
    fullTracker="Track1" # tracker node
    crop="Crop1" #crop node
     
    newName=fullTracker+"_crop_mode"
    newNode=nuke.nodes.Tracker3(name=newName)
     
    newNode['enable1'].setValue(True)
    newNode['enable2'].setValue(True)
    newNode['enable3'].setValue(True)
    newNode['enable4'].setValue(True)
    newNode['track1'].setExpression(fullTracker+'.track1-'+crop+'.box.x', 0)
    newNode['track1'].setExpression(fullTracker+'.track1-'+crop+'.box.y', 1)
    newNode['track2'].setExpression(fullTracker+'.track2-'+crop+'.box.x', 0)
    newNode['track2'].setExpression(fullTracker+'.track2-'+crop+'.box.y', 1)
    newNode['track3'].setExpression(fullTracker+'.track3-'+crop+'.box.x', 0)
    newNode['track3'].setExpression(fullTracker+'.track3-'+crop+'.box.y', 1)
    newNode['track4'].setExpression(fullTracker+'.track4-'+crop+'.box.x', 0)
    newNode['track4'].setExpression(fullTracker+'.track4-'+crop+'.box.y', 1)
  • animation related
    curNodeAttr = nuke.toNode("Grade1")['blackpoint']
    animCuves = curNodeAttr.animations()
    curAnimCurve = curNodeAttr.animation(0)
    # get value at frame
    valueAtFrame1 = curAnimCurve.evaluate(2) # value at frame 2
    # set value at frame
    curAnimCurve.setKey(10, 0.5) # set to 0.5 at frame 10
    # check value expression
    curNodeAttr.hasExpression() # 1,0
    curAnimCurve.noExpression() # 1,0
    # set expression connection
    animCurveB.setExpression('blackpoint')
    # get expression
    exp = curAnimCurve.expression()
    # get key
    keys = curAnimCurve.keys()
    curAnimCurve.addKey(keys)
    curAnimCurve.clear()
    # same as
    nuke.animation("Grade1.blackpoint", "clear")
  • plugins
    nuke.pluginPath() # list all plugin path, use first found in path
    nuke.pluginAddPath(os.path.abspath("/my/path")) # all os compatile

Nuke menu customization

  1. put menu.py in “.nuke” folder (which is under home directory)
  2. sample code of import a py file GUI tool in menu.py file
    import sys; sys.path.insert(0, "/myPythonScriptDir/dev/")
    import MyPythonFileNameOnly as myFirstGUI
    m = menubar.addMenu("MyCustMenu")
    m.addCommand("Menu Item Name", "myFirstGUI.go()")
    • menu.py can also be used to change/add menu shortcut (read reference)
    • menu.py can also be used to set node default setting (read reference)
  3. sample code of Python GUI tool for menu integration
    import nuke
    import nukescripts
     
    class myFirstGUI(nukescripts.panels.PythonPanel):
     
        def __init__(self, name):
            nukescripts.panels.PythonPanel.__init__(self, name)
            # gui preset
            newline = nuke.Text_Knob("")
            newline.setVisible(False)
     
            self.btn01=nuke.PyScript_Knob("firstBtn", "My button")
            self.addKnob(self.btn01)
     
            self.addKnob(newline)
            self.addKnob(nuke.PyScript_Knob("AskBtn", "Hello Button", "nuke.message(\"Done by Me v1.0! \\n\\nv1.0 usage:\\n- Do it.\")"))
        def knobChanged( self, knob ):
            if knob == self.btn01:
                print("cool")
     
    def go():
        mypanel = myFirstGUI("GUI cool v1.0")
        mypanel.show()
  • common GUI element
    p = nuke.Panel('My Pop-up Panel')
    p.addButton("cool")
    result = p.show()
  • built-in GUI panel_test
    from nukescripts import panel_test
    panel_test.panel_example()
  • tab window template
    class modelPanel(nukescripts.panels.PythonPanel):
     
        def __init__(self, name):
            nukescripts.panels.PythonPanel.__init__(self, name)
    #=================
            self.addKnob(nuke.Text_Knob("Group1"))
     
            newline = nuke.Text_Knob("")
            newline.setVisible(False)
     
            self.addKnob(nuke.PyScript_Knob("btn01", "Label01", "nuke.message(\"hey\")" ))
            self.addKnob(newline)
            self.addKnob(nuke.PyScript_Knob("btn02", "Label02", "nuke.message(\"hey\")" ))
            self.addKnob(newline)
     
    #=================
            self.addKnob(nuke.Text_Knob("Group2"))
            self.addKnob(nuke.PyScript_Knob("btn03", "Label03", "nuke.message(\"hey\")" ))
            self.addKnob(newline)
    #=================
            self.addKnob(nuke.Text_Knob("Group3"))
            self.addKnob(nuke.PyScript_Knob("btn04", "Label04", "nuke.message(\"hey\")" ))
            self.addKnob(newline)
     
     
    #========== show up script run seperately
    panel = None
    global panel
    if not panel:
        panel = modelPanel("Model Select Panel")
    panel.show()
  • GUI knob button with multi-line pop-up message
    self.addKnob(nuke.PyScript_Knob("HelpBtn", "Tip*Trick", "nuke.message(\"\\n1.ask me line 1.\\n2.check my update line2.\")"))

Example of procedure GUI design

  • tracker in full resolution image into crop reformat image
    #============================
    # Full-Res Tracker to Crop Tracker by yx v1.0
    # Input : name of tracker and crop node
    # Output: new tracker node with linked corresponding tracker data in crop mode
    # 2014.02.15
    #============================
    import nuke
     
    def go():
      p = nuke.Panel('Full-Res Tracker to Crop Tracker by yx v1.0')
      p.addSingleLineInput('Full-Res Tracker Node', '')
      p.addSingleLineInput('Crop Node', '')
      p.addButton('Cancel')
      p.addButton('Generate Crop Tracker Node')
     
      ret = p.show()
     
      fullTracker = p.value('Full-Res Tracker Node')
      crop = p.value('Crop Node')
     
      if ret:
          print fullTracker, crop
          newName=fullTracker+"_crop_mode"
          newNode=nuke.nodes.Tracker3(name=newName)
          print newName+" created"
          newNode['enable1'].setValue(True)
          newNode['enable2'].setValue(True)
          newNode['enable3'].setValue(True)
          newNode['enable4'].setValue(True)
          newNode['track1'].setExpression(fullTracker+'.track1-'+crop+'.box.x', 0)
          newNode['track1'].setExpression(fullTracker+'.track1-'+crop+'.box.y', 1)
          newNode['track2'].setExpression(fullTracker+'.track2-'+crop+'.box.x', 0)
          newNode['track2'].setExpression(fullTracker+'.track2-'+crop+'.box.y', 1)
          newNode['track3'].setExpression(fullTracker+'.track3-'+crop+'.box.x', 0)
          newNode['track3'].setExpression(fullTracker+'.track3-'+crop+'.box.y', 1)
          newNode['track4'].setExpression(fullTracker+'.track4-'+crop+'.box.x', 0)
          newNode['track4'].setExpression(fullTracker+'.track4-'+crop+'.box.y', 1)    

Example of Class like GUI design

  • copy BoundBox to CornerPin setting
    #============================
    # BBox_Pin v1.0
    # pin placed directly to bbox of the node
    #
    #============================
    p1x=0
    p1y=0
    p2x=0
    p2y=0
    p3x=0
    p3y=0
    p4x=0
    p4y=0
     
     
    class bboxPinPanel(nukescripts.panels.PythonPanel):
     
        def __init__(self, name):
            nukescripts.panels.PythonPanel.__init__(self, name)
            # gui preset
            newline = nuke.Text_Knob("")
            newline.setVisible(False)
            self.btn01=nuke.PyScript_Knob("CopyBtn", "Copy BBox")
            #btn01.setCommand("copyBBox()") 
            self.addKnob(self.btn01)
            self.btn02=nuke.PyScript_Knob("PasteBtn", "Paste to Pin")
            self.addKnob(self.btn02)
     
        def knobChanged( self, knob ):
            if knob == self.btn01:
                global p1x
                global p1y
                global p2x
                global p2y
                global p3x
                global p3y
                global p4x
                global p4y
                znode = nuke.selectedNode()
                print znode.name()
                bh=znode.bbox().h();
                bw=znode.bbox().w();
                bx=znode.bbox().x();
                by=znode.bbox().y();
                p1x=bx
                p1y=by
                p2x=bx+bw
                p2y=by
                p3x=bx+bw
                p3y=by+bh
                p4x=bx
                p4y=by+bh
                print [p1x,p1y,p2x,p2y,p3x,p3y,p4x,p4y,]
            if knob == self.btn02:
                global p1x
                global p1y
                global p2x
                global p2y
                global p3x
                global p3y
                global p4x
                global p4y
                znode = nuke.selectedNode()
                znode["to1"].setValue(p1x,0)
                znode["to1"].setValue(p1y,1)
                znode["to2"].setValue(p2x,0)
                znode["to2"].setValue(p2y,1)
                znode["to3"].setValue(p3x,0)
                znode["to3"].setValue(p3y,1)
                znode["to4"].setValue(p4x,0)
                znode["to4"].setValue(p4y,1)
                znode["from1"].setValue(p1x,0)
                znode["from1"].setValue(p1y,1)
                znode["from2"].setValue(p2x,0)
                znode["from2"].setValue(p2y,1)
                znode["from3"].setValue(p3x,0)
                znode["from3"].setValue(p3y,1)
                znode["from4"].setValue(p4x,0)
                znode["from4"].setValue(p4y,1)
     
    bppanel = bboxPinPanel("BBox to PinPoint by Ying v1.0")
    bppanel.show()
  • roto shape point access and control script
    node = nuke.selectedNode()
    knob = node['curves']
    for element in knob.rootLayer:
        for index, point in enumerate(element):
            print index, point.center.getPosition(nuke.frame()) 
            # setPosition(CVec2(cpx, cpy))