nukescripts.precomp

Package nukescripts :: Module precomp |
[hide private]
[frames] | no frames]

# Source Code for Module nukescripts.precomp

      1  # Copyright (c) 2009 The Foundry Visionmongers Ltd.  All Rights Reserved.
      2  import nuke, os, re, sys, math, time
      3  from nukescripts import execute_panel
      4  from nukescripts import panels
      5
      6 -class PrecompOptionsDialog( panels.PythonPanel ):
      7 -  def __init__( self ):
      8      panels.PythonPanel.__init__( self, "Precomp Nodes", "uk.co.thefoundry.PrecompOptionsDialog" )
      9      self.scriptPath = nuke.File_Knob( "script", "Precomp script path " )
     10      self.renderPath = nuke.File_Knob( "render", "Precomp render path " )
     11      self.channels = nuke.Channel_Knob( "channels", "Channels " )
     12      self.origNodes = nuke.Enumeration_Knob( "orig", "Original nodes ", ["add backdrop", "delete", "no change" ] )
     13      self.addKnob ( self.scriptPath )
     14      self.addKnob ( self.renderPath )
     15      self.addKnob ( self.channels )
     16      self.addKnob ( self.origNodes )
     17
     18      self.channels.setValue('all')
     19
     20      defaultDir = nuke.Root()['name'].value()
     21      if defaultDir and defaultDir != "":
     22        defaultDir = os.path.dirname( defaultDir )
     23        if not defaultDir.endswith("/"):
     24          defaultDir += "/"
     25      else:
     26        defaultDir = ""
     27
     28      basename = findNextName("Precomp")
     29      self.scriptPath.setValue( defaultDir + basename + "_v01.nk" )
     30      self.renderPath.setValue( defaultDir + basename + ".####.exr" )
     31      self.setMinimumSize( 420, 50 )
     32
     33 -class PrecompOptions:
     34 -  def __init__(self):
     35      self.scriptPath = ""
     36      self.renderPath = ""
     37      self.channels = ""
     38      self.addBackdrop = False
     39      self.delete = False
     40
     41 -  def askUserForOptions(self):
     42      p = PrecompOptionsDialog()
     43      result = p.showModalDialog()
     44      if result:
     45        self.scriptPath = p.scriptPath.value()
     46        self.renderPath = p.renderPath.value()
     47        self.channels = p.channels.value()
     48        if p.origNodes.value() == "delete":
     49          self.delete = True
     50        elif p.origNodes.value() == "add backdrop":
     51          self.addBackdrop = True
     52
     53        if nuke.env['ple']:
     54          nukeExt = ".nkple"
     55        else:
     56          nukeExt = ".nk"
     57
     58        (root, ext) = os.path.splitext(self.scriptPath)
     59        if not ext:
     60          self.scriptPath += nukeExt
     61        elif ext == ".nk" and ext != nukeExt:
     62          self.scriptPath = self.scriptPath[0:-3] + nukeExt
     63
     64        (root,ext) = os.path.splitext(self.renderPath)
     65        if not ext:
     66          self.renderPath += ".exr"
     67
     68        if os.path.exists(self.scriptPath):
     69          if not nuke.ask("Overwrite existing " + self.scriptPath + " ?"):
     70            return False
     71        return True
     72      else:
     73        return False
     74
     75 -def precomp_open(precomp):
     76    nuke.Root().setModified( True )
     77    nuke.scriptOpen(precomp["file"].evaluate())
     78
     79 -def precomp_render(precomp):
     80    reading = precomp["reading"].getValue()
     81    precomp["reading"].setValue( False )
     82    try:
     83      finalNode = None
     84      if precomp['useOutput'].value() == True:
     85        finalNode = nuke.toNode( precomp['output'].value() )
     86      else:
     87        if precomp.output() and precomp.output().input(0):
     88          finalNode = precomp.output().input(0)
     89      execute_panel( [ finalNode ] )
     90    except RuntimeError, e:
     91      if e.message[0:9] != "Cancelled":   # TO DO: change this to an exception type
     92        raise
     93      return
     94    precomp["reading"].setValue( True )
     95
     96 -def findNextName(name):
     97    i = 1
     98    while nuke.toNode ( name + str(i) ) != None:
     99      i += 1
    100
    101    return name + str(i)
    102
    103 -def precomp_copyToGroup(precomp):
    104
    105    ## group context is set to precomp, so back up one level.
    106    nuke.endGroup()
    107
    108    g = nuke.nodes.Group()
    109    with precomp:
    110      nuke.selectAll()
    111      nuke.nodeCopy ( '%clipboard%' )
    112
    113    with g:
    114      nuke.nodePaste( '%clipboard%' )
    115
    116    for k in ['label', 'icon', 'indicators', 'tile_color', 'disable']:
    117      v =  precomp[k].getValue()
    118      if v:
    119        g[k].setValue( v )
    120
    121
    122 -def precomp_selected():
    123
    124    nodes = nuke.selectedNodes()
    125    if len(nodes) == 0:
    126      g = nuke.createNode( "Precomp" )
    127      return
    128
    129    options = PrecompOptions()
    130
    131    if not options.askUserForOptions():
    132      return False
    133
    134    sel = nodes[0]
    135
    136    ## select upstream nodes
    137    if len( nodes ) == 1:
    138      upstreamNodes = nuke.dependencies( nodes )
    139      while len ( upstreamNodes ) != 0:
    140        nodes += upstreamNodes
    141        upstreamNodes = nuke.dependencies( upstreamNodes )
    142
    143    left = right = nodes[0].xpos()
    144    top = bottom = nodes[0].ypos()
    145
    146    nodeSize = 100
    147    titleHeight = 50
    148
    149    inputs = []
    150
    151    for n in nodes:
    152      n["selected"].setValue ( True )
    153      if n.xpos() < left:
    154        left = n.xpos()
    155
    156      if n.xpos() > right:
    157        right = n.xpos()
    158
    159      if n.ypos() < top:
    160        top = n.ypos()
    161
    162      if n.ypos() > bottom:
    163        bottom = n.ypos()
    164
    165      for i in range( 0, n.inputs() ):
    166        if not n.input(i):
    167          continue
    168
    169        if not n.input(i) in nodes:
    170          inputs.append( n.input(i) )
    171
    172    ## find all the dependent nodes
    173    inputDeps = []
    174    expressionDeps = []
    175
    176    for n in nodes:
    177      for d in nuke.dependentNodes( nuke.INPUTS, [n]):
    178        if d not in nodes:
    179          if d.Class() != 'Viewer':
    180            inputIndices = [i for i in range(d.inputs()) if d.input(i) == n]
    181            inputDeps.append( (d, inputIndices) )
    182
    183      for d in nuke.dependencies( [n], nuke.EXPRESSIONS ):
    184        if d not in nodes:
    185          expressionDeps.append( d )
    186
    187    if len(inputDeps) > 1:
    188      nuke.message( "You cannot precomp the selected nodes because there are multiple outputs." )
    189      return
    190
    191    addLinkedExpressionNodes = False
    192    if len(expressionDeps) > 0:
    193      addLinkedExpressionNodes = nuke.ask( "Warning: The selected nodes have expressions to nodes outside the precomp. Do you want to copy these nodes to the precomp?" )
    194
    195    ## make group and export
    196    if len( nodes ) == 1 and nodes[0].Class() == "Group":
    197      group = nodes[0]
    198    else:
    199      group = nuke.makeGroup( False )
    200
    201    with group:
    202      outputInputs = []
    203      output = group.output()
    204      for i in range(0, output.inputs()):
    205        outputInputs.append( output.input(i) )
    206
    207      ## insert write node or use existing one
    208      outInp = output.input(0)
    209      if outInp is None or outInp.Class() != "Write":
    210        w = nuke.createNode( "Write", inpanel = False)
    211        w.setInput( 0, None )
    212      else:
    213        w = outInp
    214
    215      for i in range(0, len(outputInputs) ):
    216        w.setInput( i, outputInputs[i] )
    217        output.setInput(i, None )
    218
    219      output.setInput(0, w )
    220
    221      w.knob("file").setValue( options.renderPath )
    222      type = os.path.splitext( options.renderPath)[1][1:].lower()
    223      w.knob("file_type").setValue( type )
    224      w.knob("channels").setValue( options.channels )
    225
    226      for n in nuke.allNodes():
    227        n['selected'].setValue( False )
    228
    229    if addLinkedExpressionNodes:
    230      for n in nuke.allNodes():
    231        n['selected'].setValue( False )
    232
    233      for n in expressionDeps:
    234        n['selected'].setValue( True )
    235
    236      nuke.nodeCopy ( '%clipboard%' )
    237
    238      with group:
    239        nuke.nodePaste( '%clipboard%' )
    240
    241    writeOk = True
    242    with group:
    243      try:
    244        nuke.tcl("export_as_precomp",  options.scriptPath)
    245      except:
    246        nuke.message( "Could not write precomp script, permission denied, please specify a different \'script path\' and try again.")
    247        writeOk = False
    248
    249      for n in nuke.selectedNodes():
    250        n['selected'].setValue( False )
    251
    252    if group != nodes[0]:
    253      group['selected'].setValue( False )
    254      nuke.delete( group )
    255
    256    if not writeOk:
    257      for n in nuke.selectedNodes():
    258        n['selected'].setValue( False )
    259
    260      for n in nodes:
    261        n['selected'].setValue( True )
    262
    263      return
    264
    265    ## reload saved out script
    266    g = nuke.createNode( "Precomp" )
    267    g[ "file" ].setValue( options.scriptPath )
    268
    269    #nuke.tprint( "Selected Node: " + sel.name() )
    270
    271    for d in inputDeps:
    272      node = d[0]
    273      for inp in d[1]:
    274        #nuke.tprint ( "Reconnecting dep " + node.name() + " input " + str(inp) )
    275        node.setInput(inp, g)
    276
    277    ## reconnect inputs, if any
    278    for i in range(0, len(inputs)):
    279      #nuke.tprint ( "Reconnecting input " + inputs[i].name() + " " + str(i) )
    280      g.setInput(i, inputs[i] )
    281
    282    pad = 5
    283
    284    if options.addBackdrop:
    285      b = nuke.createNode( "BackdropNode", inpanel = False )
    286      width = int(math.fabs(right - left)) + (pad * 2) + nodeSize
    287      height = int(math.fabs(bottom - top)) + ( pad * 2 ) + nodeSize + titleHeight
    288      b['label'].setValue( os.path.basename( options.scriptPath ) )
    289      b['note_font_size'].setValue( 18 )
    290      b.setXYpos( left - pad * 2, top - ( pad * 2) - titleHeight )
    291      b.knob( "bdwidth" ).setValue( width  )
    292      b.knob( "bdheight").setValue( height )
    293      b['selected'].setValue(False)
    294      g.setXYpos( b.xpos() + width/2 - nodeSize/2, b.ypos() + height + pad * 2 )
    295    elif options.delete:
    296      for n in nodes:
    297        nuke.delete( n )
    298
    299    if len(inputs) > 0:
    300      nuke.message( "Warning: The precomp script requires inputs and may not render the same independent of its parent script." )
    301
    302    return group
    303