# Copyright (c) 2009 The Foundry Visionmongers Ltd. All Rights Reserved.
import nuke_internal as nuke
import os, re, sys, math, time
from nukescripts import execute_panel
from nukescripts import panels
[docs]class PrecompOptionsDialog( panels.PythonPanel ):
def __init__( self ):
panels.PythonPanel.__init__( self, "Precomp Nodes", "uk.co.thefoundry.PrecompOptionsDialog" )
self.scriptPath = nuke.File_Knob( "script", "Precomp script path " )
self.renderPath = nuke.File_Knob( "render", "Precomp render path " )
self.channels = nuke.Channel_Knob( "channels", "Channels " )
self.origNodes = nuke.Enumeration_Knob( "orig", "Original nodes ", ["add backdrop", "delete", "no change" ] )
self.addKnob ( self.scriptPath )
self.addKnob ( self.renderPath )
self.addKnob ( self.channels )
self.addKnob ( self.origNodes )
self.channels.setValue('all')
defaultDir = nuke.Root()['name'].value()
if defaultDir and defaultDir != "":
defaultDir = os.path.dirname( defaultDir )
if not defaultDir.endswith("/"):
defaultDir += "/"
else:
defaultDir = ""
basename = findNextName("Precomp")
self.scriptPath.setValue( defaultDir + basename + "_v01.nk" )
self.renderPath.setValue( defaultDir + basename + ".####.exr" )
self.setMinimumSize( 420, 50 )
[docs]class PrecompOptions:
def __init__(self):
self.scriptPath = ""
self.renderPath = ""
self.channels = ""
self.addBackdrop = False
self.delete = False
def askUserForOptions(self):
p = PrecompOptionsDialog()
result = p.showModalDialog()
if result:
self.scriptPath = p.scriptPath.value()
self.renderPath = p.renderPath.value()
self.channels = p.channels.value()
if p.origNodes.value() == "delete":
self.delete = True
elif p.origNodes.value() == "add backdrop":
self.addBackdrop = True
if nuke.env['nc']:
nukeExt = ".nknc"
if nuke.env['indie']:
nukeExt = ".nkind"
else:
nukeExt = ".nk"
(root, ext) = os.path.splitext(self.scriptPath)
if not ext:
self.scriptPath += nukeExt
elif ext == ".nk" and ext != nukeExt:
self.scriptPath = self.scriptPath[0:-3] + nukeExt
(root,ext) = os.path.splitext(self.renderPath)
if not ext:
self.renderPath += ".exr"
if os.path.exists(self.scriptPath):
if not nuke.ask("Overwrite existing " + self.scriptPath + " ?"):
return False
return True
else:
return False
[docs]def precomp_open(precomp):
precomp.executePythonCallback(nuke.PRECOMP_CALLBACK_OPENED)
nuke.Root().setModified( True )
nuke.scriptOpen(precomp["file"].evaluate())
[docs]def precomp_render(precomp):
reading = precomp["reading"].getValue()
precomp["reading"].setValue( False )
try:
finalNode = None
if precomp['useOutput'].value() == True:
finalNode = nuke.toNode( precomp['output'].value() )
else:
if precomp.output() and precomp.output().input(0):
finalNode = precomp.output().input(0)
execute_panel( [ finalNode ] )
except RuntimeError as e:
if e.message[0:9] != "Cancelled": # TO DO: change this to an exception type
raise
return
precomp["reading"].setValue( True )
[docs]def findNextName(name):
i = 1
while nuke.toNode ( name + str(i) ) != None:
i += 1
return name + str(i)
[docs]def precomp_copyToGroup(precomp):
## group context is set to precomp, so back up one level.
nuke.endGroup()
g = nuke.nodes.Group()
with precomp:
nuke.selectAll()
nuke.nodeCopy ( '%clipboard%' )
with g:
nuke.nodePaste( '%clipboard%' )
for k in ['label', 'icon', 'indicators', 'tile_color', 'disable']:
v = precomp[k].value()
if v:
g[k].setValue( v )
for k in precomp.allKnobs():
if isinstance( k, nuke.Link_Knob ):
lnk = nuke.Link_Knob( k.name() )
lnk.setLink( k.getLink() )
g.addKnob( lnk )
[docs]def precomp_selected():
nodes = nuke.selectedNodes()
if len(nodes) == 0:
g = nuke.createNode( "Precomp" )
return
options = PrecompOptions()
if not options.askUserForOptions():
return False
sel = nodes[0]
## select upstream nodes
if len( nodes ) == 1:
upstreamNodes = nuke.dependencies( nodes )
while len ( upstreamNodes ) != 0:
nodes += upstreamNodes
upstreamNodes = nuke.dependencies( upstreamNodes )
left = right = nodes[0].xpos()
top = bottom = nodes[0].ypos()
nodeSize = 100
titleHeight = 50
inputs = []
for n in nodes:
n["selected"].setValue ( True )
if n.xpos() < left:
left = n.xpos()
if n.xpos() > right:
right = n.xpos()
if n.ypos() < top:
top = n.ypos()
if n.ypos() > bottom:
bottom = n.ypos()
for i in range( 0, n.inputs() ):
if not n.input(i):
continue
if not n.input(i) in nodes:
inputs.append( n.input(i) )
## find all the dependent nodes
inputDeps = []
expressionDeps = []
for n in nodes:
for d in nuke.dependentNodes( nuke.INPUTS, [n]):
if d not in nodes:
if d.Class() != 'Viewer':
inputIndices = [i for i in range(d.inputs()) if d.input(i) == n]
inputDeps.append( (d, inputIndices) )
for d in nuke.dependencies( [n], nuke.EXPRESSIONS ):
if d not in nodes:
expressionDeps.append( d )
if len(inputDeps) > 1:
nuke.message( "You cannot precomp the selected nodes because there are multiple outputs." )
return
addLinkedExpressionNodes = False
if len(expressionDeps) > 0:
addLinkedExpressionNodes = nuke.ask( "Warning: The selected nodes have expressions to nodes outside the precomp. Do you want to copy these nodes to the precomp?" )
## make group and export
if len( nodes ) == 1 and nodes[0].Class() == "Group":
group = nodes[0]
else:
group = nuke.makeGroup( False )
with group:
outputInputs = []
output = group.output()
for i in range(0, output.inputs()):
outputInputs.append( output.input(i) )
## insert write node or use existing one
outInp = output.input(0)
if outInp is None or outInp.Class() != "Write":
w = nuke.createNode( "Write", inpanel = False)
w.setInput( 0, None )
else:
w = outInp
for i in range(0, len(outputInputs) ):
w.setInput( i, outputInputs[i] )
output.setInput(i, None )
output.setInput(0, w )
w.knob("file").setValue( options.renderPath )
type = os.path.splitext( options.renderPath)[1][1:].lower()
w.knob("file_type").setValue( type )
w.knob("channels").setValue( options.channels )
for n in nuke.allNodes():
n['selected'].setValue( False )
if addLinkedExpressionNodes:
for n in nuke.allNodes():
n['selected'].setValue( False )
for n in expressionDeps:
n['selected'].setValue( True )
nuke.nodeCopy ( '%clipboard%' )
with group:
nuke.nodePaste( '%clipboard%' )
writeOk = True
with group:
try:
nuke.tcl("export_as_precomp", options.scriptPath)
except:
nuke.message( "Could not write precomp script, permission denied, please specify a different \'script path\' and try again.")
writeOk = False
for n in nuke.selectedNodes():
n['selected'].setValue( False )
if group != nodes[0]:
group['selected'].setValue( False )
nuke.delete( group )
if not writeOk:
for n in nuke.selectedNodes():
n['selected'].setValue( False )
for n in nodes:
n['selected'].setValue( True )
return
## reload saved out script
g = nuke.createNode( "Precomp" )
g[ "file" ].setValue( options.scriptPath )
#nuke.tprint( "Selected Node: " + sel.name() )
for d in inputDeps:
node = d[0]
for inp in d[1]:
#nuke.tprint ( "Reconnecting dep " + node.name() + " input " + str(inp) )
node.setInput(inp, g)
## reconnect inputs, if any
for i in range(0, len(inputs)):
#nuke.tprint ( "Reconnecting input " + inputs[i].name() + " " + str(i) )
g.setInput(i, inputs[i] )
pad = 5
if options.addBackdrop:
b = nuke.createNode( "BackdropNode", inpanel = False )
width = int(math.fabs(right - left)) + (pad * 2) + nodeSize
height = int(math.fabs(bottom - top)) + ( pad * 2 ) + nodeSize + titleHeight
b['label'].setValue( os.path.basename( options.scriptPath ) )
b['note_font_size'].setValue( 18 )
b.setXYpos( left - pad * 2, top - ( pad * 2) - titleHeight )
b.knob( "bdwidth" ).setValue( width )
b.knob( "bdheight").setValue( height )
b.knob( "z_order" ).setValue( 0 )
b['selected'].setValue(False)
g.setXYpos( b.xpos() + width/2 - nodeSize/2, b.ypos() + height + pad * 2 )
elif options.delete:
for n in nodes:
nuke.delete( n )
if len(inputs) > 0:
nuke.message( "Warning: The precomp script requires inputs and may not render the same independent of its parent script." )
return group